tosijs-ui 1.6.9 → 1.6.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/doc-browser.js +72 -0
- package/dist/doc-system/routing.d.ts +8 -0
- package/dist/doc-system/routing.js +13 -0
- package/dist/doc-system/site/generate-site.js +7 -2
- package/dist/iife.js +53 -50
- package/dist/iife.js.map +11 -11
- package/dist/live-example/code-transform.d.ts +42 -8
- package/dist/live-example/code-transform.js +0 -0
- package/dist/live-example/component.d.ts +30 -1
- package/dist/live-example/component.js +190 -9
- package/dist/live-example/insert-examples.js +6 -1
- package/dist/live-example/save-to-source.js +11 -2
- package/dist/live-example/styles.d.ts +17 -0
- package/dist/live-example/styles.js +18 -0
- package/dist/live-example/types.d.ts +2 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/llms.txt +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -67,7 +67,7 @@ npm add tosijs-ui
|
|
|
67
67
|
Then import the component `elementCreator` and create elements. A `tosijs`
|
|
68
68
|
`elementCreator` is syntax sugar around `document.createElement()`.
|
|
69
69
|
|
|
70
|
-
```
|
|
70
|
+
```typescript
|
|
71
71
|
import { tosiTable } from 'tosijs-ui'
|
|
72
72
|
|
|
73
73
|
document.body.append(tosiTable())
|
package/dist/doc-browser.js
CHANGED
|
@@ -274,6 +274,40 @@ export function createDocBrowser(options) {
|
|
|
274
274
|
? document.location.search.substring(1).split('&')[0]
|
|
275
275
|
: '';
|
|
276
276
|
};
|
|
277
|
+
// Resolve a content-link anchor to one of our docs' filenames, in either the
|
|
278
|
+
// legacy `?filename` form or a clean `/slug/` path — or null for external
|
|
279
|
+
// links, in-page anchors, mailto:, downloads, etc. Used to navigate internal
|
|
280
|
+
// doc links client-side (via go()) instead of triggering a full page reload,
|
|
281
|
+
// and to canonicalize their hrefs for the current routing mode.
|
|
282
|
+
const docFilenameForHref = (anchor) => {
|
|
283
|
+
const raw = anchor.getAttribute('href') || '';
|
|
284
|
+
if (raw === '' || raw.startsWith('#'))
|
|
285
|
+
return null;
|
|
286
|
+
let url;
|
|
287
|
+
try {
|
|
288
|
+
url = new URL(raw, document.location.href);
|
|
289
|
+
}
|
|
290
|
+
catch {
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
if (url.origin !== document.location.origin)
|
|
294
|
+
return null; // external
|
|
295
|
+
const known = (filename) => docs.some((doc) => doc.filename === filename) ? filename : null;
|
|
296
|
+
// Legacy `?filename` form: a single bare filename, not a real query string.
|
|
297
|
+
const query = url.search.replace(/^\?/, '');
|
|
298
|
+
if (query !== '' && !query.includes('=') && !query.includes('&')) {
|
|
299
|
+
const filename = known(decodeURIComponent(query));
|
|
300
|
+
if (filename)
|
|
301
|
+
return filename;
|
|
302
|
+
}
|
|
303
|
+
// Clean `/slug/` path form (only meaningful when slugs are in play).
|
|
304
|
+
if (!memoryRouting && routing !== 'query') {
|
|
305
|
+
const filename = known(filenameForPath(url.pathname, slugMap));
|
|
306
|
+
if (filename)
|
|
307
|
+
return filename;
|
|
308
|
+
}
|
|
309
|
+
return null;
|
|
310
|
+
};
|
|
277
311
|
const docName = filenameFromLocation() || docs[0]?.filename || 'README.md';
|
|
278
312
|
const currentDoc = docs.find((doc) => doc.filename === docName) || docs[0];
|
|
279
313
|
const { app } = tosi({
|
|
@@ -512,6 +546,20 @@ export function createDocBrowser(options) {
|
|
|
512
546
|
// matches the initial doc. A memory-routed embed's initial doc
|
|
513
547
|
// (initialRoute/docs[0]) has no relation to whatever happens to sit inside the
|
|
514
548
|
// host element (often an empty placeholder), so always render it fresh.
|
|
549
|
+
// Canonicalize internal content links to the current routing mode's href and
|
|
550
|
+
// tag them — so copy-link / right-click / no-JS land on the right page, and a
|
|
551
|
+
// left-click can be short-circuited to client-side nav. Authored docs use the
|
|
552
|
+
// routing-agnostic `?filename` form (the only one resolvable in every mode);
|
|
553
|
+
// this rewrites them to `/slug/` on the static path-routed site, etc.
|
|
554
|
+
const rewriteContentLinks = () => {
|
|
555
|
+
for (const anchor of docContent.querySelectorAll('a')) {
|
|
556
|
+
const filename = docFilenameForHref(anchor);
|
|
557
|
+
if (filename !== null) {
|
|
558
|
+
anchor.setAttribute('href', hrefFor(filename));
|
|
559
|
+
anchor.dataset.docLink = filename;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
};
|
|
515
563
|
let adoptInitialContent = contentElement !== undefined && !memoryRouting;
|
|
516
564
|
const showDoc = (doc) => {
|
|
517
565
|
if (adoptInitialContent) {
|
|
@@ -520,6 +568,7 @@ export function createDocBrowser(options) {
|
|
|
520
568
|
else {
|
|
521
569
|
docContent.innerHTML = renderDocMarkdown(doc.text);
|
|
522
570
|
}
|
|
571
|
+
rewriteContentLinks();
|
|
523
572
|
// Stamp each example with its source file (for the source↔doc map). doc.path
|
|
524
573
|
// is the extracted file (.md, or a source file with doc comments).
|
|
525
574
|
LiveExample.insertExamples(docContent, context, doc.path || undefined);
|
|
@@ -554,6 +603,28 @@ export function createDocBrowser(options) {
|
|
|
554
603
|
}
|
|
555
604
|
navigateTo(filename);
|
|
556
605
|
};
|
|
606
|
+
// Intercept clicks on internal doc links anywhere in the rendered content so
|
|
607
|
+
// they navigate client-side instead of reloading the page. Plain left-clicks
|
|
608
|
+
// only — modified clicks / middle-clicks / new-tab / download links fall
|
|
609
|
+
// through to the browser. Works for both freshly-rendered and statically
|
|
610
|
+
// pre-rendered (adopted) content, resolving the href at click time.
|
|
611
|
+
docContent.addEventListener('click', (event) => {
|
|
612
|
+
if (event.defaultPrevented ||
|
|
613
|
+
event.button !== 0 ||
|
|
614
|
+
event.metaKey ||
|
|
615
|
+
event.ctrlKey ||
|
|
616
|
+
event.shiftKey ||
|
|
617
|
+
event.altKey)
|
|
618
|
+
return;
|
|
619
|
+
const anchor = event.target.closest('a');
|
|
620
|
+
if (!anchor || anchor.target === '_blank' || anchor.hasAttribute('download'))
|
|
621
|
+
return;
|
|
622
|
+
const filename = docFilenameForHref(anchor);
|
|
623
|
+
if (filename === null)
|
|
624
|
+
return;
|
|
625
|
+
event.preventDefault();
|
|
626
|
+
go(filename);
|
|
627
|
+
});
|
|
557
628
|
// ── Edit page source (Foundation B / #4) ──────────────────────────────────
|
|
558
629
|
// The "view source" affordance opens a menu: edit the whole source file in a
|
|
559
630
|
// code editor that fills the content area, preview the result in-browser, and
|
|
@@ -656,6 +727,7 @@ export function createDocBrowser(options) {
|
|
|
656
727
|
editUI.view = view;
|
|
657
728
|
if (view === 'preview') {
|
|
658
729
|
docContent.innerHTML = renderDocMarkdown(docMarkdownFromSource(editUI.editor.value, editUI.doc.path));
|
|
730
|
+
rewriteContentLinks();
|
|
659
731
|
LiveExample.insertExamples(docContent, context, editUI.doc.path || undefined);
|
|
660
732
|
}
|
|
661
733
|
else if (view === 'diff') {
|
|
@@ -12,6 +12,14 @@ export interface DocLike {
|
|
|
12
12
|
export declare function buildSlugMap(docs: DocLike[]): Record<string, string>;
|
|
13
13
|
/** site-root-relative path for a slug: '' -> '/', 'button' -> '/button/' */
|
|
14
14
|
export declare function pathForSlug(slug: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Rewrite legacy `?<filename>` (and `/?<filename>`) doc links in an HTML string
|
|
17
|
+
* to resolved hrefs. Used by the static generator so the pre-rendered pages have
|
|
18
|
+
* clean links for no-JS readers, crawlers, and the brief window before the
|
|
19
|
+
* doc-browser hydrates. `hrefFor` returns the target href for a known filename,
|
|
20
|
+
* or null to leave the link untouched (unknown filename / a real query string).
|
|
21
|
+
*/
|
|
22
|
+
export declare function rewriteDocLinks(html: string, hrefFor: (filename: string) => string | null): string;
|
|
15
23
|
/**
|
|
16
24
|
* Map a legacy `?<filename>` query (the old doc-browser's query-param routing,
|
|
17
25
|
* e.g. `?button.ts`, `?README.md`) to the new slug path (`/button/`, `/`).
|
|
@@ -46,6 +46,19 @@ export function buildSlugMap(docs) {
|
|
|
46
46
|
export function pathForSlug(slug) {
|
|
47
47
|
return slug === '' ? '/' : `/${slug}/`;
|
|
48
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Rewrite legacy `?<filename>` (and `/?<filename>`) doc links in an HTML string
|
|
51
|
+
* to resolved hrefs. Used by the static generator so the pre-rendered pages have
|
|
52
|
+
* clean links for no-JS readers, crawlers, and the brief window before the
|
|
53
|
+
* doc-browser hydrates. `hrefFor` returns the target href for a known filename,
|
|
54
|
+
* or null to leave the link untouched (unknown filename / a real query string).
|
|
55
|
+
*/
|
|
56
|
+
export function rewriteDocLinks(html, hrefFor) {
|
|
57
|
+
return html.replace(/href="\/?\?([^"&=]+)"/g, (match, query) => {
|
|
58
|
+
const href = hrefFor(decodeURIComponent(query));
|
|
59
|
+
return href === null ? match : `href="${href}"`;
|
|
60
|
+
});
|
|
61
|
+
}
|
|
49
62
|
/**
|
|
50
63
|
* Map a legacy `?<filename>` query (the old doc-browser's query-param routing,
|
|
51
64
|
* e.g. `?button.ts`, `?README.md`) to the new slug path (`/button/`, `/`).
|
|
@@ -10,7 +10,7 @@ the IIFE bundle loads.
|
|
|
10
10
|
Build-time only (uses Bun.write). Shares slug + markdown rendering with the runtime
|
|
11
11
|
component (src/doc-system/*) so static and hydrated output agree.
|
|
12
12
|
*/
|
|
13
|
-
import { buildSlugMap, pathForSlug } from '../routing';
|
|
13
|
+
import { buildSlugMap, pathForSlug, rewriteDocLinks } from '../routing';
|
|
14
14
|
import { buildNavTree, navOpenPath } from '../nav-tree';
|
|
15
15
|
import { renderDocMarkdown, docDescription } from '../render';
|
|
16
16
|
const escapeAttr = (s) => s
|
|
@@ -93,7 +93,12 @@ function pageHtml(doc, config, slugMap, configAttr) {
|
|
|
93
93
|
: doc.keywords || '';
|
|
94
94
|
const canonical = baseUrl + withBase(basePath, pathForSlug(slugMap[doc.filename]));
|
|
95
95
|
const imageAbs = absUrl(baseUrl, withBase(basePath, doc.image || config.ogImage || ''));
|
|
96
|
-
|
|
96
|
+
// Rewrite legacy `?filename` content links to clean `/slug/` paths so the
|
|
97
|
+
// static HTML is correct for no-JS readers and crawlers (the doc-browser also
|
|
98
|
+
// does this client-side after hydration).
|
|
99
|
+
const body = rewriteDocLinks(renderDocMarkdown(doc.text), (filename) => slugMap[filename] !== undefined
|
|
100
|
+
? withBase(basePath, pathForSlug(slugMap[filename]))
|
|
101
|
+
: null);
|
|
97
102
|
const nav = navHtml(config.docs, slugMap, doc.filename, basePath);
|
|
98
103
|
const navbar = linkListHtml('doc-navbar', config.navbarLinks);
|
|
99
104
|
const jsonLd = baseUrl
|