anentrypoint-design 0.0.477 → 0.0.480
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 +101 -0
- package/chat.css +4 -4
- package/colors_and_type.css +33 -3
- package/community.css +22 -8
- package/dist/247420.css +324 -49
- package/dist/247420.js +123 -55
- package/dist/index.html +109 -32
- package/package.json +1 -1
- package/src/community-app.js +4 -2
- package/src/components/community/navigation.js +2 -2
- package/src/components/community/presence.js +10 -4
- package/src/components/community/views.js +8 -2
- package/src/components/content/avatar.js +30 -0
- package/src/components/content/hero.js +9 -2
- package/src/components/content.js +2 -2
- package/src/components/freddie/pages-missing.js +1 -1
- package/src/components/overlay-primitives/emoji-picker.js +15 -3
- package/src/components/shell/app-shell.js +15 -4
- package/src/components/shell/workspace-shell.js +1 -1
- package/src/components/theme-toggle.js +8 -3
- package/src/copy-code.js +103 -0
- package/src/css/app-shell/base.css +18 -0
- package/src/css/app-shell/catalog-theme.css +20 -2
- package/src/css/app-shell/chat-polish.css +12 -0
- package/src/css/app-shell/collab.css +5 -0
- package/src/css/app-shell/data-density.css +4 -0
- package/src/css/app-shell/files.css +7 -1
- package/src/css/app-shell/hero-content.css +14 -1
- package/src/css/app-shell/kits-appended.css +13 -0
- package/src/css/app-shell/loading-alerts.css +14 -12
- package/src/css/app-shell/otp-input.css +2 -2
- package/src/css/app-shell/panel-row.css +15 -0
- package/src/css/app-shell/plugins-config.css +3 -0
- package/src/css/app-shell/primitives.css +13 -4
- package/src/css/app-shell/responsive.css +13 -0
- package/src/css/app-shell/responsive2-workspace.css +4 -1
- package/src/css/app-shell/row-print.css +34 -0
- package/src/css/app-shell/states-interactions.css +62 -4
- package/src/css/app-shell/topbar.css +1 -0
- package/src/kits/os/app-panes.css +3 -2
- package/src/kits/os/icons.js +16 -0
- package/src/kits/os/shell-geometry.js +2 -2
- package/src/kits/os/shell.js +24 -3
- package/src/kits/os/theme.css +272 -78
- package/src/kits/os/wm.css +19 -6
- package/src/kits/os/wm.js +38 -1
- package/src/kits/spoint/host-join-lobby.css +3 -3
- package/src/page-html/client-script.js +96 -28
- package/src/page-html.js +3 -2
- package/src/theme.js +6 -2
- package/src/web-components/ds-chat.js +9 -0
- package/src/web-components/freddie-chat.js +2 -2
- package/types/components.d.ts +6 -0
package/dist/index.html
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
<meta name="description" content="the visual paradigm. layered surfaces, monospace labels, loud content inside quiet chrome. consumed by every 247420 surface.">
|
|
9
9
|
<meta name="keywords" content="design-system, tokens, 247420, anentrypoint">
|
|
10
|
-
<meta name="author" content="247420
|
|
10
|
+
<meta name="author" content="247420 · a design system by AnEntrypoint">
|
|
11
11
|
<meta name="robots" content="index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1">
|
|
12
12
|
<meta name="generator" content="anentrypoint-design">
|
|
13
13
|
<link rel="canonical" href="https://anentrypoint.github.io/design">
|
|
@@ -116,14 +116,21 @@ a.row:hover .ds-row-arrow { opacity: 1 }
|
|
|
116
116
|
.ds-page-footer-links a { color: var(--fg-3) }
|
|
117
117
|
.ds-page-footer-links a:hover { color: var(--fg) }
|
|
118
118
|
</style>
|
|
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>
|
|
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, 25 previews, one deck, and a full desktop-OS shell — plus a web component and an SDK, all tokens-only, 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":[],"bodyHtml":"","sidebar":{"sections":[{"group":"open","items":[{"glyph":"+","label":"open","href":"./ui_kits/homepage/"}]},{"group":"bins","items":[{"label":"everything","href":"#all","active":true,"count":45},{"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":5}]},{"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"},{"label":"MIT license","href":"https://github.com/AnEntrypoint/design/blob/main/LICENSE"}]}]},"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 ->","href":"./ui_kits/file_browser/"},{"code":"fb","title":"FileRow","sub":"rail icon name meta","meta":"open ->","href":"./preview/file-row.html"},{"code":"fb","title":"FileGrid","sub":"stack of rows in a panel","meta":"open ->","href":"./preview/file-grid.html"},{"code":"fb","title":"DropZone","sub":"tonal upload target","meta":"open ->","href":"./preview/dropzone.html"},{"code":"fb","title":"FileViewer","sub":"modal preview keyed head","meta":"open ->","href":"./preview/file-viewer.html"},{"code":"fb","title":"FileToolbar","sub":"actions breadcrumb upload","meta":"open ->","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":5,"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"},{"code":"md","title":"component props","sub":"generated prop reference","meta":"open ->","href":"https://github.com/AnEntrypoint/design/blob/main/docs/component-props.md"},{"code":"md","title":"changelog","sub":"release history","meta":"open ->","href":"https://github.com/AnEntrypoint/design/blob/main/CHANGELOG.md"}]},{"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"],"seoAuthor":"247420 · a design system by AnEntrypoint"}</script>
|
|
120
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>
|
|
121
121
|
</head>
|
|
122
122
|
<body>
|
|
123
123
|
<div id="app"></div>
|
|
124
124
|
<script type="module">
|
|
125
125
|
import { mount, components as C, h } from 'anentrypoint-design';
|
|
126
|
-
|
|
126
|
+
let data;
|
|
127
|
+
try {
|
|
128
|
+
data = JSON.parse(document.getElementById('__site__').textContent);
|
|
129
|
+
} catch (e) {
|
|
130
|
+
console.error('[page-html] failed to parse #__site__ bootstrap data:', e);
|
|
131
|
+
document.getElementById('app').textContent = 'This page failed to load correctly. Please refresh, or contact support if the problem persists.';
|
|
132
|
+
throw e;
|
|
133
|
+
}
|
|
127
134
|
|
|
128
135
|
function heroNode(hero) {
|
|
129
136
|
if (!hero) return null;
|
|
@@ -150,11 +157,13 @@ function heroNode(hero) {
|
|
|
150
157
|
);
|
|
151
158
|
}
|
|
152
159
|
|
|
160
|
+
function __esc(s) { return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); }
|
|
161
|
+
|
|
153
162
|
function sectionNode(sec, idx) {
|
|
154
163
|
const features = sec.features || sec.items || [];
|
|
155
164
|
const rows = features.map((f, i) => {
|
|
156
165
|
const kids = [h('div', { key: 't', class: 'ds-feature-title' }, String(f.name || ''))];
|
|
157
|
-
if (f.desc) kids.push(h('div', { key: 'd', class: 'ds-feature-desc', innerHTML: String(f.desc).replace(/`([^`]+)`/g, '<code>$1</code>') }));
|
|
166
|
+
if (f.desc) kids.push(h('div', { key: 'd', class: 'ds-feature-desc', innerHTML: __esc(String(f.desc)).replace(/`([^`]+)`/g, '<code>$1</code>') }));
|
|
158
167
|
if (f.benefit) kids.push(h('div', { key: 'b', class: 'ds-feature-benefit' }, String(f.benefit)));
|
|
159
168
|
return h('div', { key: i, class: 'ds-feature' }, ...kids);
|
|
160
169
|
});
|
|
@@ -185,9 +194,64 @@ function examplesNode(examples) {
|
|
|
185
194
|
});
|
|
186
195
|
}
|
|
187
196
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
197
|
+
// Category glyph per homepage panel id — the same line-icon vocabulary
|
|
198
|
+
// icons.js already provides, so kits/previews/decks/docs read as distinct
|
|
199
|
+
// categories at a glance instead of by text label alone. Falls back to no
|
|
200
|
+
// icon for panel ids outside this table (e.g. one-off feature panels).
|
|
201
|
+
const PANEL_ICON = {
|
|
202
|
+
kits: 'grid',
|
|
203
|
+
file_browser: 'folder',
|
|
204
|
+
desktop_os: 'square',
|
|
205
|
+
web_components: 'page',
|
|
206
|
+
api_exports: 'link',
|
|
207
|
+
decks: 'screen',
|
|
208
|
+
docs: 'file-text',
|
|
209
|
+
previews: 'eye',
|
|
210
|
+
features: 'info',
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
// Filter state for the kits search box above the "ui kits" panel. Reuses
|
|
214
|
+
// ui_kits/search's own query-bar pattern (plain module-level state object,
|
|
215
|
+
// mutated on input, driving a re-render) rather than inventing a second
|
|
216
|
+
// filtering mechanism -- this homepage already ships that kit, so the
|
|
217
|
+
// homepage's own kit listing gets the same affordance instead of being the
|
|
218
|
+
// one surface on the site without a way to search kits by name.
|
|
219
|
+
const kitsFilterState = { q: '' };
|
|
220
|
+
|
|
221
|
+
function panelNode(panel, idx, rerender) {
|
|
222
|
+
let items = Array.isArray(panel.items) ? panel.items : [];
|
|
223
|
+
const isKits = panel.id === 'kits';
|
|
224
|
+
const q = isKits ? kitsFilterState.q.trim().toLowerCase() : '';
|
|
225
|
+
if (isKits && q) {
|
|
226
|
+
items = items.filter((it) => {
|
|
227
|
+
const hay = (String(it.title || it.name || '') + ' ' + String(it.sub || it.desc || '')).toLowerCase();
|
|
228
|
+
return hay.includes(q);
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
const iconName = panel.id && PANEL_ICON[panel.id];
|
|
232
|
+
const titleText = panel.title || panel.name || '';
|
|
233
|
+
const titleNode = iconName && titleText
|
|
234
|
+
? h('span', { class: 'ds-panel-title-glyph' }, C.Icon(iconName, { size: 15 }), h('span', {}, titleText))
|
|
235
|
+
: titleText;
|
|
236
|
+
const filterInput = isKits && rerender ? h('div', { class: 'ds-kits-filter' },
|
|
237
|
+
h('input', {
|
|
238
|
+
type: 'search', class: 'input ds-kits-filter-input',
|
|
239
|
+
placeholder: 'filter kits by name or description…',
|
|
240
|
+
value: kitsFilterState.q,
|
|
241
|
+
'aria-label': 'filter ui kits',
|
|
242
|
+
oninput: (e) => { kitsFilterState.q = e.target.value; rerender(); },
|
|
243
|
+
})) : null;
|
|
244
|
+
if (!items.length) {
|
|
245
|
+
if (isKits && q) {
|
|
246
|
+
return h('div', { class: 'ds-kits-panel-wrap' }, filterInput,
|
|
247
|
+
C.Panel({ id: panel.id || null, title: titleNode, count: 0, children:
|
|
248
|
+
h('div', { class: 'ds-empty-state' },
|
|
249
|
+
h('div', { class: 'ds-empty-state-glyph' }, '( )'),
|
|
250
|
+
h('p', { class: 'ds-empty-state-msg' }, 'no kits match ', h('code', {}, '"' + kitsFilterState.q.trim() + '"')),
|
|
251
|
+
) }));
|
|
252
|
+
}
|
|
253
|
+
return filterInput ? h('div', { class: 'ds-kits-panel-wrap' }, filterInput) : null;
|
|
254
|
+
}
|
|
191
255
|
const rows = items.map((it, i) => {
|
|
192
256
|
const code = it.code == null ? '' : String(it.code).trim();
|
|
193
257
|
const kids = [];
|
|
@@ -197,7 +261,8 @@ function panelNode(panel, idx) {
|
|
|
197
261
|
kids.push(h('span', { key: 'a', class: 'ds-row-arrow' }, it.meta || '->'));
|
|
198
262
|
return h('a', { key: i, class: 'row', href: it.href || '#' }, ...kids);
|
|
199
263
|
});
|
|
200
|
-
|
|
264
|
+
const panelEl = C.Panel({ id: panel.id || null, title: titleNode, count: items.length, children: rows });
|
|
265
|
+
return filterInput ? h('div', { class: 'ds-kits-panel-wrap' }, filterInput, panelEl) : panelEl;
|
|
201
266
|
}
|
|
202
267
|
|
|
203
268
|
function marqueeNode(marquee) {
|
|
@@ -301,8 +366,14 @@ const bodyNode = data.bodyHtml ? C.Section({ children: h('div', { class: 'page-b
|
|
|
301
366
|
function footerNode() {
|
|
302
367
|
const year = new Date().getFullYear();
|
|
303
368
|
const links = (data.navItems || []).filter(([, href]) => /^https?:/.test(String(href)));
|
|
369
|
+
// Names the actual maintaining entity, not just the project's numeric
|
|
370
|
+
// brand. This is a personal/community project (MIT, copyright holder
|
|
371
|
+
// "AnEntrypoint" per LICENSE) with no separate legal org behind it, so the
|
|
372
|
+
// footer says that plainly instead of leaving "247420" to stand in for an
|
|
373
|
+
// entity it isn't.
|
|
374
|
+
const entity = (data.seoAuthor || '247420 · a design system by AnEntrypoint').toLowerCase();
|
|
304
375
|
return h('footer', { class: 'ds-page-footer' },
|
|
305
|
-
h('span', { class: 'ds-page-footer-copy' }, String(year) + ' · ' +
|
|
376
|
+
h('span', { class: 'ds-page-footer-copy' }, String(year) + ' · ' + entity),
|
|
306
377
|
links.length ? h('nav', { class: 'ds-page-footer-links', 'aria-label': 'footer' },
|
|
307
378
|
...links.map(([label, href], i) => h('a', { key: i, href }, String(label).replace(' ->', '')))
|
|
308
379
|
) : null,
|
|
@@ -320,40 +391,46 @@ function tierNode(tier, children) {
|
|
|
320
391
|
return h('section', { key: tier.key, class: 'ds-tier ds-tier-' + tier.key, id: tier.key, 'aria-labelledby': labelId }, head, ...kids);
|
|
321
392
|
}
|
|
322
393
|
|
|
323
|
-
const panelsById = new Map((data.panels || []).map((p) => [p.id || p.title || p.name || '', p]));
|
|
324
|
-
const takePanel = (id) => { const p = panelsById.get(id); if (p) panelsById.delete(id); return p ? panelNode(p) : null; };
|
|
325
|
-
|
|
326
394
|
const TIERS = [
|
|
327
395
|
{ key: 'open', label: 'open', lede: 'browse and try the system running.', ids: ['kits', 'previews', 'decks'], extra: () => [examplesNode(data.examples)] },
|
|
328
396
|
{ key: 'ships', label: 'ships', lede: 'what the package contains.', ids: ['file_browser', 'desktop_os', 'web_components', 'api_exports'], extra: () => [] },
|
|
329
397
|
{ key: 'read', label: 'read', lede: 'understand the rules behind it.', ids: ['docs', 'features'], extra: () => [quickstartNode(data.quickstart)] },
|
|
330
398
|
];
|
|
331
399
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
return
|
|
339
|
-
});
|
|
340
|
-
const leftoverPanels = [...panelsById.values()].map(panelNode);
|
|
400
|
+
// Builds the whole main-content tree fresh each render so the kits filter
|
|
401
|
+
// (module-level kitsFilterState) can drive a real re-render via mount()'s
|
|
402
|
+
// own returned render callback -- same reactive shape mountKit() gives every
|
|
403
|
+
// other kit on the site, applied here to the static-page renderer.
|
|
404
|
+
function buildMainChildren(rerender) {
|
|
405
|
+
const panelsById = new Map((data.panels || []).map((p) => [p.id || p.title || p.name || '', p]));
|
|
406
|
+
const takePanel = (id) => { const p = panelsById.get(id); if (p) panelsById.delete(id); return p ? panelNode(p, 0, rerender) : null; };
|
|
341
407
|
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
].
|
|
408
|
+
const tierNodes = TIERS.map((t) => {
|
|
409
|
+
const kids = [...t.ids.map(takePanel), ...t.extra()].filter(Boolean);
|
|
410
|
+
if (t.key === 'open' && kids.length) {
|
|
411
|
+
const lead = kids[0];
|
|
412
|
+
if (lead.props) lead.props.class = (lead.props.class || '') + ' ds-tier-lead';
|
|
413
|
+
}
|
|
414
|
+
return tierNode(t, kids);
|
|
415
|
+
});
|
|
416
|
+
const leftoverPanels = [...panelsById.values()].map((p) => panelNode(p, 0, rerender));
|
|
417
|
+
|
|
418
|
+
return [
|
|
419
|
+
heroNode(data.hero),
|
|
420
|
+
marqueeNode(data.marquee),
|
|
421
|
+
...data.sections.map(sectionNode),
|
|
422
|
+
...tierNodes,
|
|
423
|
+
...leftoverPanels,
|
|
424
|
+
bodyNode,
|
|
425
|
+
footerNode(),
|
|
426
|
+
].filter(Boolean);
|
|
427
|
+
}
|
|
351
428
|
|
|
352
|
-
mount(document.getElementById('app'), () => C.AppShell({
|
|
429
|
+
mount(document.getElementById('app'), (rerender) => C.AppShell({
|
|
353
430
|
topbar: C.Topbar({ brand: data.siteName, items: data.navItems, active: data.title }),
|
|
354
431
|
crumb: C.Crumb({ leaf: data.title }),
|
|
355
432
|
side: sideNode(data.sidebar),
|
|
356
|
-
main: h('div', { class: 'app-stage' }, ...
|
|
433
|
+
main: h('div', { class: 'app-stage' }, ...buildMainChildren(rerender)),
|
|
357
434
|
status: C.Status({
|
|
358
435
|
left: data.statusLeft || [data.siteName.toLowerCase(), data.slug],
|
|
359
436
|
right: data.statusRight || ['live'],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "anentrypoint-design",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.480",
|
|
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",
|
package/src/community-app.js
CHANGED
|
@@ -15,7 +15,9 @@
|
|
|
15
15
|
// audioQueueItems, audioQueueCurrentId, audioQueuePaused,
|
|
16
16
|
// showAuthModal, settingsOpen, voiceSettingsOpen, replyTarget,
|
|
17
17
|
// mobileMenuOpen, // drives the .ca-rail off-canvas drawer on narrow shells
|
|
18
|
-
// canManage
|
|
18
|
+
// canManage, // gates the rail's "+ create channel" affordance
|
|
19
|
+
// forumPosts, // ch.type==='forum': [{id,title,snippet,author,time,replyCount,tags?}]
|
|
20
|
+
// pageHtml, pageAuthor, pageUpdatedAt // ch.type==='page': PageView content + attribution
|
|
19
21
|
// }
|
|
20
22
|
// adapter.subscribe(cb) -> unsubscribe // cb fires when any snapshot field changes
|
|
21
23
|
// adapter.actions = {
|
|
@@ -223,7 +225,7 @@ export function mountCommunityApp(root, adapter = {}) {
|
|
|
223
225
|
const inVoiceChannel = ch.type === 'voice';
|
|
224
226
|
const bodyMain = inVoiceChannel ? voiceView(s)
|
|
225
227
|
: ch.type === 'forum' ? ForumView({ posts: s.forumPosts || [], onSelect: (id) => A.openThread && A.openThread(id), onNewPost: () => A.newForumPost && A.newForumPost() })
|
|
226
|
-
: ch.type === 'page' ? PageView({ title: ch.name, html: s.pageHtml || '', isAdmin: !!s.canManage, onEdit: () => A.editPage && A.editPage() })
|
|
228
|
+
: ch.type === 'page' ? PageView({ title: ch.name, html: s.pageHtml || '', author: s.pageAuthor || '', updatedAt: s.pageUpdatedAt || 0, isAdmin: !!s.canManage, onEdit: () => A.editPage && A.editPage() })
|
|
227
229
|
: chatView(s);
|
|
228
230
|
const showVoiceBanner = s.voiceConnected && s.voiceChannelName && !(inVoiceChannel && s.voiceChannelName === ch.name);
|
|
229
231
|
return h('div', { class: 'ca-app' },
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import * as webjsx from '../../../vendor/webjsx/index.js';
|
|
6
6
|
import { Icon } from '../shell.js';
|
|
7
|
-
import { Avatar, avatarInitial } from '../content.js';
|
|
7
|
+
import { Avatar, avatarInitial, avatarContrastFg } from '../content.js';
|
|
8
8
|
import { UserPanel } from './presence.js';
|
|
9
9
|
const h = webjsx.createElement;
|
|
10
10
|
|
|
@@ -97,7 +97,7 @@ export function ChannelItem({ id, name, type = 'text', active, voiceActive, voic
|
|
|
97
97
|
),
|
|
98
98
|
voiceActive && participants.length ? h('div', { class: 'cm-ch-voice-users' },
|
|
99
99
|
...participants.map(p => h('div', { class: 'cm-ch-voice-user' + (p.speaking ? ' speaking' : '') },
|
|
100
|
-
h('div', { class: 'cm-ch-voice-user-avatar', style: p.color ? `--avatar-bg:${p.color}` : null }, avatarInitial(p.identity)),
|
|
100
|
+
h('div', { class: 'cm-ch-voice-user-avatar', style: p.color ? (avatarContrastFg(p.color) ? `--avatar-bg:${p.color};--avatar-fg:${avatarContrastFg(p.color)}` : `--avatar-bg:${p.color}`) : null }, avatarInitial(p.identity)),
|
|
101
101
|
h('span', { class: 'cm-ch-voice-user-name' }, p.identity)
|
|
102
102
|
))
|
|
103
103
|
) : null
|
|
@@ -4,13 +4,19 @@
|
|
|
4
4
|
|
|
5
5
|
import * as webjsx from '../../../vendor/webjsx/index.js';
|
|
6
6
|
import { Icon } from '../shell.js';
|
|
7
|
-
import { avatarInitial } from '../content.js';
|
|
7
|
+
import { avatarInitial, avatarContrastFg } from '../content.js';
|
|
8
8
|
const h = webjsx.createElement;
|
|
9
9
|
|
|
10
|
+
function avatarStyle(color) {
|
|
11
|
+
if (!color) return null;
|
|
12
|
+
const fg = avatarContrastFg(color);
|
|
13
|
+
return fg ? `--avatar-bg:${color};--avatar-fg:${fg}` : `--avatar-bg:${color}`;
|
|
14
|
+
}
|
|
15
|
+
|
|
10
16
|
export function VoiceUser({ identity, speaking, color } = {}) {
|
|
11
17
|
const initial = avatarInitial(identity);
|
|
12
18
|
return h('div', { class: 'cm-voice-user' + (speaking ? ' speaking' : '') },
|
|
13
|
-
h('div', { class: 'cm-voice-user-avatar', style: color
|
|
19
|
+
h('div', { class: 'cm-voice-user-avatar', style: avatarStyle(color) }, initial),
|
|
14
20
|
h('span', { class: 'cm-voice-user-name' }, identity)
|
|
15
21
|
);
|
|
16
22
|
}
|
|
@@ -29,7 +35,7 @@ export function UserPanel({ name, tag, color, muted, deafened, onMute, onDeafen,
|
|
|
29
35
|
}
|
|
30
36
|
};
|
|
31
37
|
return h('div', { class: 'cm-user-panel' },
|
|
32
|
-
h('div', { class: 'cm-user-avatar', style: color
|
|
38
|
+
h('div', { class: 'cm-user-avatar', style: avatarStyle(color) },
|
|
33
39
|
h('span', { class: 'cm-user-status-dot' }),
|
|
34
40
|
initial
|
|
35
41
|
),
|
|
@@ -48,7 +54,7 @@ export function UserPanel({ name, tag, color, muted, deafened, onMute, onDeafen,
|
|
|
48
54
|
export function MemberItem({ identity, name, color, status = 'online' } = {}) {
|
|
49
55
|
const initial = avatarInitial(name || identity);
|
|
50
56
|
return h('div', { class: 'cm-member-item' },
|
|
51
|
-
h('div', { class: 'cm-member-avatar', style: color
|
|
57
|
+
h('div', { class: 'cm-member-avatar', style: avatarStyle(color) },
|
|
52
58
|
h('span', { class: 'cm-member-status' + (status === 'online' ? ' online' : '') }),
|
|
53
59
|
initial
|
|
54
60
|
),
|
|
@@ -111,10 +111,16 @@ export function ForumView({ posts = [], onSearch, onSort, onSelect, onNewPost, l
|
|
|
111
111
|
);
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
export function PageView({ title = '', html = '', isAdmin = false, onEdit } = {}) {
|
|
114
|
+
export function PageView({ title = '', html = '', author = '', updatedAt = 0, isAdmin = false, onEdit } = {}) {
|
|
115
115
|
return h('div', { class: 'cm-page', role: 'document' },
|
|
116
116
|
h('div', { class: 'cm-page-head' },
|
|
117
|
-
h('
|
|
117
|
+
h('div', { class: 'cm-page-head-title' },
|
|
118
|
+
h('h1', { class: 'cm-page-title' }, title || ''),
|
|
119
|
+
(author || updatedAt) ? h('div', { class: 'cm-page-meta' },
|
|
120
|
+
author ? h('span', { class: 'cm-page-author' }, author) : null,
|
|
121
|
+
updatedAt ? h('span', { class: 'cm-page-time' }, fmtRelTime(updatedAt)) : null
|
|
122
|
+
) : null
|
|
123
|
+
),
|
|
118
124
|
isAdmin && onEdit ? h('button', { type: 'button', class: 'cm-page-edit', onclick: onEdit }, 'Edit') : null
|
|
119
125
|
),
|
|
120
126
|
h('div', {
|
|
@@ -16,6 +16,36 @@ export function avatarInitial(name, count = 1) {
|
|
|
16
16
|
return name ? String(name).trim().slice(0, count).toUpperCase() || '?' : '?';
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
// avatarContrastFg — picks black or white text against an arbitrary
|
|
20
|
+
// per-user hashed --avatar-bg color, via WCAG relative luminance, so
|
|
21
|
+
// initials clear 4.5:1 regardless of which hue the hash lands on (a
|
|
22
|
+
// single fixed --fg-2 token can't: some hashed hues are light enough
|
|
23
|
+
// that dark-on-dark or light-on-light both existed in the wild).
|
|
24
|
+
// Returns null for an unparsable color so callers can omit the
|
|
25
|
+
// inline style entirely and fall back to the CSS default.
|
|
26
|
+
export function avatarContrastFg(color) {
|
|
27
|
+
if (!color) return null;
|
|
28
|
+
let r, g, b;
|
|
29
|
+
const hex = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(String(color).trim());
|
|
30
|
+
if (hex) {
|
|
31
|
+
let h = hex[1];
|
|
32
|
+
if (h.length === 3) h = h.split('').map(c => c + c).join('');
|
|
33
|
+
r = parseInt(h.slice(0, 2), 16); g = parseInt(h.slice(2, 4), 16); b = parseInt(h.slice(4, 6), 16);
|
|
34
|
+
} else {
|
|
35
|
+
const rgb = /^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/i.exec(String(color).trim());
|
|
36
|
+
if (!rgb) return null;
|
|
37
|
+
r = +rgb[1]; g = +rgb[2]; b = +rgb[3];
|
|
38
|
+
}
|
|
39
|
+
const lin = (c) => { const v = c / 255; return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4); };
|
|
40
|
+
const L = 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b);
|
|
41
|
+
// Contrast against pure black/white; pick whichever side clears more
|
|
42
|
+
// headroom (both landing >=4.5:1 is common, but some hues only clear
|
|
43
|
+
// one side, so max-headroom is the right tiebreak, not a 0.5 cutoff).
|
|
44
|
+
const contrastWhite = 1.05 / (L + 0.05);
|
|
45
|
+
const contrastBlack = (L + 0.05) / 0.05;
|
|
46
|
+
return contrastWhite >= contrastBlack ? '#fff' : '#000';
|
|
47
|
+
}
|
|
48
|
+
|
|
19
49
|
// Avatar — the single letter-fallback/image avatar primitive. `initialsCount`
|
|
20
50
|
// (default 1) controls how many leading characters of `name` become the
|
|
21
51
|
// fallback letters when no `src`/`fallback` is given (community.js's
|
|
@@ -99,13 +99,20 @@ export function Marquee({ items = [], sep = '/' }) {
|
|
|
99
99
|
if (!items.length) return null;
|
|
100
100
|
// Two identical runs make the -50% translate loop seamless. Each text and
|
|
101
101
|
// separator is a keyed span so webjsx applyDiff never sees a primitive
|
|
102
|
-
// sibling beside a keyed VElement.
|
|
102
|
+
// sibling beside a keyed VElement. Run 'a' is the real, assistive-tech-
|
|
103
|
+
// visible content; run 'b' is a purely visual duplicate for the seamless
|
|
104
|
+
// loop and must not be exposed to screen readers as doubled text, so it
|
|
105
|
+
// is wrapped in its own aria-hidden container (standard seamless-marquee
|
|
106
|
+
// technique).
|
|
103
107
|
const run = (runKey) => items.flatMap((it, i) => [
|
|
104
108
|
h('span', { class: 'ds-marquee-item', key: `${runKey}-i${i}` }, it),
|
|
105
109
|
h('span', { class: 'ds-marquee-sep', key: `${runKey}-s${i}`, 'aria-hidden': 'true' }, sep),
|
|
106
110
|
]);
|
|
107
111
|
return h('div', { class: 'ds-marquee', role: 'marquee' },
|
|
108
|
-
h('div', { class: 'ds-marquee-track' },
|
|
112
|
+
h('div', { class: 'ds-marquee-track' },
|
|
113
|
+
h('span', { class: 'ds-marquee-run ds-marquee-run-a' }, ...run('a')),
|
|
114
|
+
h('span', { class: 'ds-marquee-run ds-marquee-run-b', 'aria-hidden': 'true' }, ...run('b')),
|
|
115
|
+
)
|
|
109
116
|
);
|
|
110
117
|
}
|
|
111
118
|
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// submodule under ./content/, and the public export surface here is unchanged
|
|
7
7
|
// — no consumer import needs to move.
|
|
8
8
|
|
|
9
|
-
import { avatarInitial, Avatar } from './content/avatar.js';
|
|
9
|
+
import { avatarInitial, avatarContrastFg, Avatar } from './content/avatar.js';
|
|
10
10
|
import { Row, RowLink } from './content/row.js';
|
|
11
11
|
import { Panel, Card, PanelFromItems, Section, Receipt, Changelog } from './content/panel.js';
|
|
12
12
|
import { Hero, HeroFromPageData, Marquee, Manifesto, PageHeader } from './content/hero.js';
|
|
@@ -20,7 +20,7 @@ import { Spinner, Skeleton, Alert, FilterPills } from './content/feedback.js';
|
|
|
20
20
|
import { HomeView, ProjectView } from './content/views.js';
|
|
21
21
|
|
|
22
22
|
export {
|
|
23
|
-
avatarInitial, Avatar,
|
|
23
|
+
avatarInitial, avatarContrastFg, Avatar,
|
|
24
24
|
Row, RowLink,
|
|
25
25
|
Panel, Card, PanelFromItems, Section, Receipt, Changelog,
|
|
26
26
|
Hero, HeroFromPageData, Marquee, Manifesto, PageHeader,
|
|
@@ -90,7 +90,7 @@ export const themePage = makePage((ctx) => {
|
|
|
90
90
|
return [
|
|
91
91
|
PageHeader({ title: 'theme', lede: 'theme preference' }),
|
|
92
92
|
section('current', Table({ headers: ['setting', 'value'], rows: [
|
|
93
|
-
['theme', (typeof window !== 'undefined' && document.
|
|
93
|
+
['theme', (typeof window !== 'undefined' && document.documentElement.getAttribute('data-theme')) || 'github-dark'],
|
|
94
94
|
['density', (typeof window !== 'undefined' && document.documentElement.getAttribute('data-density')) || 'compact'],
|
|
95
95
|
['accent', (typeof window !== 'undefined' && document.documentElement.getAttribute('data-accent')) || 'default'],
|
|
96
96
|
] })),
|
|
@@ -44,12 +44,18 @@ const ALL_EMOJI = EMOJI_CATEGORIES.flatMap((c) => c.emoji);
|
|
|
44
44
|
export function EmojiPicker({ open, anchorX = 0, anchorY = 0, onSelect, onClose, query = '' } = {}) {
|
|
45
45
|
if (!open) return null;
|
|
46
46
|
let cat = EMOJI_CATEGORIES[0].id;
|
|
47
|
-
let rootEl = null, gridEl = null;
|
|
47
|
+
let rootEl = null, gridEl = null, searchEl = null;
|
|
48
|
+
// Internal search state, seeded from the `query` prop so a consumer that
|
|
49
|
+
// already knows the typed ':smile' trigger text (e.g. a composer keydown
|
|
50
|
+
// handler) can still pre-fill it — but typing in the picker's own input
|
|
51
|
+
// (added here since no consumer previously had anywhere to route text
|
|
52
|
+
// into `query`) is the primary path now.
|
|
53
|
+
let search = query || '';
|
|
48
54
|
const close = () => onClose && onClose();
|
|
49
55
|
|
|
50
56
|
const renderGrid = () => {
|
|
51
57
|
if (!gridEl) return;
|
|
52
|
-
const q = (
|
|
58
|
+
const q = (search || '').trim().toLowerCase();
|
|
53
59
|
const cells = q
|
|
54
60
|
? ALL_EMOJI.filter(([, name]) => name.toLowerCase().includes(q))
|
|
55
61
|
: (EMOJI_CATEGORIES.find(x => x.id === cat) || EMOJI_CATEGORIES[0]).emoji;
|
|
@@ -82,7 +88,13 @@ export function EmojiPicker({ open, anchorX = 0, anchorY = 0, onSelect, onClose,
|
|
|
82
88
|
el._ovEmojiCleanup = _anchoredOverlayLifecycle(el, { anchorX, anchorY, fallbackW: 260, fallbackH: 240, close });
|
|
83
89
|
},
|
|
84
90
|
},
|
|
85
|
-
|
|
91
|
+
h('input', {
|
|
92
|
+
type: 'search', class: 'ov-emoji-search', placeholder: 'Search emoji…',
|
|
93
|
+
'aria-label': 'search emoji', value: search,
|
|
94
|
+
ref: (el) => { searchEl = el; },
|
|
95
|
+
oninput: (e) => { search = e.target.value; renderGrid(); },
|
|
96
|
+
}),
|
|
97
|
+
(search || '').trim() ? null : h('div', { class: 'ov-emoji-tabs', role: 'tablist' },
|
|
86
98
|
...EMOJI_CATEGORIES.map((c) => h('button', {
|
|
87
99
|
type: 'button', class: 'ov-emoji-tab', role: 'tab',
|
|
88
100
|
'aria-selected': c.id === cat ? 'true' : 'false',
|
|
@@ -7,9 +7,14 @@ import * as webjsx from '../../../vendor/webjsx/index.js';
|
|
|
7
7
|
import { trapTab } from '../overlay-primitives.js';
|
|
8
8
|
import { Brand, Glyph } from './atoms.js';
|
|
9
9
|
import { Icon } from './icons.js';
|
|
10
|
+
import { ThemeToggle } from '../theme-toggle.js';
|
|
10
11
|
const h = webjsx.createElement;
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
// `themeToggle` defaults on: every Topbar consumer (homepage included) gets a
|
|
14
|
+
// real, working light/dark/auto control in the shell chrome instead of each
|
|
15
|
+
// call site having to remember to wire ThemeToggle() in by hand. Pass
|
|
16
|
+
// `themeToggle: false` to opt a shell out (e.g. a kit that renders its own).
|
|
17
|
+
export function Topbar({ brand = '247420', leaf = '', items = [], active = '', onNav, search, themeToggle = true } = {}) {
|
|
13
18
|
return h('header', { class: 'app-topbar', role: 'banner' },
|
|
14
19
|
Brand({ name: brand, leaf }),
|
|
15
20
|
search ? h('label', { class: 'app-search' },
|
|
@@ -50,7 +55,8 @@ export function Topbar({ brand = '247420', leaf = '', items = [], active = '', o
|
|
|
50
55
|
}
|
|
51
56
|
}
|
|
52
57
|
}, label);
|
|
53
|
-
}))
|
|
58
|
+
})),
|
|
59
|
+
themeToggle ? h('div', { class: 'app-topbar-theme' }, ThemeToggle({ compact: true })) : null
|
|
54
60
|
);
|
|
55
61
|
}
|
|
56
62
|
|
|
@@ -208,9 +214,14 @@ export function AppShell({ topbar, crumb, side, main, status, narrow } = {}) {
|
|
|
208
214
|
// brand + nav (topbar) and breadcrumb + right slot (crumb) share a single
|
|
209
215
|
// band so the chrome reads as one bar, not two. Either prop alone still
|
|
210
216
|
// renders on its own (consumers that pass only a topbar are unaffected).
|
|
217
|
+
// Landmark wrapper: topbar/crumb are opaque children with no semantics of
|
|
218
|
+
// their own, so screen-reader users landmark-navigating a page saw no
|
|
219
|
+
// "banner"/"navigation" region at all above <main>. <header role="banner">
|
|
220
|
+
// names the brand+nav band; the app-side-shell aside below already exists
|
|
221
|
+
// as a real <aside>, so this closes the missing landmark for the top band.
|
|
211
222
|
const chrome = (topbar && crumb)
|
|
212
|
-
? h('
|
|
213
|
-
: (topbar || crumb || null
|
|
223
|
+
? h('header', { class: 'app-chrome', role: 'banner' }, topbar, crumb)
|
|
224
|
+
: (topbar || crumb) ? h('header', { class: 'app-chrome', role: 'banner' }, topbar || crumb) : null;
|
|
214
225
|
return h('div', { class: 'app', ref: syncAppSide },
|
|
215
226
|
h('a', { href: '#app-main', class: 'skip-link' }, 'skip to main content'),
|
|
216
227
|
hasSide ? h('button', {
|
|
@@ -173,7 +173,7 @@ export function WorkspaceRail({ brand = '247420', action, items = [], footer } =
|
|
|
173
173
|
it.icon ? Icon(it.icon) : h('span', { class: 'ws-rail-item-glyph', 'aria-hidden': 'true' }),
|
|
174
174
|
h('span', { class: 'ws-rail-item-label' }, it.label),
|
|
175
175
|
(it.count != null && it.count !== 0 && it.count !== '0')
|
|
176
|
-
? h('span', { class: 'ws-rail-item-count', 'aria-hidden': 'true' }, String(it.count))
|
|
176
|
+
? h('span', { class: 'ws-rail-item-count ds-badge ds-badge--sm', 'aria-hidden': 'true' }, String(it.count))
|
|
177
177
|
: null,
|
|
178
178
|
it.rail ? h('span', { class: 'ws-rail-item-flag tone-' + it.rail, 'aria-hidden': 'true' }) : null)))),
|
|
179
179
|
footer ? h('div', { class: 'ws-rail-foot' }, footer) : null,
|