anentrypoint-design 0.0.454 → 0.0.456

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/dist/index.html CHANGED
@@ -76,7 +76,13 @@
76
76
  a.row:hover .ds-row-arrow { opacity: 1 }
77
77
  /* hero stat strip — all badges as a wrapping inline rhythm, not one empty panel */
78
78
  .ds-hero-stats { display: flex; flex-wrap: wrap; gap: var(--space-3, 16px) var(--space-5, 32px); margin-top: var(--space-2, 8px) }
79
- .ds-hero-stat { display: flex; align-items: baseline; gap: var(--space-2, 8px) }
79
+ /* hero-content.css's .ds-hero-stat is styled for a vertical stacked list
80
+ (border-bottom row divider, meant to read top-to-bottom); this page renders
81
+ badges in a horizontal row instead (.ds-hero-stats above), where that same
82
+ border reads as an underlined tab strip -- an affordance for a set of
83
+ plain, non-interactive metadata chips. Reset both inherited properties so a
84
+ row of badges never borrows tab-bar chrome it did not opt into. */
85
+ .ds-hero-stat { display: flex; align-items: baseline; gap: var(--space-2, 8px); border-bottom: none !important; padding-bottom: 0 !important }
80
86
  .ds-hero-stat-n { font-family: var(--ff-body); font-weight: 700; font-size: var(--fs-lg, 18px); color: var(--fg) }
81
87
  .ds-hero-stat-l { font-size: var(--fs-sm, 15px); color: var(--fg-3) }
82
88
  /* accent sits on its own line, muted, so it reads as a distinct aside instead
@@ -93,8 +99,24 @@ a.row:hover .ds-row-arrow { opacity: 1 }
93
99
  .ds-feature-title { font-weight: 600; font-size: var(--fs-lg, 18px); color: var(--fg) }
94
100
  .ds-feature-desc { font-size: var(--fs-sm, 15px); color: var(--fg-2); line-height: 1.5; overflow-wrap: anywhere }
95
101
  .ds-feature-benefit { font-style: italic; font-size: var(--fs-sm, 15px); color: var(--fg-3); margin-top: var(--space-1, 4px) }
102
+ /* Real closing content instead of the page simply stopping after its last
103
+ panel — a tonal divider line, a copyright string, and the same external
104
+ links the topbar already carries (footer.js reuses data.navItems, so this
105
+ is never invented content). Sits inside .app-stage's own grid gap like
106
+ every other block, no extra margin needed. */
107
+ .ds-page-footer {
108
+ display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
109
+ gap: var(--space-3, 16px);
110
+ padding-top: var(--space-5, 32px);
111
+ margin-top: var(--space-3, 16px);
112
+ border-top: 1px solid var(--rule, var(--bg-3));
113
+ color: var(--fg-3); font-size: var(--fs-sm, 15px);
114
+ }
115
+ .ds-page-footer-links { display: flex; gap: var(--space-4, 24px) }
116
+ .ds-page-footer-links a { color: var(--fg-3) }
117
+ .ds-page-footer-links a:hover { color: var(--fg) }
96
118
  </style>
