feat: a link into a tab — app.pedshub.com/#resources opens My Resources
The app has no pages and is not getting any. Another site can still send someone straight to a tab by naming it in the hash: it is read once at load, stored where sign-in looks for the last tab, and dropped from the URL, so it survives the round trip through the SSO and does not stick to the address bar. #resources, #deck and #decks all mean My Resources. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dv6sqaY6Vq3ChZHMem3cnU
This commit is contained in:
parent
733510d85d
commit
e306c3ce28
2 changed files with 59 additions and 0 deletions
|
|
@ -2,6 +2,24 @@
|
|||
// APP.JS — Core utilities, tabs, model selector, helpers
|
||||
// ============================================================
|
||||
|
||||
// ── A link into a tab, without routes ───────────────────────
|
||||
// The app has no pages: every tab lives at "/". Another site can still send
|
||||
// someone straight to one — app.pedshub.com/#resources opens My Resources —
|
||||
// by naming the tab in the hash. It is read once, stored where sign-in looks
|
||||
// for the last tab, and removed from the URL, so it survives the trip to the
|
||||
// SSO and back and does not stick to the address bar afterwards.
|
||||
var TAB_ALIASES = { resources: 'myresources', deck: 'myresources', decks: 'myresources', assistant: 'assistant' };
|
||||
function openTabFromHash(win, doc, storage) {
|
||||
var wanted = String((win.location && win.location.hash) || '').replace(/^#/, '').toLowerCase();
|
||||
wanted = TAB_ALIASES[wanted] || wanted;
|
||||
if (!wanted) return null;
|
||||
if (wanted !== 'assistant' && !doc.querySelector('.tab-btn[data-tab="' + wanted + '"]')) return null;
|
||||
try { storage.setItem('ped_last_tab', wanted); } catch (e) {}
|
||||
try { win.history.replaceState(null, '', win.location.pathname + win.location.search); } catch (e) {}
|
||||
return wanted;
|
||||
}
|
||||
try { openTabFromHash(window, document, window.localStorage); } catch (e) {}
|
||||
|
||||
// ── Client-side error logging ──────────────────────────────
|
||||
function sendError(data) {
|
||||
try {
|
||||
|
|
|
|||
41
test/open-tab-from-hash.test.js
Normal file
41
test/open-tab-from-hash.test.js
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
// app.pedshub.com/#resources opens My Resources. The app has no routes and is
|
||||
// not getting any; a hash is read once, stored as the last tab, and dropped.
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const src = fs.readFileSync(path.join(__dirname, '..', 'public/js/app.js'), 'utf8');
|
||||
const start = src.indexOf('var TAB_ALIASES');
|
||||
const end = src.indexOf('try { openTabFromHash(window');
|
||||
const openTabFromHash = new Function(src.slice(start, end) + '; return openTabFromHash;')();
|
||||
|
||||
function world(hash, tabs) {
|
||||
const stored = {}; const replaced = [];
|
||||
return {
|
||||
win: { location: { hash, pathname: '/', search: '?sso=ok' }, history: { replaceState: (_s, _t, url) => replaced.push(url) } },
|
||||
doc: { querySelector: sel => tabs.some(t => sel === '.tab-btn[data-tab="' + t + '"]') ? {} : null },
|
||||
storage: { setItem: (k, v) => { stored[k] = v; } }, stored, replaced
|
||||
};
|
||||
}
|
||||
|
||||
test('#resources stores My Resources as the tab to open and clears the hash', () => {
|
||||
const w = world('#resources', ['encounter', 'myresources']);
|
||||
assert.equal(openTabFromHash(w.win, w.doc, w.storage), 'myresources');
|
||||
assert.equal(w.stored.ped_last_tab, 'myresources');
|
||||
assert.deepEqual(w.replaced, ['/?sso=ok'], 'the query survives, the hash goes');
|
||||
});
|
||||
|
||||
test('a tab that does not exist, or no hash at all, changes nothing', () => {
|
||||
for (const hash of ['#nonsense', '', '#']) {
|
||||
const w = world(hash, ['encounter']);
|
||||
assert.equal(openTabFromHash(w.win, w.doc, w.storage), null);
|
||||
assert.deepEqual(w.stored, {}); assert.deepEqual(w.replaced, []);
|
||||
}
|
||||
});
|
||||
|
||||
test('the hash is honoured by the same code that restores the last tab after sign-in', () => {
|
||||
const auth = fs.readFileSync(path.join(__dirname, '..', 'public/js/auth.js'), 'utf8');
|
||||
assert.match(auth, /localStorage\.getItem\('ped_last_tab'\)/);
|
||||
assert.match(src, /try \{ openTabFromHash\(window, document, window\.localStorage\); \} catch/);
|
||||
});
|
||||
Loading…
Reference in a new issue