97
- <script id="__site__" type="application/json">{"title":"design","slug":"index","siteName":"design","navItems":[["247420","https://247420.xyz"],["GitHub","https://github.com/AnEntrypoint"]],"theme":"auto","hero":{"heading":"design","subheading":"the 247420 design system","body":"shadcn-neutral. oversized display type, a grayscale accent, print texture inert. every 247420 surface runs on it.","badges":[{"label":"design-system"},{"label":"tokens"},{"label":"web"}],"ctas":[{"label":"GitHub","href":"https://github.com/AnEntrypoint/design","primary":true},{"label":"247420","href":"https://247420.xyz"}]},"sections":[],"examples":[{"label":"index shell","desc":"full navigation shell with tabs, filters, and row interactions.","href":"./index.html"},{"label":"homepage kit","desc":"hero + works layout and updated readable type scale.","href":"./ui_kits/homepage/"},{"label":"project page kit","desc":"overview, install, receipt, and changelog template.","href":"./ui_kits/project_page/"},{"label":"docs kit","desc":"technical documentation layout with sidebar and prose.","href":"./ui_kits/docs/"},{"label":"blog kit","desc":"long-form writing layout tuned for readability.","href":"./ui_kits/blog/"},{"label":"chat kit","desc":"message thread + composer with pill bubbles and room sidebar.","href":"./ui_kits/chat/"},{"label":"aicat kit","desc":"ai assistant with cat persona — ascii portrait + mood face.","href":"./ui_kits/aicat/"},{"label":"slide deck","desc":"16:9 deck stage with improved presenter controls text.","href":"./slides/"},{"label":"dashboard kit","desc":"kpis, tables, receipts, changelog — operations surface.","href":"./ui_kits/dashboard/"},{"label":"terminal kit","desc":"cli prompt, command lines, log viewer.","href":"./ui_kits/terminal/"},{"label":"settings kit","desc":"sectioned forms, toggles, segmented buttons, save bar.","href":"./ui_kits/settings/"},{"label":"signin kit","desc":"auth panel, providers, magic link.","href":"./ui_kits/signin/"},{"label":"search kit","desc":"query bar, faceted filters, ranked results.","href":"./ui_kits/search/"},{"label":"404 kit","desc":"empty-state hero with suggested routes.","href":"./ui_kits/error_404/"},{"label":"gallery kit","desc":"visual grid + lightbox over tonal cards.","href":"./ui_kits/gallery/"},{"label":"preview samples","desc":"design previews and component samples.","href":"./preview/buttons.html"}],"bodyHtml":"","sidebar":{"sections":[{"group":"open","items":[{"glyph":"+","label":"open","href":"./ui_kits/homepage/"}]},{"group":"bins","items":[{"label":"everything","href":"#all","count":23,"active":true},{"label":"pinned","href":"#kits","count":5},{"label":"shipped","href":"#previews","count":12},{"label":"drafts","href":"#decks","count":6}]},{"group":"labels","items":[{"label":"ui kits","href":"#kits","count":4},{"label":"decks","href":"#decks","count":1},{"label":"previews","href":"#previews","count":20},{"label":"docs","href":"#docs","count":3}]},{"group":"more","items":[{"label":"readme","href":"https://github.com/AnEntrypoint/design/blob/main/README.md"},{"label":"skill","href":"https://github.com/AnEntrypoint/design/blob/main/SKILL.md"},{"label":"tokens","href":"https://github.com/AnEntrypoint/design/blob/main/colors_and_type.css"},{"label":"source","href":"https://github.com/AnEntrypoint/design"}]}]},"marquee":{"items":["open source","design tokens","buildless components","zero raw color literals"],"sep":"/"},"panels":[{"id":"kits","title":"ui kits","count":"14 surfaces","items":[{"code":"","title":"homepage","sub":"marquee + works grid","meta":"open ->","href":"./ui_kits/homepage/"},{"code":"","title":"project page","sub":"hero receipt install changelog","meta":"open ->","href":"./ui_kits/project_page/"},{"code":"","title":"docs","sub":"long-form prose chrome","meta":"open ->","href":"./ui_kits/docs/"},{"code":"","title":"blog","sub":"dated index + reading view","meta":"open ->","href":"./ui_kits/blog/"},{"code":"","title":"chat","sub":"message thread + composer","meta":"open ->","href":"./ui_kits/chat/"},{"code":"","title":"aicat","sub":"ai assistant with cat persona","meta":"open ->","href":"./ui_kits/aicat/"},{"code":"","title":"file browser","sub":"rails by file type drop preview","meta":"open ->","href":"./ui_kits/file_browser/"},{"code":"","title":"dashboard","sub":"kpis tables receipts changelog","meta":"open ->","href":"./ui_kits/dashboard/"},{"code":"","title":"terminal","sub":"cli prompt command lines log view","meta":"open ->","href":"./ui_kits/terminal/"},{"code":"","title":"settings","sub":"sectioned forms toggles save bar","meta":"open ->","href":"./ui_kits/settings/"},{"code":"","title":"signin","sub":"auth providers magic link","meta":"open ->","href":"./ui_kits/signin/"},{"code":"","title":"search","sub":"topbar query faceted filters rows","meta":"open ->","href":"./ui_kits/search/"},{"code":"","title":"404","sub":"empty-state hero suggested routes","meta":"open ->","href":"./ui_kits/error_404/"},{"code":"","title":"gallery","sub":"visual grid tonal cards lightbox","meta":"open ->","href":"./ui_kits/gallery/"}]},{"id":"file_browser","title":"file browser","count":"primitives","items":[{"code":"fb","title":"file_browser kit","sub":"drop, list, preview, dialogs","meta":"open kit","href":"./ui_kits/file_browser/"},{"code":"fb","title":"FileRow","sub":"rail icon name meta","meta":"preview","href":"./preview/file-row.html"},{"code":"fb","title":"FileGrid","sub":"stack of rows in a panel","meta":"preview","href":"./preview/file-grid.html"},{"code":"fb","title":"DropZone","sub":"tonal upload target","meta":"preview","href":"./preview/dropzone.html"},{"code":"fb","title":"FileViewer","sub":"modal preview keyed head","meta":"preview","href":"./preview/file-viewer.html"},{"code":"fb","title":"FileToolbar","sub":"actions breadcrumb upload","meta":"preview","href":"./preview/file-toolbar.html"},{"code":"fb","title":"fsbrowse","sub":"Express + busboy backend","meta":"source","href":"https://github.com/AnEntrypoint/fsbrowse"}]},{"id":"desktop_os","title":"desktop os shell","count":"in-page wm","items":[{"code":"os","title":"createDesktopShell","sub":"menubar apps launcher taskbar window manager","meta":"shell.js","href":"https://github.com/AnEntrypoint/design/blob/main/src/desktop/shell.js"},{"code":"os","title":"icons set","sub":"svg glyphs for menubar + taskbar","meta":"icons.js","href":"https://github.com/AnEntrypoint/design/blob/main/src/desktop/icons.js"},{"code":"os","title":"theme.css","sub":"os chrome tokens menubar launcher wm","meta":"theme.css","href":"https://github.com/AnEntrypoint/design/blob/main/src/desktop/theme.css"},{"code":"os","title":"import path","sub":"anentrypoint-design/desktop","meta":"exports","href":"https://github.com/AnEntrypoint/design/blob/main/package.json"}]},{"id":"web_components","title":"web components","count":"1 element","items":[{"code":"wc","title":"\u003cds-chat>","sub":"auto-registers when the sdk loads in a browser","meta":"ds-chat.js","href":"https://github.com/AnEntrypoint/design/blob/main/src/web-components/ds-chat.js"},{"code":"wc","title":"registerChatElement","sub":"manual register helper","meta":"source","href":"https://github.com/AnEntrypoint/design/blob/main/src/index.js"}]},{"id":"api_exports","title":"public api","count":"sdk surface","items":[{"code":"api","title":"h / applyDiff","sub":"webjsx createElement + diff","meta":"index.js","href":"https://github.com/AnEntrypoint/design/blob/main/src/index.js"},{"code":"api","title":"installStyles / scope","sub":"ds-247420 css scope + bootstrap","meta":"styles.js","href":"https://github.com/AnEntrypoint/design/blob/main/src/styles.js"},{"code":"api","title":"loadCss","sub":"fetch the bundled css string","meta":"styles.js","href":"https://github.com/AnEntrypoint/design/blob/main/src/styles.js"},{"code":"api","title":"mount / mountKit","sub":"ds shell + ui_kit bootstrap","meta":"bootstrap.js","href":"https://github.com/AnEntrypoint/design/blob/main/src/bootstrap.js"},{"code":"api","title":"registerDeckStage / getDeckStage","sub":"16:9 deck stage api","meta":"deck-stage.js","href":"https://github.com/AnEntrypoint/design/blob/main/src/deck-stage.js"},{"code":"api","title":"renderMarkdown","sub":"marked v15 + dompurify lazy stack","meta":"markdown.js","href":"https://github.com/AnEntrypoint/design/blob/main/src/markdown.js"},{"code":"api","title":"ensurePrism / highlightAllUnder","sub":"prism core + per-language","meta":"highlight.js","href":"https://github.com/AnEntrypoint/design/blob/main/src/highlight.js"},{"code":"api","title":"renderPageHtml","sub":"static page html template","meta":"page-html.js","href":"https://github.com/AnEntrypoint/design/blob/main/src/page-html.js"},{"code":"api","title":"components / motion / debug","sub":"namespaces from the default export","meta":"components.js","href":"https://github.com/AnEntrypoint/design/blob/main/src/components.js"}]},{"id":"decks","title":"decks","count":1,"items":[{"code":"","title":"deck template","sub":"16:9 stage with auto-fit","meta":"open ->","href":"./slides/"}]},{"id":"docs","title":"docs","count":3,"items":[{"code":"md","title":"readme","sub":"overview, manifesto, conventions","meta":"open ->","href":"https://github.com/AnEntrypoint/design/blob/main/README.md"},{"code":"md","title":"skill","sub":"authoring rules for the agent","meta":"open ->","href":"https://github.com/AnEntrypoint/design/blob/main/SKILL.md"},{"code":"css","title":"tokens","sub":"colors, type, panel surface tokens","meta":"open ->","href":"https://github.com/AnEntrypoint/design/blob/main/colors_and_type.css"}]},{"id":"features","title":"why design","count":4,"items":[{"code":"","title":"layered surfaces","sub":"panel-on-panel rhythm; loud content sits inside quiet chrome.","meta":"panel-row.css","href":"https://github.com/AnEntrypoint/design/blob/main/src/css/app-shell/panel-row.css"},{"code":"","title":"monospace labels","sub":"mono only for codes/labels/CLI; sans for everything legible.","meta":"colors_and_type.css","href":"https://github.com/AnEntrypoint/design/blob/main/colors_and_type.css"},{"code":"","title":"indicator rails","sub":"color-coded inset rails for category separation, never borders.","meta":"panel-row.css","href":"https://github.com/AnEntrypoint/design/blob/main/src/css/app-shell/panel-row.css#L126"},{"code":"","title":"tokens-only","sub":"every surface, type ramp, accent driven by --panel-* / --fs-* tokens.","meta":"colors_and_type.css","href":"https://github.com/AnEntrypoint/design/blob/main/colors_and_type.css"}]},{"id":"previews","title":"previews","count":25,"items":[{"code":"","title":"buttons","sub":"preview · buttons.html","meta":"open ->","href":"./preview/buttons.html"},{"code":"","title":"colors core","sub":"preview · colors-core.html","meta":"open ->","href":"./preview/colors-core.html"},{"code":"","title":"colors lore","sub":"preview · colors-lore.html","meta":"open ->","href":"./preview/colors-lore.html"},{"code":"","title":"colors semantic","sub":"preview · colors-semantic.html","meta":"open ->","href":"./preview/colors-semantic.html"},{"code":"","title":"dateline","sub":"preview · dateline.html","meta":"open ->","href":"./preview/dateline.html"},{"code":"","title":"dropzone","sub":"preview · dropzone.html","meta":"open ->","href":"./preview/dropzone.html"},{"code":"","title":"file grid","sub":"preview · file-grid.html","meta":"open ->","href":"./preview/file-grid.html"},{"code":"","title":"file row","sub":"preview · file-row.html","meta":"open ->","href":"./preview/file-row.html"},{"code":"","title":"file toolbar","sub":"preview · file-toolbar.html","meta":"open ->","href":"./preview/file-toolbar.html"},{"code":"","title":"file viewer","sub":"preview · file-viewer.html","meta":"open ->","href":"./preview/file-viewer.html"},{"code":"","title":"header","sub":"preview · header.html","meta":"open ->","href":"./preview/header.html"},{"code":"","title":"icons unicode","sub":"preview · icons-unicode.html","meta":"open ->","href":"./preview/icons-unicode.html"},{"code":"","title":"index row","sub":"preview · index-row.html","meta":"open ->","href":"./preview/index-row.html"},{"code":"","title":"inputs","sub":"preview · inputs.html","meta":"open ->","href":"./preview/inputs.html"},{"code":"","title":"manifesto","sub":"preview · manifesto.html","meta":"open ->","href":"./preview/manifesto.html"},{"code":"","title":"rules","sub":"preview · rules.html","meta":"open ->","href":"./preview/rules.html"},{"code":"","title":"spacing","sub":"preview · spacing.html","meta":"open ->","href":"./preview/spacing.html"},{"code":"","title":"stamps","sub":"preview · stamps.html","meta":"open ->","href":"./preview/stamps.html"},{"code":"","title":"stamps lore","sub":"preview · stamps-lore.html","meta":"open ->","href":"./preview/stamps-lore.html"},{"code":"","title":"theme ink","sub":"preview · theme-ink.html","meta":"open ->","href":"./preview/theme-ink.html"},{"code":"","title":"type display","sub":"preview · type-display.html","meta":"open ->","href":"./preview/type-display.html"},{"code":"","title":"type mono","sub":"preview · type-mono.html","meta":"open ->","href":"./preview/type-mono.html"},{"code":"","title":"type prose","sub":"preview · type-prose.html","meta":"open ->","href":"./preview/type-prose.html"},{"code":"","title":"type scale","sub":"preview · type-scale.html","meta":"open ->","href":"./preview/type-scale.html"},{"code":"","title":"wordmarks","sub":"preview · wordmarks.html","meta":"open ->","href":"./preview/wordmarks.html"}]}],"quickstart":{"heading":"quick start","lines":[{"kind":"cmt","text":"# install via importmap (no bundler)"},{"kind":"cmd","text":"\u003cscript type='importmap'>{\"imports\":{\"anentrypoint-design\":\"https://unpkg.com/anentrypoint-design@latest/dist/247420.js\"}}\u003c/script>"},{"kind":"cmt","text":"# or clone the source"},{"kind":"cmd","text":"git clone https://github.com/AnEntrypoint/design.git"}]},"statusLeft":["main","utf-8","lf"],"statusRight":["247420 · mmxxvi","probably emerging"]}</script>
119
+ <script id="__site__" type="application/json">{"title":"design","slug":"index","siteName":"design","navItems":[["247420","https://247420.xyz"],["AnEntrypoint ->","https://github.com/AnEntrypoint"]],"theme":"auto","hero":{"heading":"design","subheading":"the 247420 design system","body":"14 kit surfaces, zero raw color literals, one importmap script tag to install. every panel, row, and form control in this system reads its color, radius, and spacing from a single token file — swap the tokens and every surface built on them changes with it.","badges":[{"label":"design-system"},{"label":"tokens"},{"label":"web"}],"ctas":[{"label":"GitHub","href":"https://github.com/AnEntrypoint/design","primary":true},{"label":"247420","href":"https://247420.xyz"}]},"sections":[],"examples":[{"label":"index shell","desc":"full navigation shell with tabs, filters, and row interactions.","href":"./index.html"},{"label":"homepage kit","desc":"hero + works layout and updated readable type scale.","href":"./ui_kits/homepage/"},{"label":"project page kit","desc":"overview, install, receipt, and changelog template.","href":"./ui_kits/project_page/"},{"label":"docs kit","desc":"technical documentation layout with sidebar and prose.","href":"./ui_kits/docs/"},{"label":"blog kit","desc":"long-form writing layout tuned for readability.","href":"./ui_kits/blog/"},{"label":"chat kit","desc":"message thread + composer with pill bubbles and room sidebar.","href":"./ui_kits/chat/"},{"label":"aicat kit","desc":"ai assistant with cat persona — ascii portrait + mood face.","href":"./ui_kits/aicat/"},{"label":"slide deck","desc":"16:9 deck stage with improved presenter controls text.","href":"./slides/"},{"label":"dashboard kit","desc":"kpis, tables, receipts, changelog — operations surface.","href":"./ui_kits/dashboard/"},{"label":"terminal kit","desc":"cli prompt, command lines, log viewer.","href":"./ui_kits/terminal/"},{"label":"settings kit","desc":"sectioned forms, toggles, segmented buttons, save bar.","href":"./ui_kits/settings/"},{"label":"signin kit","desc":"auth panel, providers, magic link.","href":"./ui_kits/signin/"},{"label":"search kit","desc":"query bar, faceted filters, ranked results.","href":"./ui_kits/search/"},{"label":"404 kit","desc":"empty-state hero with suggested routes.","href":"./ui_kits/error_404/"},{"label":"gallery kit","desc":"visual grid + lightbox over tonal cards.","href":"./ui_kits/gallery/"},{"label":"preview samples","desc":"design previews and component samples.","href":"./preview/buttons.html"}],"bodyHtml":"","sidebar":{"sections":[{"group":"open","items":[{"glyph":"+","label":"open","href":"./ui_kits/homepage/"}]},{"group":"bins","items":[{"label":"everything","href":"#all","active":true,"count":43},{"label":"pinned","href":"#kits","count":14},{"label":"shipped","href":"#previews","count":25},{"label":"drafts","href":"#decks","count":1}]},{"group":"labels","items":[{"label":"ui kits","href":"#kits","count":14},{"label":"decks","href":"#decks","count":1},{"label":"previews","href":"#previews","count":25},{"label":"docs","href":"#docs","count":3}]},{"group":"more","items":[{"label":"readme","href":"https://github.com/AnEntrypoint/design/blob/main/README.md"},{"label":"skill","href":"https://github.com/AnEntrypoint/design/blob/main/SKILL.md"},{"label":"tokens","href":"https://github.com/AnEntrypoint/design/blob/main/colors_and_type.css"},{"label":"source","href":"https://github.com/AnEntrypoint/design"}]}]},"marquee":{"items":["open source","design tokens","buildless components","zero raw color literals"],"sep":"/"},"panels":[{"id":"kits","title":"ui kits","count":"14 surfaces","items":[{"code":"","title":"homepage","sub":"marquee + works grid","meta":"open ->","href":"./ui_kits/homepage/"},{"code":"","title":"project page","sub":"hero receipt install changelog","meta":"open ->","href":"./ui_kits/project_page/"},{"code":"","title":"docs","sub":"long-form prose chrome","meta":"open ->","href":"./ui_kits/docs/"},{"code":"","title":"blog","sub":"dated index + reading view","meta":"open ->","href":"./ui_kits/blog/"},{"code":"","title":"chat","sub":"message thread + composer","meta":"open ->","href":"./ui_kits/chat/"},{"code":"","title":"aicat","sub":"ai assistant with cat persona","meta":"open ->","href":"./ui_kits/aicat/"},{"code":"","title":"file browser","sub":"rails by file type drop preview","meta":"open ->","href":"./ui_kits/file_browser/"},{"code":"","title":"dashboard","sub":"kpis tables receipts changelog","meta":"open ->","href":"./ui_kits/dashboard/"},{"code":"","title":"terminal","sub":"cli prompt command lines log view","meta":"open ->","href":"./ui_kits/terminal/"},{"code":"","title":"settings","sub":"sectioned forms toggles save bar","meta":"open ->","href":"./ui_kits/settings/"},{"code":"","title":"signin","sub":"auth providers magic link","meta":"open ->","href":"./ui_kits/signin/"},{"code":"","title":"search","sub":"topbar query faceted filters rows","meta":"open ->","href":"./ui_kits/search/"},{"code":"","title":"404","sub":"empty-state hero suggested routes","meta":"open ->","href":"./ui_kits/error_404/"},{"code":"","title":"gallery","sub":"visual grid tonal cards lightbox","meta":"open ->","href":"./ui_kits/gallery/"}]},{"id":"file_browser","title":"file browser","count":"primitives","items":[{"code":"fb","title":"file_browser kit","sub":"drop, list, preview, dialogs","meta":"open kit","href":"./ui_kits/file_browser/"},{"code":"fb","title":"FileRow","sub":"rail icon name meta","meta":"preview","href":"./preview/file-row.html"},{"code":"fb","title":"FileGrid","sub":"stack of rows in a panel","meta":"preview","href":"./preview/file-grid.html"},{"code":"fb","title":"DropZone","sub":"tonal upload target","meta":"preview","href":"./preview/dropzone.html"},{"code":"fb","title":"FileViewer","sub":"modal preview keyed head","meta":"preview","href":"./preview/file-viewer.html"},{"code":"fb","title":"FileToolbar","sub":"actions breadcrumb upload","meta":"preview","href":"./preview/file-toolbar.html"},{"code":"fb","title":"fsbrowse","sub":"Express + busboy backend","meta":"source","href":"https://github.com/AnEntrypoint/fsbrowse"}]},{"id":"desktop_os","title":"desktop os shell","count":"in-page wm","items":[{"code":"os","title":"createDesktopShell","sub":"menubar apps launcher taskbar window manager","meta":"shell.js","href":"https://github.com/AnEntrypoint/design/blob/main/src/desktop/shell.js"},{"code":"os","title":"icons set","sub":"svg glyphs for menubar + taskbar","meta":"icons.js","href":"https://github.com/AnEntrypoint/design/blob/main/src/desktop/icons.js"},{"code":"os","title":"theme.css","sub":"os chrome tokens menubar launcher wm","meta":"theme.css","href":"https://github.com/AnEntrypoint/design/blob/main/src/desktop/theme.css"},{"code":"os","title":"import path","sub":"anentrypoint-design/desktop","meta":"exports","href":"https://github.com/AnEntrypoint/design/blob/main/package.json"}]},{"id":"web_components","title":"web components","count":"1 element","items":[{"code":"wc","title":"\u003cds-chat>","sub":"auto-registers when the sdk loads in a browser","meta":"ds-chat.js","href":"https://github.com/AnEntrypoint/design/blob/main/src/web-components/ds-chat.js"},{"code":"wc","title":"registerChatElement","sub":"manual register helper","meta":"source","href":"https://github.com/AnEntrypoint/design/blob/main/src/index.js"}]},{"id":"api_exports","title":"public api","count":"sdk surface","items":[{"code":"api","title":"h / applyDiff","sub":"webjsx createElement + diff","meta":"index.js","href":"https://github.com/AnEntrypoint/design/blob/main/src/index.js"},{"code":"api","title":"installStyles / scope","sub":"ds-247420 css scope + bootstrap","meta":"styles.js","href":"https://github.com/AnEntrypoint/design/blob/main/src/styles.js"},{"code":"api","title":"loadCss","sub":"fetch the bundled css string","meta":"styles.js","href":"https://github.com/AnEntrypoint/design/blob/main/src/styles.js"},{"code":"api","title":"mount / mountKit","sub":"ds shell + ui_kit bootstrap","meta":"bootstrap.js","href":"https://github.com/AnEntrypoint/design/blob/main/src/bootstrap.js"},{"code":"api","title":"registerDeckStage / getDeckStage","sub":"16:9 deck stage api","meta":"deck-stage.js","href":"https://github.com/AnEntrypoint/design/blob/main/src/deck-stage.js"},{"code":"api","title":"renderMarkdown","sub":"marked v15 + dompurify lazy stack","meta":"markdown.js","href":"https://github.com/AnEntrypoint/design/blob/main/src/markdown.js"},{"code":"api","title":"ensurePrism / highlightAllUnder","sub":"prism core + per-language","meta":"highlight.js","href":"https://github.com/AnEntrypoint/design/blob/main/src/highlight.js"},{"code":"api","title":"renderPageHtml","sub":"static page html template","meta":"page-html.js","href":"https://github.com/AnEntrypoint/design/blob/main/src/page-html.js"},{"code":"api","title":"components / motion / debug","sub":"namespaces from the default export","meta":"components.js","href":"https://github.com/AnEntrypoint/design/blob/main/src/components.js"}]},{"id":"decks","title":"decks","count":1,"items":[{"code":"","title":"deck template","sub":"16:9 stage with auto-fit","meta":"open ->","href":"./slides/"}]},{"id":"docs","title":"docs","count":3,"items":[{"code":"md","title":"readme","sub":"overview, manifesto, conventions","meta":"open ->","href":"https://github.com/AnEntrypoint/design/blob/main/README.md"},{"code":"md","title":"skill","sub":"authoring rules for the agent","meta":"open ->","href":"https://github.com/AnEntrypoint/design/blob/main/SKILL.md"},{"code":"css","title":"tokens","sub":"colors, type, panel surface tokens","meta":"open ->","href":"https://github.com/AnEntrypoint/design/blob/main/colors_and_type.css"}]},{"id":"features","title":"why design","count":4,"items":[{"code":"","title":"layered surfaces","sub":"panel-on-panel rhythm; loud content sits inside quiet chrome.","meta":"panel-row.css","href":"https://github.com/AnEntrypoint/design/blob/main/src/css/app-shell/panel-row.css"},{"code":"","title":"monospace labels","sub":"mono only for codes/labels/CLI; sans for everything legible.","meta":"colors_and_type.css","href":"https://github.com/AnEntrypoint/design/blob/main/colors_and_type.css"},{"code":"","title":"indicator rails","sub":"color-coded inset rails for category separation, never borders.","meta":"panel-row.css","href":"https://github.com/AnEntrypoint/design/blob/main/src/css/app-shell/panel-row.css#L126"},{"code":"","title":"tokens-only","sub":"every surface, type ramp, accent driven by --panel-* / --fs-* tokens.","meta":"colors_and_type.css","href":"https://github.com/AnEntrypoint/design/blob/main/colors_and_type.css"}]},{"id":"previews","title":"previews","count":25,"items":[{"code":"","title":"buttons","sub":"preview · buttons.html","meta":"open ->","href":"./preview/buttons.html"},{"code":"","title":"colors core","sub":"preview · colors-core.html","meta":"open ->","href":"./preview/colors-core.html"},{"code":"","title":"colors lore","sub":"preview · colors-lore.html","meta":"open ->","href":"./preview/colors-lore.html"},{"code":"","title":"colors semantic","sub":"preview · colors-semantic.html","meta":"open ->","href":"./preview/colors-semantic.html"},{"code":"","title":"dateline","sub":"preview · dateline.html","meta":"open ->","href":"./preview/dateline.html"},{"code":"","title":"dropzone","sub":"preview · dropzone.html","meta":"open ->","href":"./preview/dropzone.html"},{"code":"","title":"file grid","sub":"preview · file-grid.html","meta":"open ->","href":"./preview/file-grid.html"},{"code":"","title":"file row","sub":"preview · file-row.html","meta":"open ->","href":"./preview/file-row.html"},{"code":"","title":"file toolbar","sub":"preview · file-toolbar.html","meta":"open ->","href":"./preview/file-toolbar.html"},{"code":"","title":"file viewer","sub":"preview · file-viewer.html","meta":"open ->","href":"./preview/file-viewer.html"},{"code":"","title":"header","sub":"preview · header.html","meta":"open ->","href":"./preview/header.html"},{"code":"","title":"icons unicode","sub":"preview · icons-unicode.html","meta":"open ->","href":"./preview/icons-unicode.html"},{"code":"","title":"index row","sub":"preview · index-row.html","meta":"open ->","href":"./preview/index-row.html"},{"code":"","title":"inputs","sub":"preview · inputs.html","meta":"open ->","href":"./preview/inputs.html"},{"code":"","title":"manifesto","sub":"preview · manifesto.html","meta":"open ->","href":"./preview/manifesto.html"},{"code":"","title":"rules","sub":"preview · rules.html","meta":"open ->","href":"./preview/rules.html"},{"code":"","title":"spacing","sub":"preview · spacing.html","meta":"open ->","href":"./preview/spacing.html"},{"code":"","title":"stamps","sub":"preview · stamps.html","meta":"open ->","href":"./preview/stamps.html"},{"code":"","title":"stamps lore","sub":"preview · stamps-lore.html","meta":"open ->","href":"./preview/stamps-lore.html"},{"code":"","title":"theme ink","sub":"preview · theme-ink.html","meta":"open ->","href":"./preview/theme-ink.html"},{"code":"","title":"type display","sub":"preview · type-display.html","meta":"open ->","href":"./preview/type-display.html"},{"code":"","title":"type mono","sub":"preview · type-mono.html","meta":"open ->","href":"./preview/type-mono.html"},{"code":"","title":"type prose","sub":"preview · type-prose.html","meta":"open ->","href":"./preview/type-prose.html"},{"code":"","title":"type scale","sub":"preview · type-scale.html","meta":"open ->","href":"./preview/type-scale.html"},{"code":"","title":"wordmarks","sub":"preview · wordmarks.html","meta":"open ->","href":"./preview/wordmarks.html"}]}],"quickstart":{"heading":"quick start","lines":[{"kind":"cmt","text":"# install via importmap (no bundler)"},{"kind":"cmd","text":"\u003cscript type='importmap'>{\"imports\":{\"anentrypoint-design\":\"https://unpkg.com/anentrypoint-design@latest/dist/247420.js\"}}\u003c/script>"},{"kind":"cmt","text":"# or clone the source"},{"kind":"cmd","text":"git clone https://github.com/AnEntrypoint/design.git"}]},"statusLeft":["main","utf-8","lf"],"statusRight":["anentrypoint-design@latest","14 kits"]}</script>
98
120
  <style>html,body{margin:0;padding:0}body{background:var(--bg,#F6F5F1);color:var(--fg,#131318);font-family:var(--ff-body,system-ui,sans-serif)}</style>
99
121
  </head>
100
122
  <body>
@@ -183,6 +205,39 @@ function marqueeNode(marquee) {
183
205
  return C.Marquee ? C.Marquee({ items: marquee.items, sep: marquee.sep || '/' }) : null;
184
206
  }
185
207
 
208
+ let __copyResetTimer = null;
209
+ function copyQuickstart(text, btnEl) {
210
+ const done = (ok) => {
211
+ if (!btnEl) return;
212
+ btnEl.textContent = ok ? 'copied' : 'copy failed';
213
+ clearTimeout(__copyResetTimer);
214
+ __copyResetTimer = setTimeout(() => { btnEl.textContent = 'copy'; }, 1600);
215
+ };
216
+ if (navigator.clipboard && navigator.clipboard.writeText) {
217
+ navigator.clipboard.writeText(text).then(() => done(true), () => copyViaTextarea(text, done));
218
+ } else {
219
+ copyViaTextarea(text, done);
220
+ }
221
+ }
222
+ // Fallback for non-secure contexts / browsers without navigator.clipboard:
223
+ // a hidden textarea + document.execCommand('copy'), the standard shim.
224
+ function copyViaTextarea(text, done) {
225
+ try {
226
+ const ta = document.createElement('textarea');
227
+ ta.value = text;
228
+ ta.style.position = 'fixed';
229
+ ta.style.opacity = '0';
230
+ document.body.appendChild(ta);
231
+ ta.focus();
232
+ ta.select();
233
+ const ok = document.execCommand('copy');
234
+ document.body.removeChild(ta);
235
+ done(ok);
236
+ } catch (_e) {
237
+ done(false);
238
+ }
239
+ }
240
+
186
241
  function quickstartNode(quickstart) {
187
242
  if (!quickstart || !Array.isArray(quickstart.lines) || !quickstart.lines.length) return null;
188
243
  // A single .cli wrapper holding .ds-cli-row/.ds-cli-comment children, not
@@ -196,7 +251,19 @@ function quickstartNode(quickstart) {
196
251
  : h('div', { key: 'q' + i, class: 'ds-cli-row' },
197
252
  h('span', { class: 'prompt' }, '$'),
198
253
  h('span', { class: 'cmd' }, l.text)));
199
- return C.Panel({ title: quickstart.heading || 'quick start', children: h('div', { class: 'cli' }, ...lineNodes) });
254
+ // Copy the whole snippet (comment lines included) as one paste-ready block,
255
+ // not just the command lines -- a reader copying "the quick start" expects
256
+ // what they see, not a silently filtered subset.
257
+ const fullText = quickstart.lines.map((l) => l.text).join('\n');
258
+ const copyBtn = h('button', {
259
+ type: 'button', class: 'copy ds-quickstart-copy',
260
+ 'aria-label': 'copy quick start snippet',
261
+ onclick: (e) => copyQuickstart(fullText, e.currentTarget),
262
+ }, 'copy');
263
+ return C.Panel({
264
+ title: quickstart.heading || 'quick start',
265
+ children: h('div', { class: 'cli ds-quickstart-cli' }, h('div', { class: 'ds-cli-block' }, ...lineNodes), copyBtn),
266
+ });
200
267
  }
201
268
 
202
269
  function sideNode(sidebar) {
@@ -227,6 +294,21 @@ function __md(md) {
227
294
 
228
295
  const bodyNode = data.bodyHtml ? C.Section({ children: h('div', { class: 'page-body', innerHTML: data.bodyHtml }) }) : null;
229
296
 
297
+ // A real closing block: the page previously just stopped after its last
298
+ // panel, leaving nothing but the app-stage's own bottom padding as unexplained
299
+ // empty space. Reuses data already on the page (nav links, siteName, year) —
300
+ // no invented content.
301
+ function footerNode() {
302
+ const year = new Date().getFullYear();
303
+ const links = (data.navItems || []).filter(([, href]) => /^https?:/.test(String(href)));
304
+ return h('footer', { class: 'ds-page-footer' },
305
+ h('span', { class: 'ds-page-footer-copy' }, String(year) + ' · ' + data.siteName),
306
+ links.length ? h('nav', { class: 'ds-page-footer-links', 'aria-label': 'footer' },
307
+ ...links.map(([label, href], i) => h('a', { key: i, href }, String(label).replace(' ->', '')))
308
+ ) : null,
309
+ );
310
+ }
311
+
230
312
  function tierNode(tier, children) {
231
313
  const kids = children.filter(Boolean);
232
314
  if (!kids.length) return null;
@@ -264,6 +346,7 @@ const mainChildren = [
264
346
  ...tierNodes,
265
347
  ...leftoverPanels,
266
348
  bodyNode,
349
+ footerNode(),
267
350
  ].filter(Boolean);
268
351
 
269
352
  mount(document.getElementById('app'), () => C.AppShell({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anentrypoint-design",
3
- "version": "0.0.454",
3
+ "version": "0.0.456",
4
4
  "description": "247420 design system SDK — webjsx + modified ripple-ui, single-file ESM bundle for reproducible use of the AnEntrypoint design.",
5
5
  "type": "module",
6
6
  "main": "./dist/247420.js",
@@ -81,7 +81,7 @@ export function AgentControls({ agents, selectedAgent, models, selectedModel, bu
81
81
  // renders them as a breadcrumb + clickable dir list right in the composer
82
82
  // — no round-trip through the Files tab and back required anymore.
83
83
  export function CwdBar({ cwd, editing, draft, onEdit, onSave, onCancel, onClear, onDraft, error, checking,
84
- roots, recent, browse, onBrowseCrumb, onBrowseEnter, onBrowsePick, onBrowseToggle }) {
84
+ roots, recent, browse, onBrowseCrumb, onBrowseEnter, onBrowsePick, onBrowseToggle, defaultCwd }) {
85
85
  if (editing) {
86
86
  const hint = checking ? 'checking…' : (error || null);
87
87
  const rootsRow = (roots && roots.length)
@@ -139,5 +139,9 @@ export function CwdBar({ cwd, editing, draft, onEdit, onSave, onCancel, onClear,
139
139
  h('span', { class: 'agentchat-cwd-text', title: cwd || 'server default working directory' },
140
140
  'cwd: ' + (cwd || 'server default')),
141
141
  h('button', { type: 'button', class: 'agentchat-cwd-btn', onclick: () => onEdit && onEdit() }, cwd ? 'change' : 'set'),
142
- cwd ? h('button', { type: 'button', class: 'agentchat-cwd-btn', onclick: () => onClear && onClear() }, 'use default') : null);
142
+ // What "default" resolves to previously wasn't visible until clicked -
143
+ // a title tooltip on the button itself answers that before the click.
144
+ cwd ? h('button', { type: 'button', class: 'agentchat-cwd-btn',
145
+ title: defaultCwd ? ('resets to: ' + defaultCwd) : 'reset to the server default working directory',
146
+ onclick: () => onClear && onClear() }, 'use default') : null);
143
147
  }
@@ -28,7 +28,7 @@ export function AgentChat(props = {}) {
28
28
  agents = [], selectedAgent = '', models = [], selectedModel = '', modelsLoading = false, agentsLoading = false,
29
29
  messages = [], busy = false, draft = '', status, banners = [],
30
30
  cwd = '', cwdEditing = false, cwdDraft, cwdError, cwdChecking = false,
31
- cwdRoots, cwdRecent, cwdBrowse,
31
+ cwdRoots, cwdRecent, cwdBrowse, defaultCwd,
32
32
  agentName, placeholder,
33
33
  onSelectAgent, onSelectModel, onSend, onStop, onNewChat, onInput,
34
34
  onCwdEdit, onCwdSave, onCwdCancel, onCwdClear, onCwdDraft,
@@ -197,7 +197,7 @@ export function AgentChat(props = {}) {
197
197
  AgentControls({ agents, selectedAgent, models, selectedModel, busy, status, modelsLoading, agentsLoading,
198
198
  onSelectAgent, onSelectModel, onNewChat, onStop, exportActions }),
199
199
  CwdBar({ cwd, editing: cwdEditing, draft: cwdDraft, error: cwdError, checking: cwdChecking,
200
- roots: cwdRoots, recent: cwdRecent, browse: cwdBrowse,
200
+ roots: cwdRoots, recent: cwdRecent, browse: cwdBrowse, defaultCwd,
201
201
  onEdit: onCwdEdit, onSave: onCwdSave, onCancel: onCwdCancel, onClear: onCwdClear, onDraft: onCwdDraft,
202
202
  onBrowseToggle: onCwdBrowseToggle, onBrowseCrumb: onCwdBrowseCrumb, onBrowseEnter: onCwdBrowseEnter, onBrowsePick: onCwdBrowsePick }),
203
203
  ...(banners || []).filter(Boolean),
@@ -144,7 +144,7 @@ function langFromFilename(filename = '') {
144
144
  // highlight.js's Prism loader for language detection by file extension; the
145
145
  // +/- marker column is drawn by CSS (tone classes), Prism only tokenizes the
146
146
  // code content inside each line.
147
- export function GitDiffView({ diff = '', filename } = {}) {
147
+ export function GitDiffView({ diff = '', filename, binary = false } = {}) {
148
148
  const hunks = parseUnifiedDiff(diff);
149
149
  const lang = langFromFilename(filename);
150
150
  const highlightRef = (el) => {
@@ -152,7 +152,11 @@ export function GitDiffView({ diff = '', filename } = {}) {
152
152
  try { highlightAllUnder(el); } catch { /* swallow: progressive enhancement only */ }
153
153
  };
154
154
  if (!hunks.length) {
155
- return h('div', { class: 'ds-git-diff-empty', role: 'status' }, 'no diff to show');
155
+ // A binary changed file produces no unified diff at all - saying
156
+ // "no diff to show" reads as "nothing changed", which is wrong and
157
+ // misleading; name the real reason instead.
158
+ return h('div', { class: 'ds-git-diff-empty', role: 'status' },
159
+ binary ? 'binary file, diff not shown' : 'no diff to show');
156
160
  }
157
161
  return h('div', { class: 'ds-git-diff', ref: highlightRef },
158
162
  filename ? h('div', { class: 'ds-git-diff-head' }, h('span', { class: 'name' }, filename)) : null,
@@ -156,7 +156,11 @@ export function SessionDashboard({ sessions = [], onStop, onOpen, onView, onStop
156
156
  selectable && selCount
157
157
  ? h('span', { key: 'cnt', class: 'ds-dash-count', role: 'status', 'aria-live': 'polite' }, selCount + ' selected')
158
158
  : (breakdown || h('span', { key: 'cnt', class: 'ds-dash-count', role: 'status', 'aria-live': 'polite' },
159
- sessions.length ? sessions.length + ' running' : '0 running')),
159
+ // A "0 running" count is only trustworthy once the stream has
160
+ // actually connected - while still connecting/offline, zero means
161
+ // "no data yet", not "verified empty", and must read as such.
162
+ sessions.length ? sessions.length + ' running'
163
+ : (streamState && streamState !== 'connected' ? '— running (' + (STREAM_WORD[streamState] || streamState) + ')' : '0 running'))),
160
164
  selectAllCtl, clearCtl, streamLine,
161
165
  h('span', { key: 'spread', class: 'spread' }),
162
166
  // No stop control without a session to stop; the empty dashboard keeps
@@ -127,6 +127,24 @@
127
127
  static readouts). */
128
128
  .ds-session-row[role="button"], a.ds-session-row { cursor: pointer; }
129
129
 
130
+ /* ConversationList's title/meta layout had no dedicated rule anywhere (only a
131
+ stray cross-reference comment in panel-row.css), so the title sat at flex's
132
+ default 0-basis and lost the space race to its meta siblings (agent name,
133
+ status dot) - titles truncated to a character or two ("d…") well before the
134
+ row ran out of real width. .ds-session-main grows to fill the row; its
135
+ title/sub children get the min-width:0 an ellipsis needs inside a flex
136
+ item; the meta/actions siblings take only what their content needs. */
137
+ .ds-session-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 0; }
138
+ .ds-session-title {
139
+ display: block; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
140
+ font-size: var(--fs-sm); font-weight: 500; color: var(--fg);
141
+ }
142
+ .ds-session-sub {
143
+ display: block; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
144
+ font-size: var(--fs-micro); color: var(--fg-3);
145
+ }
146
+ .ds-session-meta, .ds-session-row-actions { flex: 0 0 auto; display: flex; align-items: center; gap: var(--space-1); }
147
+
130
148
  /* Sub-parts that decorate the row above. */
131
149
  .ds-session-row-id {
132
150
  font-family: var(--ff-mono); font-size: var(--fs-micro); color: var(--accent-ink);
@@ -217,13 +217,33 @@
217
217
  padding: 3px 0;
218
218
  }
219
219
  .ds-cli-row .prompt { color: var(--green-2); flex: 0 0 auto; user-select: none; }
220
- .ds-cli-row .cmd { flex: 1 1 auto; white-space: pre-wrap; word-break: break-word; color: var(--paper); }
220
+ /* `overflow-wrap: break-word` only breaks a token when it genuinely cannot
221
+ fit the line at all (last resort); `word-break: break-word` (its
222
+ predecessor here) breaks ANY overflowing token eagerly, which is what
223
+ split "anentrypoint-design" mid-identifier inside the importmap JSON line.
224
+ `min-width: 0` lets the flex child actually shrink to trigger wrapping
225
+ instead of pushing the panel wider. */
226
+ .ds-cli-row .cmd { flex: 1 1 auto; min-width: 0; white-space: pre-wrap; overflow-wrap: break-word; word-break: normal; color: var(--paper); }
221
227
  .ds-cli-comment {
222
228
  color: var(--fg-3);
223
229
  font-family: var(--ff-mono); font-size: var(--fs-tiny); line-height: 1.6;
224
- padding: 3px 0; white-space: pre-wrap; word-break: break-word;
230
+ padding: 3px 0; white-space: pre-wrap; overflow-wrap: break-word; word-break: normal;
225
231
  }
226
232
 
233
+ /* Quickstart copy button: sits below the multi-line block (Install's inline
234
+ copy button assumes a single-line row with room beside it; the stacked
235
+ quickstart block has no such side room). */
236
+ .ds-quickstart-cli { flex-direction: column; align-items: stretch; gap: var(--space-2); }
237
+ .ds-quickstart-copy {
238
+ align-self: flex-end;
239
+ padding: 8px 16px; background: transparent; color: var(--paper);
240
+ font-family: var(--ff-mono); font-size: var(--fs-tiny); text-transform: uppercase; letter-spacing: var(--tr-caps);
241
+ cursor: pointer; box-shadow: inset 0 0 0 1px var(--rule-strong);
242
+ border-radius: var(--r-pill); border: none;
243
+ }
244
+ .ds-quickstart-copy:hover { background: var(--fg-3); }
245
+ .ds-quickstart-copy:focus-visible { outline: var(--focus-w) solid var(--focus-color); outline-offset: var(--focus-offset); }
246
+
227
247
  /* ============================================================
228
248
  Receipt — key/value table
229
249
  ============================================================ */
@@ -155,7 +155,12 @@ body.canvas-host { background: transparent !important; }
155
155
  .app-crumb .leaf { color: var(--fg); font-weight: 600; }
156
156
  .app-crumb .crumb-right { margin-left: auto; color: var(--fg-3); }
157
157
 
158
- .app-side-shell { background: var(--bg); }
158
+ /* The shell is the grid cell (.app-body's fixed sidebar column); it must own
159
+ its own scroll, not just size to content, because .app-body's row track is
160
+ `minmax(0, 1fr)` — a real height the sidebar can overflow once bins +
161
+ labels + more exceed it. Without this the extra rows were silently chopped
162
+ at the viewport edge with no way to reach them. */
163
+ .app-side-shell { background: var(--bg); overflow-y: auto; min-height: 0; }
159
164
  .app-body { display: grid; grid-template-columns: var(--sidebar-width) minmax(0, 1fr); grid-template-rows: minmax(0, 1fr); align-items: stretch; align-content: stretch; flex: 1; min-height: 0; }
160
165
  .app-body.no-side { grid-template-columns: minmax(0, 1fr); }
161
166
  /* When there is no sidebar, the empty side-shell must not occupy a grid row,
@@ -91,6 +91,39 @@ function marqueeNode(marquee) {
91
91
  return C.Marquee ? C.Marquee({ items: marquee.items, sep: marquee.sep || '/' }) : null;
92
92
  }
93
93
 
94
+ let __copyResetTimer = null;
95
+ function copyQuickstart(text, btnEl) {
96
+ const done = (ok) => {
97
+ if (!btnEl) return;
98
+ btnEl.textContent = ok ? 'copied' : 'copy failed';
99
+ clearTimeout(__copyResetTimer);
100
+ __copyResetTimer = setTimeout(() => { btnEl.textContent = 'copy'; }, 1600);
101
+ };
102
+ if (navigator.clipboard && navigator.clipboard.writeText) {
103
+ navigator.clipboard.writeText(text).then(() => done(true), () => copyViaTextarea(text, done));
104
+ } else {
105
+ copyViaTextarea(text, done);
106
+ }
107
+ }
108
+ // Fallback for non-secure contexts / browsers without navigator.clipboard:
109
+ // a hidden textarea + document.execCommand('copy'), the standard shim.
110
+ function copyViaTextarea(text, done) {
111
+ try {
112
+ const ta = document.createElement('textarea');
113
+ ta.value = text;
114
+ ta.style.position = 'fixed';
115
+ ta.style.opacity = '0';
116
+ document.body.appendChild(ta);
117
+ ta.focus();
118
+ ta.select();
119
+ const ok = document.execCommand('copy');
120
+ document.body.removeChild(ta);
121
+ done(ok);
122
+ } catch (_e) {
123
+ done(false);
124
+ }
125
+ }
126
+
94
127
  function quickstartNode(quickstart) {
95
128
  if (!quickstart || !Array.isArray(quickstart.lines) || !quickstart.lines.length) return null;
96
129
  // A single .cli wrapper holding .ds-cli-row/.ds-cli-comment children, not
@@ -104,7 +137,19 @@ function quickstartNode(quickstart) {
104
137
  : h('div', { key: 'q' + i, class: 'ds-cli-row' },
105
138
  h('span', { class: 'prompt' }, '$'),
106
139
  h('span', { class: 'cmd' }, l.text)));
107
- return C.Panel({ title: quickstart.heading || 'quick start', children: h('div', { class: 'cli' }, ...lineNodes) });
140
+ // Copy the whole snippet (comment lines included) as one paste-ready block,
141
+ // not just the command lines -- a reader copying "the quick start" expects
142
+ // what they see, not a silently filtered subset.
143
+ const fullText = quickstart.lines.map((l) => l.text).join('\\n');
144
+ const copyBtn = h('button', {
145
+ type: 'button', class: 'copy ds-quickstart-copy',
146
+ 'aria-label': 'copy quick start snippet',
147
+ onclick: (e) => copyQuickstart(fullText, e.currentTarget),
148
+ }, 'copy');
149
+ return C.Panel({
150
+ title: quickstart.heading || 'quick start',
151
+ children: h('div', { class: 'cli ds-quickstart-cli' }, h('div', { class: 'ds-cli-block' }, ...lineNodes), copyBtn),
152
+ });
108
153
  }
109
154
 
110
155
  function sideNode(sidebar) {
@@ -135,6 +180,21 @@ function __md(md) {
135
180
 
136
181
  const bodyNode = data.bodyHtml ? C.Section({ children: h('div', { class: 'page-body', innerHTML: data.bodyHtml }) }) : null;
137
182
 
183
+ // A real closing block: the page previously just stopped after its last
184
+ // panel, leaving nothing but the app-stage's own bottom padding as unexplained
185
+ // empty space. Reuses data already on the page (nav links, siteName, year) —
186
+ // no invented content.
187
+ function footerNode() {
188
+ const year = new Date().getFullYear();
189
+ const links = (data.navItems || []).filter(([, href]) => /^https?:/.test(String(href)));
190
+ return h('footer', { class: 'ds-page-footer' },
191
+ h('span', { class: 'ds-page-footer-copy' }, String(year) + ' · ' + data.siteName),
192
+ links.length ? h('nav', { class: 'ds-page-footer-links', 'aria-label': 'footer' },
193
+ ...links.map(([label, href], i) => h('a', { key: i, href }, String(label).replace(' ->', '')))
194
+ ) : null,
195
+ );
196
+ }
197
+
138
198
  function tierNode(tier, children) {
139
199
  const kids = children.filter(Boolean);
140
200
  if (!kids.length) return null;
@@ -172,6 +232,7 @@ const mainChildren = [
172
232
  ...tierNodes,
173
233
  ...leftoverPanels,
174
234
  bodyNode,
235
+ footerNode(),
175
236
  ].filter(Boolean);
176
237
 
177
238
  mount(document.getElementById('app'), () => C.AppShell({
@@ -51,7 +51,13 @@ export const PAGE_INLINE_STYLES = `
51
51
  a.row:hover .ds-row-arrow { opacity: 1 }
52
52
  /* hero stat strip — all badges as a wrapping inline rhythm, not one empty panel */
53
53
  .ds-hero-stats { display: flex; flex-wrap: wrap; gap: var(--space-3, 16px) var(--space-5, 32px); margin-top: var(--space-2, 8px) }
54
- .ds-hero-stat { display: flex; align-items: baseline; gap: var(--space-2, 8px) }
54
+ /* hero-content.css's .ds-hero-stat is styled for a vertical stacked list
55
+ (border-bottom row divider, meant to read top-to-bottom); this page renders
56
+ badges in a horizontal row instead (.ds-hero-stats above), where that same
57
+ border reads as an underlined tab strip -- an affordance for a set of
58
+ plain, non-interactive metadata chips. Reset both inherited properties so a
59
+ row of badges never borrows tab-bar chrome it did not opt into. */
60
+ .ds-hero-stat { display: flex; align-items: baseline; gap: var(--space-2, 8px); border-bottom: none !important; padding-bottom: 0 !important }
55
61
  .ds-hero-stat-n { font-family: var(--ff-body); font-weight: 700; font-size: var(--fs-lg, 18px); color: var(--fg) }
56
62
  .ds-hero-stat-l { font-size: var(--fs-sm, 15px); color: var(--fg-3) }
57
63
  /* accent sits on its own line, muted, so it reads as a distinct aside instead
@@ -68,4 +74,20 @@ a.row:hover .ds-row-arrow { opacity: 1 }
68
74
  .ds-feature-title { font-weight: 600; font-size: var(--fs-lg, 18px); color: var(--fg) }
69
75
  .ds-feature-desc { font-size: var(--fs-sm, 15px); color: var(--fg-2); line-height: 1.5; overflow-wrap: anywhere }
70
76
  .ds-feature-benefit { font-style: italic; font-size: var(--fs-sm, 15px); color: var(--fg-3); margin-top: var(--space-1, 4px) }
77
+ /* Real closing content instead of the page simply stopping after its last
78
+ panel — a tonal divider line, a copyright string, and the same external
79
+ links the topbar already carries (footer.js reuses data.navItems, so this
80
+ is never invented content). Sits inside .app-stage's own grid gap like
81
+ every other block, no extra margin needed. */
82
+ .ds-page-footer {
83
+ display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
84
+ gap: var(--space-3, 16px);
85
+ padding-top: var(--space-5, 32px);
86
+ margin-top: var(--space-3, 16px);
87
+ border-top: 1px solid var(--rule, var(--bg-3));
88
+ color: var(--fg-3); font-size: var(--fs-sm, 15px);
89
+ }
90
+ .ds-page-footer-links { display: flex; gap: var(--space-4, 24px) }
91
+ .ds-page-footer-links a { color: var(--fg-3) }
92
+ .ds-page-footer-links a:hover { color: var(--fg) }
71
93
  `.trim();
@@ -1203,6 +1203,8 @@ export interface GitDiffViewProps {
1203
1203
  /** @default '' */
1204
1204
  diff?: string;
1205
1205
  filename?: any;
1206
+ /** @default false */
1207
+ binary?: boolean;
1206
1208
  }
1207
1209
  export declare function GitDiffView(props?: GitDiffViewProps): VNode;
1208
1210