@storyshelf/app 0.5.3
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/LICENSE +21 -0
- package/README.md +18 -0
- package/dist/app-types.d.mts +54 -0
- package/dist/asset-manifest.mjs +41 -0
- package/dist/asset-manifest.mjs.map +1 -0
- package/dist/asset-paths.mjs +23 -0
- package/dist/asset-paths.mjs.map +1 -0
- package/dist/assets/htmx.min.js +3304 -0
- package/dist/assets/mark.svg +1 -0
- package/dist/capture-setup.mjs +139 -0
- package/dist/capture-setup.mjs.map +1 -0
- package/dist/credential-keys.mjs +20 -0
- package/dist/credential-keys.mjs.map +1 -0
- package/dist/credentials.mjs +56 -0
- package/dist/credentials.mjs.map +1 -0
- package/dist/index.d.mts +18 -0
- package/dist/index.mjs +310 -0
- package/dist/index.mjs.map +1 -0
- package/dist/lifecycle.mjs +132 -0
- package/dist/lifecycle.mjs.map +1 -0
- package/dist/middleware/auth-gate.mjs +36 -0
- package/dist/middleware/auth-gate.mjs.map +1 -0
- package/dist/middleware/csrf.mjs +75 -0
- package/dist/middleware/csrf.mjs.map +1 -0
- package/dist/middleware/rate-limit.mjs +68 -0
- package/dist/middleware/rate-limit.mjs.map +1 -0
- package/dist/middleware/request-log.mjs +30 -0
- package/dist/middleware/request-log.mjs.map +1 -0
- package/dist/middleware/server-timing.mjs +39 -0
- package/dist/middleware/server-timing.mjs.map +1 -0
- package/dist/middleware/setup-gate.mjs +37 -0
- package/dist/middleware/setup-gate.mjs.map +1 -0
- package/dist/middleware/store-scope.mjs +33 -0
- package/dist/middleware/store-scope.mjs.map +1 -0
- package/dist/notify.d.mts +17 -0
- package/dist/notify.mjs +141 -0
- package/dist/notify.mjs.map +1 -0
- package/dist/pages/admin-system.mjs +243 -0
- package/dist/pages/admin-system.mjs.map +1 -0
- package/dist/pages/build-detail.mjs +392 -0
- package/dist/pages/build-detail.mjs.map +1 -0
- package/dist/pages/build-diff-actions.mjs +162 -0
- package/dist/pages/build-diff-actions.mjs.map +1 -0
- package/dist/pages/build-diff-comments.mjs +104 -0
- package/dist/pages/build-diff-comments.mjs.map +1 -0
- package/dist/pages/build-diff-header.mjs +117 -0
- package/dist/pages/build-diff-header.mjs.map +1 -0
- package/dist/pages/build-diff-model.mjs +48 -0
- package/dist/pages/build-diff-model.mjs.map +1 -0
- package/dist/pages/build-diff-nav.mjs +122 -0
- package/dist/pages/build-diff-nav.mjs.map +1 -0
- package/dist/pages/build-diff-shortcuts.mjs +78 -0
- package/dist/pages/build-diff-shortcuts.mjs.map +1 -0
- package/dist/pages/build-diff-stale.mjs +70 -0
- package/dist/pages/build-diff-stale.mjs.map +1 -0
- package/dist/pages/build-diff-viewer.mjs +70 -0
- package/dist/pages/build-diff-viewer.mjs.map +1 -0
- package/dist/pages/build-diff.mjs +110 -0
- package/dist/pages/build-diff.mjs.map +1 -0
- package/dist/pages/compute-jobs.mjs +165 -0
- package/dist/pages/compute-jobs.mjs.map +1 -0
- package/dist/pages/invite.mjs +91 -0
- package/dist/pages/invite.mjs.map +1 -0
- package/dist/pages/label-detail.mjs +154 -0
- package/dist/pages/label-detail.mjs.map +1 -0
- package/dist/pages/library.mjs +343 -0
- package/dist/pages/library.mjs.map +1 -0
- package/dist/pages/login.mjs +117 -0
- package/dist/pages/login.mjs.map +1 -0
- package/dist/pages/passkey-ceremony.mjs +97 -0
- package/dist/pages/passkey-ceremony.mjs.map +1 -0
- package/dist/pages/profile.mjs +364 -0
- package/dist/pages/profile.mjs.map +1 -0
- package/dist/pages/project-builds-table.mjs +151 -0
- package/dist/pages/project-builds-table.mjs.map +1 -0
- package/dist/pages/project-builds.mjs +143 -0
- package/dist/pages/project-builds.mjs.map +1 -0
- package/dist/pages/project-create.mjs +90 -0
- package/dist/pages/project-create.mjs.map +1 -0
- package/dist/pages/project-settings.mjs +95 -0
- package/dist/pages/project-settings.mjs.map +1 -0
- package/dist/pages/projects-card.mjs +194 -0
- package/dist/pages/projects-card.mjs.map +1 -0
- package/dist/pages/projects.mjs +129 -0
- package/dist/pages/projects.mjs.map +1 -0
- package/dist/pages/root.mjs +98 -0
- package/dist/pages/root.mjs.map +1 -0
- package/dist/pages/settings-general.mjs +138 -0
- package/dist/pages/settings-general.mjs.map +1 -0
- package/dist/pages/settings-labels.mjs +107 -0
- package/dist/pages/settings-labels.mjs.map +1 -0
- package/dist/pages/settings-members.mjs +192 -0
- package/dist/pages/settings-members.mjs.map +1 -0
- package/dist/pages/settings-notifications.mjs +240 -0
- package/dist/pages/settings-notifications.mjs.map +1 -0
- package/dist/pages/settings-status.mjs +137 -0
- package/dist/pages/settings-status.mjs.map +1 -0
- package/dist/pages/settings-tests.mjs +111 -0
- package/dist/pages/settings-tests.mjs.map +1 -0
- package/dist/pages/settings-tokens.mjs +90 -0
- package/dist/pages/settings-tokens.mjs.map +1 -0
- package/dist/pages/settings-webhooks.mjs +104 -0
- package/dist/pages/settings-webhooks.mjs.map +1 -0
- package/dist/pages/storybook.mjs +116 -0
- package/dist/pages/storybook.mjs.map +1 -0
- package/dist/retention-timer.mjs +65 -0
- package/dist/retention-timer.mjs.map +1 -0
- package/dist/routers/admin-pages.mjs +48 -0
- package/dist/routers/admin-pages.mjs.map +1 -0
- package/dist/routers/admin.mjs +113 -0
- package/dist/routers/admin.mjs.map +1 -0
- package/dist/routers/assets.mjs +64 -0
- package/dist/routers/assets.mjs.map +1 -0
- package/dist/routers/attempts.mjs +96 -0
- package/dist/routers/attempts.mjs.map +1 -0
- package/dist/routers/auth-sync.mjs +64 -0
- package/dist/routers/auth-sync.mjs.map +1 -0
- package/dist/routers/auth.mjs +236 -0
- package/dist/routers/auth.mjs.map +1 -0
- package/dist/routers/baseline-guard.mjs +83 -0
- package/dist/routers/baseline-guard.mjs.map +1 -0
- package/dist/routers/builds.handlers.mjs +217 -0
- package/dist/routers/builds.handlers.mjs.map +1 -0
- package/dist/routers/builds.mjs +381 -0
- package/dist/routers/builds.mjs.map +1 -0
- package/dist/routers/comments.mjs +109 -0
- package/dist/routers/comments.mjs.map +1 -0
- package/dist/routers/flash.mjs +35 -0
- package/dist/routers/flash.mjs.map +1 -0
- package/dist/routers/health-report.mjs +159 -0
- package/dist/routers/health-report.mjs.map +1 -0
- package/dist/routers/health.mjs +46 -0
- package/dist/routers/health.mjs.map +1 -0
- package/dist/routers/helpers.mjs +110 -0
- package/dist/routers/helpers.mjs.map +1 -0
- package/dist/routers/htmx.mjs +24 -0
- package/dist/routers/htmx.mjs.map +1 -0
- package/dist/routers/labels.mjs +118 -0
- package/dist/routers/labels.mjs.map +1 -0
- package/dist/routers/media.mjs +53 -0
- package/dist/routers/media.mjs.map +1 -0
- package/dist/routers/members.mjs +177 -0
- package/dist/routers/members.mjs.map +1 -0
- package/dist/routers/notification-channels.mjs +190 -0
- package/dist/routers/notification-channels.mjs.map +1 -0
- package/dist/routers/notification-subscriptions.mjs +120 -0
- package/dist/routers/notification-subscriptions.mjs.map +1 -0
- package/dist/routers/profile.mjs +276 -0
- package/dist/routers/profile.mjs.map +1 -0
- package/dist/routers/projects.mjs +133 -0
- package/dist/routers/projects.mjs.map +1 -0
- package/dist/routers/review-next.mjs +47 -0
- package/dist/routers/review-next.mjs.map +1 -0
- package/dist/routers/schemas.mjs +337 -0
- package/dist/routers/schemas.mjs.map +1 -0
- package/dist/routers/settings-general.mjs +104 -0
- package/dist/routers/settings-general.mjs.map +1 -0
- package/dist/routers/settings-labels.mjs +52 -0
- package/dist/routers/settings-labels.mjs.map +1 -0
- package/dist/routers/settings-members.mjs +58 -0
- package/dist/routers/settings-members.mjs.map +1 -0
- package/dist/routers/settings-notifications.mjs +108 -0
- package/dist/routers/settings-notifications.mjs.map +1 -0
- package/dist/routers/settings-status.mjs +77 -0
- package/dist/routers/settings-status.mjs.map +1 -0
- package/dist/routers/settings-tokens.mjs +40 -0
- package/dist/routers/settings-tokens.mjs.map +1 -0
- package/dist/routers/settings-webhooks.mjs +67 -0
- package/dist/routers/settings-webhooks.mjs.map +1 -0
- package/dist/routers/settings.handlers.mjs +149 -0
- package/dist/routers/settings.handlers.mjs.map +1 -0
- package/dist/routers/settings.mjs +25 -0
- package/dist/routers/settings.mjs.map +1 -0
- package/dist/routers/snapshots.mjs +240 -0
- package/dist/routers/snapshots.mjs.map +1 -0
- package/dist/routers/status-configs.mjs +108 -0
- package/dist/routers/status-configs.mjs.map +1 -0
- package/dist/routers/storybook.mjs +171 -0
- package/dist/routers/storybook.mjs.map +1 -0
- package/dist/routers/tokens.mjs +91 -0
- package/dist/routers/tokens.mjs.map +1 -0
- package/dist/routers/ui.mjs +166 -0
- package/dist/routers/ui.mjs.map +1 -0
- package/dist/routers/webhooks.mjs +97 -0
- package/dist/routers/webhooks.mjs.map +1 -0
- package/dist/routers/well-known.mjs +43 -0
- package/dist/routers/well-known.mjs.map +1 -0
- package/dist/runtime.mjs +52 -0
- package/dist/runtime.mjs.map +1 -0
- package/dist/store.mjs +20 -0
- package/dist/store.mjs.map +1 -0
- package/dist/ui/avatar.mjs +76 -0
- package/dist/ui/avatar.mjs.map +1 -0
- package/dist/ui/buttons.mjs +205 -0
- package/dist/ui/buttons.mjs.map +1 -0
- package/dist/ui/code-block.mjs +47 -0
- package/dist/ui/code-block.mjs.map +1 -0
- package/dist/ui/compare-stage.mjs +344 -0
- package/dist/ui/compare-stage.mjs.map +1 -0
- package/dist/ui/csrf-field.mjs +20 -0
- package/dist/ui/csrf-field.mjs.map +1 -0
- package/dist/ui/css.mjs +36 -0
- package/dist/ui/css.mjs.map +1 -0
- package/dist/ui/document.mjs +130 -0
- package/dist/ui/document.mjs.map +1 -0
- package/dist/ui/dropdown.mjs +181 -0
- package/dist/ui/dropdown.mjs.map +1 -0
- package/dist/ui/feedback.mjs +341 -0
- package/dist/ui/feedback.mjs.map +1 -0
- package/dist/ui/filter-input.mjs +58 -0
- package/dist/ui/filter-input.mjs.map +1 -0
- package/dist/ui/forms.mjs +291 -0
- package/dist/ui/forms.mjs.map +1 -0
- package/dist/ui/icons/icon.mjs +53 -0
- package/dist/ui/icons/icon.mjs.map +1 -0
- package/dist/ui/icons/paths.mjs +61 -0
- package/dist/ui/icons/paths.mjs.map +1 -0
- package/dist/ui/icons/sprite.mjs +26 -0
- package/dist/ui/icons/sprite.mjs.map +1 -0
- package/dist/ui/kbd.mjs +33 -0
- package/dist/ui/kbd.mjs.map +1 -0
- package/dist/ui/layout.mjs +173 -0
- package/dist/ui/layout.mjs.map +1 -0
- package/dist/ui/progress.mjs +44 -0
- package/dist/ui/progress.mjs.map +1 -0
- package/dist/ui/segmented.mjs +77 -0
- package/dist/ui/segmented.mjs.map +1 -0
- package/dist/ui/shell/account.mjs +161 -0
- package/dist/ui/shell/account.mjs.map +1 -0
- package/dist/ui/shell/bare.mjs +69 -0
- package/dist/ui/shell/bare.mjs.map +1 -0
- package/dist/ui/shell/boot-script.mjs +46 -0
- package/dist/ui/shell/boot-script.mjs.map +1 -0
- package/dist/ui/shell/client-script.mjs +259 -0
- package/dist/ui/shell/client-script.mjs.map +1 -0
- package/dist/ui/shell/nav.mjs +111 -0
- package/dist/ui/shell/nav.mjs.map +1 -0
- package/dist/ui/shell/review-script.mjs +195 -0
- package/dist/ui/shell/review-script.mjs.map +1 -0
- package/dist/ui/shell/sidebar.mjs +323 -0
- package/dist/ui/shell/sidebar.mjs.map +1 -0
- package/dist/ui/shell/topbar.mjs +268 -0
- package/dist/ui/shell/topbar.mjs.map +1 -0
- package/dist/ui/stacks.mjs +82 -0
- package/dist/ui/stacks.mjs.map +1 -0
- package/dist/ui/styles/base.mjs +41 -0
- package/dist/ui/styles/base.mjs.map +1 -0
- package/dist/ui/styles/behavior.mjs +43 -0
- package/dist/ui/styles/behavior.mjs.map +1 -0
- package/dist/ui/styles/review-layout.mjs +347 -0
- package/dist/ui/styles/review-layout.mjs.map +1 -0
- package/dist/ui/styles/review.mjs +86 -0
- package/dist/ui/styles/review.mjs.map +1 -0
- package/dist/ui/styles/tokens.mjs +132 -0
- package/dist/ui/styles/tokens.mjs.map +1 -0
- package/dist/ui/styles.mjs +19 -0
- package/dist/ui/styles.mjs.map +1 -0
- package/dist/ui/subnav.mjs +91 -0
- package/dist/ui/subnav.mjs.map +1 -0
- package/dist/ui/table.mjs +73 -0
- package/dist/ui/table.mjs.map +1 -0
- package/dist/ui/theme.mjs +64 -0
- package/dist/ui/theme.mjs.map +1 -0
- package/dist/ui/thumbnail.mjs +51 -0
- package/dist/ui/thumbnail.mjs.map +1 -0
- package/dist/ui/time.mjs +50 -0
- package/dist/ui/time.mjs.map +1 -0
- package/package.json +92 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
//#region src/ui/styles/tokens.ts
|
|
5
|
+
/**
|
|
6
|
+
* CSS variable tokens: brand-driven colors (per theme), theme-independent
|
|
7
|
+
* scales (type, space, radius, motion), and tokens derived from the brand
|
|
8
|
+
* accent/status colors so customer themes need no extra fields.
|
|
9
|
+
*/
|
|
10
|
+
function tokenCss(light, dark) {
|
|
11
|
+
return [
|
|
12
|
+
scaleTokens(),
|
|
13
|
+
`:root {\n${colorVars(light, LIGHT_FALLBACK)}\n}`,
|
|
14
|
+
`[data-theme="dark"] {\n${colorVars(dark, DARK_FALLBACK)}\n}`,
|
|
15
|
+
`@media (prefers-color-scheme: dark) {\n[data-theme="system"] {\n${colorVars(dark, DARK_FALLBACK)}\n}\n}`
|
|
16
|
+
].join("\n");
|
|
17
|
+
}
|
|
18
|
+
const LIGHT_FALLBACK = {
|
|
19
|
+
accentContrast: "#fff",
|
|
20
|
+
muted: "#f4f4f5",
|
|
21
|
+
subtle: "#fafafa",
|
|
22
|
+
sidebar: "#ffffff",
|
|
23
|
+
shadow: "0 1px 2px rgba(0,0,0,.04)",
|
|
24
|
+
shadow2: "0 4px 12px rgba(0,0,0,.08)",
|
|
25
|
+
shadow3: "0 12px 32px rgba(0,0,0,.14)",
|
|
26
|
+
tint: "10%"
|
|
27
|
+
};
|
|
28
|
+
const DARK_FALLBACK = {
|
|
29
|
+
accentContrast: "#09090b",
|
|
30
|
+
muted: "#27272a",
|
|
31
|
+
subtle: "#18181b",
|
|
32
|
+
sidebar: "#111113",
|
|
33
|
+
shadow: "0 1px 2px rgba(0,0,0,.3)",
|
|
34
|
+
shadow2: "0 4px 12px rgba(0,0,0,.4)",
|
|
35
|
+
shadow3: "0 12px 32px rgba(0,0,0,.55)",
|
|
36
|
+
tint: "16%"
|
|
37
|
+
};
|
|
38
|
+
/** Theme-independent scales shared by every component. */
|
|
39
|
+
function scaleTokens() {
|
|
40
|
+
return `
|
|
41
|
+
:root {
|
|
42
|
+
--font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
43
|
+
--font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
44
|
+
--text-xs: 0.75rem;
|
|
45
|
+
--text-sm: 0.8125rem;
|
|
46
|
+
--text-base: 0.875rem;
|
|
47
|
+
--text-lg: 1rem;
|
|
48
|
+
--text-xl: 1.25rem;
|
|
49
|
+
--text-2xl: 1.5rem;
|
|
50
|
+
--leading-tight: 1.25;
|
|
51
|
+
--leading-normal: 1.5;
|
|
52
|
+
--space-1: 0.25rem;
|
|
53
|
+
--space-2: 0.5rem;
|
|
54
|
+
--space-3: 0.75rem;
|
|
55
|
+
--space-4: 1rem;
|
|
56
|
+
--space-5: 1.25rem;
|
|
57
|
+
--space-6: 1.5rem;
|
|
58
|
+
--space-8: 2rem;
|
|
59
|
+
--radius-pill: 999px;
|
|
60
|
+
--ease: cubic-bezier(0.2, 0, 0, 1);
|
|
61
|
+
--dur-fast: 120ms;
|
|
62
|
+
--dur-base: 200ms;
|
|
63
|
+
--sidebar-width: 220px;
|
|
64
|
+
--topbar-height: 52px;
|
|
65
|
+
}
|
|
66
|
+
@media (prefers-reduced-motion: reduce) {
|
|
67
|
+
:root { --dur-fast: 0ms; --dur-base: 0ms; }
|
|
68
|
+
}`;
|
|
69
|
+
}
|
|
70
|
+
/** Brand colors plus the tokens derived from them, for one theme. */
|
|
71
|
+
function colorVars(theme, fb) {
|
|
72
|
+
return [brandVars(theme, fb), derivedVars(fb)].join("\n");
|
|
73
|
+
}
|
|
74
|
+
function brandVars(theme, fb) {
|
|
75
|
+
const radius = theme.radius ?? "0.5rem";
|
|
76
|
+
const radiusSm = theme.radiusSm ?? "0.375rem";
|
|
77
|
+
return `
|
|
78
|
+
--accent: ${theme.accent};
|
|
79
|
+
--accent-contrast: ${theme.accentContrast ?? fb.accentContrast};
|
|
80
|
+
--ring: ${theme.ring ?? theme.accent};
|
|
81
|
+
--surface-base: ${theme.surface.base};
|
|
82
|
+
--surface-card: ${theme.surface.card};
|
|
83
|
+
--surface-muted: ${theme.surface.muted ?? fb.muted};
|
|
84
|
+
--surface-subtle: ${theme.surface.subtle ?? fb.subtle};
|
|
85
|
+
--text-primary: ${theme.text.primary};
|
|
86
|
+
--text-secondary: ${theme.text.secondary};
|
|
87
|
+
--text-muted: ${theme.text.muted ?? theme.text.secondary};
|
|
88
|
+
--border: ${theme.border};
|
|
89
|
+
--border-subtle: ${theme.borderSubtle ?? theme.border};
|
|
90
|
+
--radius: ${radius};
|
|
91
|
+
--radius-sm: ${radiusSm};
|
|
92
|
+
--radius-lg: calc(${radius} + 0.25rem);
|
|
93
|
+
--shadow: ${theme.shadow ?? fb.shadow};
|
|
94
|
+
--shadow-2: ${fb.shadow2};
|
|
95
|
+
--shadow-3: ${fb.shadow3};
|
|
96
|
+
--status-approved: ${theme.status.approved};
|
|
97
|
+
--status-new: ${theme.status.new};
|
|
98
|
+
--status-rejected: ${theme.status.rejected};
|
|
99
|
+
--sidebar-bg: ${theme.sidebarBg ?? fb.sidebar};`;
|
|
100
|
+
}
|
|
101
|
+
function mix(color, pct, into) {
|
|
102
|
+
return `color-mix(in srgb, var(${color}) ${pct}, var(${into}))`;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Tokens computed from the brand colors via `color-mix`. They resolve on
|
|
106
|
+
* the same element as the brand vars, so a customer accent flows through
|
|
107
|
+
* without extra config. The accent only ever tints; it never floods chrome.
|
|
108
|
+
*/
|
|
109
|
+
function derivedVars(fb) {
|
|
110
|
+
return `
|
|
111
|
+
--accent-subtle: ${mix("--accent", "8%", "--surface-card")};
|
|
112
|
+
--accent-wash: ${mix("--accent", "4%", "--surface-base")};
|
|
113
|
+
--accent-border: ${mix("--accent", "30%", "--border")};
|
|
114
|
+
--accent-fg: ${mix("--accent", "88%", "--text-primary")};
|
|
115
|
+
--status-info: var(--accent);
|
|
116
|
+
--status-approved-fg: ${mix("--status-approved", "60%", "--text-primary")};
|
|
117
|
+
--status-new-fg: ${mix("--status-new", "60%", "--text-primary")};
|
|
118
|
+
--status-rejected-fg: ${mix("--status-rejected", "60%", "--text-primary")};
|
|
119
|
+
--status-info-fg: var(--accent-fg);
|
|
120
|
+
--status-approved-bg: ${mix("--status-approved", fb.tint, "--surface-card")};
|
|
121
|
+
--status-new-bg: ${mix("--status-new", fb.tint, "--surface-card")};
|
|
122
|
+
--status-rejected-bg: ${mix("--status-rejected", fb.tint, "--surface-card")};
|
|
123
|
+
--status-info-bg: ${mix("--status-info", fb.tint, "--surface-card")};
|
|
124
|
+
--status-approved-border: ${mix("--status-approved", "32%", "--border")};
|
|
125
|
+
--status-new-border: ${mix("--status-new", "32%", "--border")};
|
|
126
|
+
--status-rejected-border: ${mix("--status-rejected", "32%", "--border")};
|
|
127
|
+
--status-info-border: ${mix("--status-info", "32%", "--border")};`;
|
|
128
|
+
}
|
|
129
|
+
//#endregion
|
|
130
|
+
export { tokenCss };
|
|
131
|
+
|
|
132
|
+
//# sourceMappingURL=tokens.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.mjs","names":[],"sources":["../../../src/ui/styles/tokens.ts"],"sourcesContent":["import type { BrandTheme } from \"../theme.ts\";\n\n/**\n * CSS variable tokens: brand-driven colors (per theme), theme-independent\n * scales (type, space, radius, motion), and tokens derived from the brand\n * accent/status colors so customer themes need no extra fields.\n */\nexport function tokenCss(light: BrandTheme, dark: BrandTheme): string {\n return [\n scaleTokens(),\n `:root {\\n${colorVars(light, LIGHT_FALLBACK)}\\n}`,\n `[data-theme=\"dark\"] {\\n${colorVars(dark, DARK_FALLBACK)}\\n}`,\n `@media (prefers-color-scheme: dark) {\\n[data-theme=\"system\"] {\\n${colorVars(dark, DARK_FALLBACK)}\\n}\\n}`,\n ].join(\"\\n\");\n}\n\ninterface Fallbacks {\n accentContrast: string;\n muted: string;\n subtle: string;\n sidebar: string;\n shadow: string;\n shadow2: string;\n shadow3: string;\n /** Share of the status color mixed into tinted backgrounds. */\n tint: string;\n}\n\nconst LIGHT_FALLBACK: Fallbacks = {\n accentContrast: \"#fff\",\n muted: \"#f4f4f5\",\n subtle: \"#fafafa\",\n sidebar: \"#ffffff\",\n shadow: \"0 1px 2px rgba(0,0,0,.04)\",\n shadow2: \"0 4px 12px rgba(0,0,0,.08)\",\n shadow3: \"0 12px 32px rgba(0,0,0,.14)\",\n tint: \"10%\",\n};\n\nconst DARK_FALLBACK: Fallbacks = {\n accentContrast: \"#09090b\",\n muted: \"#27272a\",\n subtle: \"#18181b\",\n sidebar: \"#111113\",\n shadow: \"0 1px 2px rgba(0,0,0,.3)\",\n shadow2: \"0 4px 12px rgba(0,0,0,.4)\",\n shadow3: \"0 12px 32px rgba(0,0,0,.55)\",\n tint: \"16%\",\n};\n\n/** Theme-independent scales shared by every component. */\nfunction scaleTokens(): string {\n return `\n :root {\n --font-sans: ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n --font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;\n --text-xs: 0.75rem;\n --text-sm: 0.8125rem;\n --text-base: 0.875rem;\n --text-lg: 1rem;\n --text-xl: 1.25rem;\n --text-2xl: 1.5rem;\n --leading-tight: 1.25;\n --leading-normal: 1.5;\n --space-1: 0.25rem;\n --space-2: 0.5rem;\n --space-3: 0.75rem;\n --space-4: 1rem;\n --space-5: 1.25rem;\n --space-6: 1.5rem;\n --space-8: 2rem;\n --radius-pill: 999px;\n --ease: cubic-bezier(0.2, 0, 0, 1);\n --dur-fast: 120ms;\n --dur-base: 200ms;\n --sidebar-width: 220px;\n --topbar-height: 52px;\n }\n @media (prefers-reduced-motion: reduce) {\n :root { --dur-fast: 0ms; --dur-base: 0ms; }\n }`;\n}\n\n/** Brand colors plus the tokens derived from them, for one theme. */\nfunction colorVars(theme: BrandTheme, fb: Fallbacks): string {\n return [brandVars(theme, fb), derivedVars(fb)].join(\"\\n\");\n}\n\nfunction brandVars(theme: BrandTheme, fb: Fallbacks): string {\n const radius = theme.radius ?? \"0.5rem\";\n const radiusSm = theme.radiusSm ?? \"0.375rem\";\n return `\n --accent: ${theme.accent};\n --accent-contrast: ${theme.accentContrast ?? fb.accentContrast};\n --ring: ${theme.ring ?? theme.accent};\n --surface-base: ${theme.surface.base};\n --surface-card: ${theme.surface.card};\n --surface-muted: ${theme.surface.muted ?? fb.muted};\n --surface-subtle: ${theme.surface.subtle ?? fb.subtle};\n --text-primary: ${theme.text.primary};\n --text-secondary: ${theme.text.secondary};\n --text-muted: ${theme.text.muted ?? theme.text.secondary};\n --border: ${theme.border};\n --border-subtle: ${theme.borderSubtle ?? theme.border};\n --radius: ${radius};\n --radius-sm: ${radiusSm};\n --radius-lg: calc(${radius} + 0.25rem);\n --shadow: ${theme.shadow ?? fb.shadow};\n --shadow-2: ${fb.shadow2};\n --shadow-3: ${fb.shadow3};\n --status-approved: ${theme.status.approved};\n --status-new: ${theme.status.new};\n --status-rejected: ${theme.status.rejected};\n --sidebar-bg: ${theme.sidebarBg ?? fb.sidebar};`;\n}\n\nfunction mix(color: string, pct: string, into: string): string {\n return `color-mix(in srgb, var(${color}) ${pct}, var(${into}))`;\n}\n\n/**\n * Tokens computed from the brand colors via `color-mix`. They resolve on\n * the same element as the brand vars, so a customer accent flows through\n * without extra config. The accent only ever tints; it never floods chrome.\n */\nfunction derivedVars(fb: Fallbacks): string {\n return `\n --accent-subtle: ${mix(\"--accent\", \"8%\", \"--surface-card\")};\n --accent-wash: ${mix(\"--accent\", \"4%\", \"--surface-base\")};\n --accent-border: ${mix(\"--accent\", \"30%\", \"--border\")};\n --accent-fg: ${mix(\"--accent\", \"88%\", \"--text-primary\")};\n --status-info: var(--accent);\n --status-approved-fg: ${mix(\"--status-approved\", \"60%\", \"--text-primary\")};\n --status-new-fg: ${mix(\"--status-new\", \"60%\", \"--text-primary\")};\n --status-rejected-fg: ${mix(\"--status-rejected\", \"60%\", \"--text-primary\")};\n --status-info-fg: var(--accent-fg);\n --status-approved-bg: ${mix(\"--status-approved\", fb.tint, \"--surface-card\")};\n --status-new-bg: ${mix(\"--status-new\", fb.tint, \"--surface-card\")};\n --status-rejected-bg: ${mix(\"--status-rejected\", fb.tint, \"--surface-card\")};\n --status-info-bg: ${mix(\"--status-info\", fb.tint, \"--surface-card\")};\n --status-approved-border: ${mix(\"--status-approved\", \"32%\", \"--border\")};\n --status-new-border: ${mix(\"--status-new\", \"32%\", \"--border\")};\n --status-rejected-border: ${mix(\"--status-rejected\", \"32%\", \"--border\")};\n --status-info-border: ${mix(\"--status-info\", \"32%\", \"--border\")};`;\n}\n"],"mappings":";;;;;;;;;AAOA,SAAgB,SAAS,OAAmB,MAA0B;CACpE,OAAO;EACL,YAAY;EACZ,YAAY,UAAU,OAAO,cAAc,EAAE;EAC7C,0BAA0B,UAAU,MAAM,aAAa,EAAE;EACzD,mEAAmE,UAAU,MAAM,aAAa,EAAE;CACpG,CAAC,CAAC,KAAK,IAAI;AACb;AAcA,MAAM,iBAA4B;CAChC,gBAAgB;CAChB,OAAO;CACP,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,SAAS;CACT,SAAS;CACT,MAAM;AACR;AAEA,MAAM,gBAA2B;CAC/B,gBAAgB;CAChB,OAAO;CACP,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,SAAS;CACT,SAAS;CACT,MAAM;AACR;;AAGA,SAAS,cAAsB;CAC7B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BT;;AAGA,SAAS,UAAU,OAAmB,IAAuB;CAC3D,OAAO,CAAC,UAAU,OAAO,EAAE,GAAG,YAAY,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI;AAC1D;AAEA,SAAS,UAAU,OAAmB,IAAuB;CAC3D,MAAM,SAAS,MAAM,UAAU;CAC/B,MAAM,WAAW,MAAM,YAAY;CACnC,OAAO;kBACS,MAAM,OAAO;2BACJ,MAAM,kBAAkB,GAAG,eAAe;gBACrD,MAAM,QAAQ,MAAM,OAAO;wBACnB,MAAM,QAAQ,KAAK;wBACnB,MAAM,QAAQ,KAAK;yBAClB,MAAM,QAAQ,SAAS,GAAG,MAAM;0BAC/B,MAAM,QAAQ,UAAU,GAAG,OAAO;wBACpC,MAAM,KAAK,QAAQ;0BACjB,MAAM,KAAK,UAAU;sBACzB,MAAM,KAAK,SAAS,MAAM,KAAK,UAAU;kBAC7C,MAAM,OAAO;yBACN,MAAM,gBAAgB,MAAM,OAAO;kBAC1C,OAAO;qBACJ,SAAS;0BACJ,OAAO;kBACf,MAAM,UAAU,GAAG,OAAO;oBACxB,GAAG,QAAQ;oBACX,GAAG,QAAQ;2BACJ,MAAM,OAAO,SAAS;sBAC3B,MAAM,OAAO,IAAI;2BACZ,MAAM,OAAO,SAAS;sBAC3B,MAAM,aAAa,GAAG,QAAQ;AACpD;AAEA,SAAS,IAAI,OAAe,KAAa,MAAsB;CAC7D,OAAO,0BAA0B,MAAM,IAAI,IAAI,QAAQ,KAAK;AAC9D;;;;;;AAOA,SAAS,YAAY,IAAuB;CAC1C,OAAO;yBACgB,IAAI,YAAY,MAAM,gBAAgB,EAAE;uBAC1C,IAAI,YAAY,MAAM,gBAAgB,EAAE;yBACtC,IAAI,YAAY,OAAO,UAAU,EAAE;qBACvC,IAAI,YAAY,OAAO,gBAAgB,EAAE;;8BAEhC,IAAI,qBAAqB,OAAO,gBAAgB,EAAE;yBACvD,IAAI,gBAAgB,OAAO,gBAAgB,EAAE;8BACxC,IAAI,qBAAqB,OAAO,gBAAgB,EAAE;;8BAElD,IAAI,qBAAqB,GAAG,MAAM,gBAAgB,EAAE;yBACzD,IAAI,gBAAgB,GAAG,MAAM,gBAAgB,EAAE;8BAC1C,IAAI,qBAAqB,GAAG,MAAM,gBAAgB,EAAE;0BACxD,IAAI,iBAAiB,GAAG,MAAM,gBAAgB,EAAE;kCACxC,IAAI,qBAAqB,OAAO,UAAU,EAAE;6BACjD,IAAI,gBAAgB,OAAO,UAAU,EAAE;kCAClC,IAAI,qBAAqB,OAAO,UAAU,EAAE;8BAChD,IAAI,iBAAiB,OAAO,UAAU,EAAE;AACtE"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { baseCss } from "./styles/base.mjs";
|
|
5
|
+
import { behaviorCss } from "./styles/behavior.mjs";
|
|
6
|
+
import { tokenCss } from "./styles/tokens.mjs";
|
|
7
|
+
//#region src/ui/styles.ts
|
|
8
|
+
/** Build the global CSS stylesheet from the light and dark brand themes. */
|
|
9
|
+
function baseStyle(light, dark) {
|
|
10
|
+
return [
|
|
11
|
+
tokenCss(light, dark),
|
|
12
|
+
baseCss(),
|
|
13
|
+
behaviorCss()
|
|
14
|
+
].join("\n");
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
export { baseStyle };
|
|
18
|
+
|
|
19
|
+
//# sourceMappingURL=styles.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"styles.mjs","names":[],"sources":["../../src/ui/styles.ts"],"sourcesContent":["import { baseCss } from \"./styles/base.ts\";\nimport { behaviorCss } from \"./styles/behavior.ts\";\nimport { tokenCss } from \"./styles/tokens.ts\";\nimport type { BrandTheme } from \"./theme.ts\";\n\n/** Build the global CSS stylesheet from the light and dark brand themes. */\nexport function baseStyle(light: BrandTheme, dark: BrandTheme): string {\n return [tokenCss(light, dark), baseCss(), behaviorCss()].join(\"\\n\");\n}\n"],"mappings":";;;;;;;;AAMA,SAAgB,UAAU,OAAmB,MAA0B;CACrE,OAAO;EAAC,SAAS,OAAO,IAAI;EAAG,QAAQ;EAAG,YAAY;CAAC,CAAC,CAAC,KAAK,IAAI;AACpE"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { css } from "./css.mjs";
|
|
5
|
+
import { Icon } from "./icons/icon.mjs";
|
|
6
|
+
import { jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
7
|
+
//#region src/ui/subnav.tsx
|
|
8
|
+
const layout = css`
|
|
9
|
+
/* subnav-layout */
|
|
10
|
+
display: grid;
|
|
11
|
+
grid-template-columns: 210px minmax(0, 1fr);
|
|
12
|
+
gap: var(--space-6);
|
|
13
|
+
align-items: start;
|
|
14
|
+
@media (max-width: 880px) {
|
|
15
|
+
& {
|
|
16
|
+
grid-template-columns: minmax(0, 1fr);
|
|
17
|
+
gap: var(--space-4);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
`;
|
|
21
|
+
const nav = css`
|
|
22
|
+
/* subnav */
|
|
23
|
+
position: sticky;
|
|
24
|
+
top: calc(var(--topbar-height) + var(--space-4));
|
|
25
|
+
display: flex;
|
|
26
|
+
flex-direction: column;
|
|
27
|
+
gap: 2px;
|
|
28
|
+
@media (max-width: 880px) {
|
|
29
|
+
& {
|
|
30
|
+
position: static;
|
|
31
|
+
flex-direction: row;
|
|
32
|
+
gap: var(--space-1);
|
|
33
|
+
padding-bottom: var(--space-2);
|
|
34
|
+
overflow-x: auto;
|
|
35
|
+
border-bottom: 1px solid var(--border);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
`;
|
|
39
|
+
const link = css`
|
|
40
|
+
/* subnav-link */
|
|
41
|
+
display: flex;
|
|
42
|
+
align-items: center;
|
|
43
|
+
gap: 0.6rem;
|
|
44
|
+
min-height: 36px;
|
|
45
|
+
padding: 0.4rem var(--space-3);
|
|
46
|
+
border-radius: var(--radius);
|
|
47
|
+
color: var(--text-secondary);
|
|
48
|
+
font-size: var(--text-base);
|
|
49
|
+
font-weight: 500;
|
|
50
|
+
text-decoration: none;
|
|
51
|
+
white-space: nowrap;
|
|
52
|
+
transition:
|
|
53
|
+
background var(--dur-fast) var(--ease),
|
|
54
|
+
color var(--dur-fast) var(--ease);
|
|
55
|
+
&:hover {
|
|
56
|
+
background: var(--surface-muted);
|
|
57
|
+
color: var(--text-primary);
|
|
58
|
+
text-decoration: none;
|
|
59
|
+
}
|
|
60
|
+
`;
|
|
61
|
+
const linkActive = css`
|
|
62
|
+
/* subnav-link-active */
|
|
63
|
+
${link}
|
|
64
|
+
background: var(--accent-subtle);
|
|
65
|
+
color: var(--accent-fg);
|
|
66
|
+
font-weight: 600;
|
|
67
|
+
&:hover {
|
|
68
|
+
background: var(--accent-subtle);
|
|
69
|
+
color: var(--accent-fg);
|
|
70
|
+
}
|
|
71
|
+
`;
|
|
72
|
+
/** Vertical section navigation beside content (a horizontal strip on phones). */
|
|
73
|
+
const SubNavLayout = ({ items, label, children }) => {
|
|
74
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
75
|
+
class: layout,
|
|
76
|
+
children: [/* @__PURE__ */ jsx("nav", {
|
|
77
|
+
class: nav,
|
|
78
|
+
"aria-label": label,
|
|
79
|
+
children: items.map((item) => /* @__PURE__ */ jsxs("a", {
|
|
80
|
+
class: item.active ? linkActive : link,
|
|
81
|
+
href: item.href,
|
|
82
|
+
"aria-current": item.active ? "page" : void 0,
|
|
83
|
+
children: [item.icon ? /* @__PURE__ */ jsx(Icon, { name: item.icon }) : null, item.label]
|
|
84
|
+
}, item.href))
|
|
85
|
+
}), /* @__PURE__ */ jsx("div", { children })]
|
|
86
|
+
});
|
|
87
|
+
};
|
|
88
|
+
//#endregion
|
|
89
|
+
export { SubNavLayout };
|
|
90
|
+
|
|
91
|
+
//# sourceMappingURL=subnav.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subnav.mjs","names":[],"sources":["../../src/ui/subnav.tsx"],"sourcesContent":["import type { FC } from \"hono/jsx\";\nimport { css } from \"./css.ts\";\nimport { Icon } from \"./icons/icon.tsx\";\nimport type { IconName } from \"./icons/paths.ts\";\n\n/* eslint-disable promise-function-async -- Hono JSX components return HtmlEscapedString | Promise<HtmlEscapedString> */\n\nconst layout = css`\n /* subnav-layout */\n display: grid;\n grid-template-columns: 210px minmax(0, 1fr);\n gap: var(--space-6);\n align-items: start;\n @media (max-width: 880px) {\n & {\n grid-template-columns: minmax(0, 1fr);\n gap: var(--space-4);\n }\n }\n`;\n\nconst nav = css`\n /* subnav */\n position: sticky;\n top: calc(var(--topbar-height) + var(--space-4));\n display: flex;\n flex-direction: column;\n gap: 2px;\n @media (max-width: 880px) {\n & {\n position: static;\n flex-direction: row;\n gap: var(--space-1);\n padding-bottom: var(--space-2);\n overflow-x: auto;\n border-bottom: 1px solid var(--border);\n }\n }\n`;\n\nconst link = css`\n /* subnav-link */\n display: flex;\n align-items: center;\n gap: 0.6rem;\n min-height: 36px;\n padding: 0.4rem var(--space-3);\n border-radius: var(--radius);\n color: var(--text-secondary);\n font-size: var(--text-base);\n font-weight: 500;\n text-decoration: none;\n white-space: nowrap;\n transition:\n background var(--dur-fast) var(--ease),\n color var(--dur-fast) var(--ease);\n &:hover {\n background: var(--surface-muted);\n color: var(--text-primary);\n text-decoration: none;\n }\n`;\n\nconst linkActive = css`\n /* subnav-link-active */\n ${link}\n background: var(--accent-subtle);\n color: var(--accent-fg);\n font-weight: 600;\n &:hover {\n background: var(--accent-subtle);\n color: var(--accent-fg);\n }\n`;\n\n/** One entry in a `SubNavLayout`. */\nexport interface SubNavItem {\n label: string;\n href: string;\n icon?: IconName;\n active?: boolean;\n}\n\n/** Vertical section navigation beside content (a horizontal strip on phones). */\nexport const SubNavLayout: FC<{ items: SubNavItem[]; label: string; children?: unknown }> = ({\n items,\n label,\n children,\n}) => {\n return (\n <div class={layout}>\n <nav class={nav} aria-label={label}>\n {items.map((item) => (\n <a\n key={item.href}\n class={item.active ? linkActive : link}\n href={item.href}\n aria-current={item.active ? \"page\" : undefined}\n >\n {item.icon ? <Icon name={item.icon} /> : null}\n {item.label}\n </a>\n ))}\n </nav>\n <div>{children}</div>\n </div>\n );\n};\n"],"mappings":";;;;;;;AAOA,MAAM,SAAS,GAAG;;;;;;;;;;;;;AAclB,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;AAmBf,MAAM,OAAO,GAAG;;;;;;;;;;;;;;;;;;;;;;AAuBhB,MAAM,aAAa,GAAG;;IAElB,KAAK;;;;;;;;;;AAmBT,MAAa,gBAAgF,EAC3F,OACA,OACA,eACI;CACJ,OACE,qBAAC,OAAD;EAAK,OAAO;EAAZ,UAAA,CACE,oBAAC,OAAD;GAAK,OAAO;GAAK,cAAY;GAC1B,UAAA,MAAM,KAAK,SACV,qBAAC,KAAD;IAEE,OAAO,KAAK,SAAS,aAAa;IAClC,MAAM,KAAK;IACX,gBAAc,KAAK,SAAS,SAAS,KAAA;IAJvC,UAAA,CAMG,KAAK,OAAO,oBAAC,MAAD,EAAM,MAAM,KAAK,KAAO,CAAA,IAAI,MACxC,KAAK,KACL;GAPI,GAAA,KAAK,IAOT,CACJ;EACE,CAAA,GACL,oBAAC,OAAD,EAAM,SAAc,CAAA,CACjB;;AAET"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { css } from "./css.mjs";
|
|
5
|
+
import { jsx } from "hono/jsx/jsx-runtime";
|
|
6
|
+
//#region src/ui/table.tsx
|
|
7
|
+
const tableWrap = css`
|
|
8
|
+
/* table */
|
|
9
|
+
overflow: auto;
|
|
10
|
+
background: var(--surface-card);
|
|
11
|
+
border: 1px solid var(--border);
|
|
12
|
+
border-radius: var(--radius-lg);
|
|
13
|
+
box-shadow: var(--shadow);
|
|
14
|
+
& table {
|
|
15
|
+
width: 100%;
|
|
16
|
+
border-collapse: separate;
|
|
17
|
+
border-spacing: 0;
|
|
18
|
+
font-size: var(--text-base);
|
|
19
|
+
}
|
|
20
|
+
& th,
|
|
21
|
+
& td {
|
|
22
|
+
text-align: left;
|
|
23
|
+
padding: 0.7rem 0.9rem;
|
|
24
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
25
|
+
vertical-align: middle;
|
|
26
|
+
}
|
|
27
|
+
& th {
|
|
28
|
+
position: sticky;
|
|
29
|
+
top: 0;
|
|
30
|
+
z-index: 1;
|
|
31
|
+
background: var(--surface-subtle);
|
|
32
|
+
color: var(--text-secondary);
|
|
33
|
+
font-size: var(--text-xs);
|
|
34
|
+
font-weight: 600;
|
|
35
|
+
letter-spacing: 0.04em;
|
|
36
|
+
text-transform: uppercase;
|
|
37
|
+
white-space: nowrap;
|
|
38
|
+
}
|
|
39
|
+
& tbody tr {
|
|
40
|
+
transition: background var(--dur-fast) var(--ease);
|
|
41
|
+
}
|
|
42
|
+
& tbody tr:hover {
|
|
43
|
+
background: var(--accent-wash);
|
|
44
|
+
}
|
|
45
|
+
& tbody tr:last-child td {
|
|
46
|
+
border-bottom: 0;
|
|
47
|
+
}
|
|
48
|
+
& td.nowrap {
|
|
49
|
+
white-space: nowrap;
|
|
50
|
+
}
|
|
51
|
+
`;
|
|
52
|
+
const tableDense = css`
|
|
53
|
+
/* table-dense */
|
|
54
|
+
${tableWrap}
|
|
55
|
+
& th,
|
|
56
|
+
& td {
|
|
57
|
+
padding: 0.4rem 0.75rem;
|
|
58
|
+
}
|
|
59
|
+
`;
|
|
60
|
+
/**
|
|
61
|
+
* Scrollable bordered table surface. Pass native `<table>` markup as
|
|
62
|
+
* children; the header, row hover, and spacing are styled here.
|
|
63
|
+
*/
|
|
64
|
+
const Table = ({ dense = false, children }) => {
|
|
65
|
+
return /* @__PURE__ */ jsx("div", {
|
|
66
|
+
class: dense ? tableDense : tableWrap,
|
|
67
|
+
children
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
//#endregion
|
|
71
|
+
export { Table };
|
|
72
|
+
|
|
73
|
+
//# sourceMappingURL=table.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table.mjs","names":[],"sources":["../../src/ui/table.tsx"],"sourcesContent":["import type { FC } from \"hono/jsx\";\nimport { css } from \"./css.ts\";\n\n/* eslint-disable promise-function-async -- Hono JSX components return HtmlEscapedString | Promise<HtmlEscapedString> */\n\nconst tableWrap = css`\n /* table */\n overflow: auto;\n background: var(--surface-card);\n border: 1px solid var(--border);\n border-radius: var(--radius-lg);\n box-shadow: var(--shadow);\n & table {\n width: 100%;\n border-collapse: separate;\n border-spacing: 0;\n font-size: var(--text-base);\n }\n & th,\n & td {\n text-align: left;\n padding: 0.7rem 0.9rem;\n border-bottom: 1px solid var(--border-subtle);\n vertical-align: middle;\n }\n & th {\n position: sticky;\n top: 0;\n z-index: 1;\n background: var(--surface-subtle);\n color: var(--text-secondary);\n font-size: var(--text-xs);\n font-weight: 600;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n white-space: nowrap;\n }\n & tbody tr {\n transition: background var(--dur-fast) var(--ease);\n }\n & tbody tr:hover {\n background: var(--accent-wash);\n }\n & tbody tr:last-child td {\n border-bottom: 0;\n }\n & td.nowrap {\n white-space: nowrap;\n }\n`;\n\nconst tableDense = css`\n /* table-dense */\n ${tableWrap}\n & th,\n & td {\n padding: 0.4rem 0.75rem;\n }\n`;\n\n/**\n * Scrollable bordered table surface. Pass native `<table>` markup as\n * children; the header, row hover, and spacing are styled here.\n */\nexport const Table: FC<{ dense?: boolean; children?: unknown }> = ({ dense = false, children }) => {\n return <div class={dense ? tableDense : tableWrap}>{children}</div>;\n};\n"],"mappings":";;;;;;AAKA,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8CrB,MAAM,aAAa,GAAG;;IAElB,UAAU;;;;;;;;;;AAWd,MAAa,SAAsD,EAAE,QAAQ,OAAO,eAAe;CACjG,OAAO,oBAAC,OAAD;EAAK,OAAO,QAAQ,aAAa;EAAY;CAAc,CAAA;AACpE"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
//#region src/ui/theme.ts
|
|
5
|
+
/** Default light brand theme (shadcn-like zinc, content-first). */
|
|
6
|
+
const LIGHT_THEME = {
|
|
7
|
+
accent: "#1d5fcf",
|
|
8
|
+
accentContrast: "#ffffff",
|
|
9
|
+
ring: "#1d5fcf",
|
|
10
|
+
surface: {
|
|
11
|
+
base: "#fafafa",
|
|
12
|
+
card: "#ffffff",
|
|
13
|
+
muted: "#f4f4f5",
|
|
14
|
+
subtle: "#fafafa"
|
|
15
|
+
},
|
|
16
|
+
text: {
|
|
17
|
+
primary: "#09090b",
|
|
18
|
+
secondary: "#5b5b64",
|
|
19
|
+
muted: "#6a6a73"
|
|
20
|
+
},
|
|
21
|
+
border: "#e4e4e7",
|
|
22
|
+
borderSubtle: "#f4f4f5",
|
|
23
|
+
status: {
|
|
24
|
+
approved: "#16a34a",
|
|
25
|
+
new: "#d97706",
|
|
26
|
+
rejected: "#dc2626"
|
|
27
|
+
},
|
|
28
|
+
sidebarBg: "#ffffff",
|
|
29
|
+
radius: "0.5rem",
|
|
30
|
+
radiusSm: "0.375rem",
|
|
31
|
+
shadow: "0 1px 2px rgba(0,0,0,.04)"
|
|
32
|
+
};
|
|
33
|
+
/** Default dark brand theme (shadcn-like zinc). */
|
|
34
|
+
const DARK_THEME = {
|
|
35
|
+
accent: "#4f8fff",
|
|
36
|
+
accentContrast: "#09090b",
|
|
37
|
+
ring: "#4f8fff",
|
|
38
|
+
surface: {
|
|
39
|
+
base: "#09090b",
|
|
40
|
+
card: "#111113",
|
|
41
|
+
muted: "#27272a",
|
|
42
|
+
subtle: "#18181b"
|
|
43
|
+
},
|
|
44
|
+
text: {
|
|
45
|
+
primary: "#fafafa",
|
|
46
|
+
secondary: "#a1a1aa",
|
|
47
|
+
muted: "#8a8a94"
|
|
48
|
+
},
|
|
49
|
+
border: "#27272a",
|
|
50
|
+
borderSubtle: "#1e1e22",
|
|
51
|
+
status: {
|
|
52
|
+
approved: "#22c55e",
|
|
53
|
+
new: "#f59e0b",
|
|
54
|
+
rejected: "#ef4444"
|
|
55
|
+
},
|
|
56
|
+
sidebarBg: "#111113",
|
|
57
|
+
radius: "0.5rem",
|
|
58
|
+
radiusSm: "0.375rem",
|
|
59
|
+
shadow: "0 1px 2px rgba(0,0,0,.3)"
|
|
60
|
+
};
|
|
61
|
+
//#endregion
|
|
62
|
+
export { DARK_THEME, LIGHT_THEME };
|
|
63
|
+
|
|
64
|
+
//# sourceMappingURL=theme.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme.mjs","names":[],"sources":["../../src/ui/theme.ts"],"sourcesContent":["/** Brand theme tokens (accent, surfaces, text, borders, statuses). */\nexport interface BrandTheme {\n accent: string;\n accentContrast?: string;\n ring?: string;\n surface: { base: string; card: string; muted?: string; subtle?: string };\n text: { primary: string; secondary: string; muted?: string };\n border: string;\n borderSubtle?: string;\n status: { approved: string; new: string; rejected: string };\n sidebarBg?: string;\n /** @deprecated No longer used: the top bar is neutral. Accepted so existing configs still parse. */\n topbarBg?: string;\n radius?: string;\n radiusSm?: string;\n shadow?: string;\n}\n\n/** Default light brand theme (shadcn-like zinc, content-first). */\nexport const LIGHT_THEME: BrandTheme = {\n accent: \"#1d5fcf\",\n accentContrast: \"#ffffff\",\n ring: \"#1d5fcf\",\n surface: { base: \"#fafafa\", card: \"#ffffff\", muted: \"#f4f4f5\", subtle: \"#fafafa\" },\n text: { primary: \"#09090b\", secondary: \"#5b5b64\", muted: \"#6a6a73\" },\n border: \"#e4e4e7\",\n borderSubtle: \"#f4f4f5\",\n status: { approved: \"#16a34a\", new: \"#d97706\", rejected: \"#dc2626\" },\n sidebarBg: \"#ffffff\",\n radius: \"0.5rem\",\n radiusSm: \"0.375rem\",\n shadow: \"0 1px 2px rgba(0,0,0,.04)\",\n};\n\n/** Default dark brand theme (shadcn-like zinc). */\nexport const DARK_THEME: BrandTheme = {\n accent: \"#4f8fff\",\n accentContrast: \"#09090b\",\n ring: \"#4f8fff\",\n surface: { base: \"#09090b\", card: \"#111113\", muted: \"#27272a\", subtle: \"#18181b\" },\n text: { primary: \"#fafafa\", secondary: \"#a1a1aa\", muted: \"#8a8a94\" },\n border: \"#27272a\",\n borderSubtle: \"#1e1e22\",\n status: { approved: \"#22c55e\", new: \"#f59e0b\", rejected: \"#ef4444\" },\n sidebarBg: \"#111113\",\n radius: \"0.5rem\",\n radiusSm: \"0.375rem\",\n shadow: \"0 1px 2px rgba(0,0,0,.3)\",\n};\n"],"mappings":";;;;;AAmBA,MAAa,cAA0B;CACrC,QAAQ;CACR,gBAAgB;CAChB,MAAM;CACN,SAAS;EAAE,MAAM;EAAW,MAAM;EAAW,OAAO;EAAW,QAAQ;CAAU;CACjF,MAAM;EAAE,SAAS;EAAW,WAAW;EAAW,OAAO;CAAU;CACnE,QAAQ;CACR,cAAc;CACd,QAAQ;EAAE,UAAU;EAAW,KAAK;EAAW,UAAU;CAAU;CACnE,WAAW;CACX,QAAQ;CACR,UAAU;CACV,QAAQ;AACV;;AAGA,MAAa,aAAyB;CACpC,QAAQ;CACR,gBAAgB;CAChB,MAAM;CACN,SAAS;EAAE,MAAM;EAAW,MAAM;EAAW,OAAO;EAAW,QAAQ;CAAU;CACjF,MAAM;EAAE,SAAS;EAAW,WAAW;EAAW,OAAO;CAAU;CACnE,QAAQ;CACR,cAAc;CACd,QAAQ;EAAE,UAAU;EAAW,KAAK;EAAW,UAAU;CAAU;CACnE,WAAW;CACX,QAAQ;CACR,UAAU;CACV,QAAQ;AACV"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { css } from "./css.mjs";
|
|
5
|
+
import { jsx } from "hono/jsx/jsx-runtime";
|
|
6
|
+
//#region src/ui/thumbnail.tsx
|
|
7
|
+
const thumbFrame = css`
|
|
8
|
+
/* thumbnail */
|
|
9
|
+
position: relative;
|
|
10
|
+
display: block;
|
|
11
|
+
overflow: hidden;
|
|
12
|
+
aspect-ratio: 16 / 10;
|
|
13
|
+
background: var(--surface-muted);
|
|
14
|
+
border: 1px solid var(--border-subtle);
|
|
15
|
+
border-radius: var(--radius-sm);
|
|
16
|
+
& img {
|
|
17
|
+
display: block;
|
|
18
|
+
width: 100%;
|
|
19
|
+
height: 100%;
|
|
20
|
+
object-fit: cover;
|
|
21
|
+
object-position: top center;
|
|
22
|
+
}
|
|
23
|
+
`;
|
|
24
|
+
const thumbEmpty = css`
|
|
25
|
+
/* thumbnail-empty */
|
|
26
|
+
${thumbFrame}
|
|
27
|
+
display: grid;
|
|
28
|
+
place-items: center;
|
|
29
|
+
color: var(--text-secondary);
|
|
30
|
+
font-size: var(--text-xs);
|
|
31
|
+
`;
|
|
32
|
+
/** Fixed-ratio, lazy-loaded screenshot preview with a muted placeholder. */
|
|
33
|
+
const Thumbnail = ({ src, alt, placeholder = "No preview" }) => {
|
|
34
|
+
if (!src) return /* @__PURE__ */ jsx("span", {
|
|
35
|
+
class: thumbEmpty,
|
|
36
|
+
children: placeholder
|
|
37
|
+
});
|
|
38
|
+
return /* @__PURE__ */ jsx("span", {
|
|
39
|
+
class: thumbFrame,
|
|
40
|
+
children: /* @__PURE__ */ jsx("img", {
|
|
41
|
+
src,
|
|
42
|
+
alt,
|
|
43
|
+
loading: "lazy",
|
|
44
|
+
decoding: "async"
|
|
45
|
+
})
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
//#endregion
|
|
49
|
+
export { Thumbnail };
|
|
50
|
+
|
|
51
|
+
//# sourceMappingURL=thumbnail.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thumbnail.mjs","names":[],"sources":["../../src/ui/thumbnail.tsx"],"sourcesContent":["import type { FC } from \"hono/jsx\";\nimport { css } from \"./css.ts\";\n\n/* eslint-disable promise-function-async -- Hono JSX components return HtmlEscapedString | Promise<HtmlEscapedString> */\n\nconst thumbFrame = css`\n /* thumbnail */\n position: relative;\n display: block;\n overflow: hidden;\n aspect-ratio: 16 / 10;\n background: var(--surface-muted);\n border: 1px solid var(--border-subtle);\n border-radius: var(--radius-sm);\n & img {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n object-position: top center;\n }\n`;\n\nconst thumbEmpty = css`\n /* thumbnail-empty */\n ${thumbFrame}\n display: grid;\n place-items: center;\n color: var(--text-secondary);\n font-size: var(--text-xs);\n`;\n\n/** Fixed-ratio, lazy-loaded screenshot preview with a muted placeholder. */\nexport const Thumbnail: FC<{ src?: string | null; alt: string; placeholder?: string }> = ({\n src,\n alt,\n placeholder = \"No preview\",\n}) => {\n if (!src) {\n return <span class={thumbEmpty}>{placeholder}</span>;\n }\n return (\n <span class={thumbFrame}>\n <img src={src} alt={alt} loading=\"lazy\" decoding=\"async\" />\n </span>\n );\n};\n"],"mappings":";;;;;;AAKA,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;AAkBtB,MAAM,aAAa,GAAG;;IAElB,WAAW;;;;;;;AAQf,MAAa,aAA6E,EACxF,KACA,KACA,cAAc,mBACV;CACJ,IAAI,CAAC,KACH,OAAO,oBAAC,QAAD;EAAM,OAAO;EAAa,UAAA;CAAkB,CAAA;CAErD,OACE,oBAAC,QAAD;EAAM,OAAO;EACX,UAAA,oBAAC,OAAD;GAAU;GAAU;GAAK,SAAQ;GAAO,UAAS;EAAS,CAAA;CACtD,CAAA;AAEV"}
|
package/dist/ui/time.mjs
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { jsx } from "hono/jsx/jsx-runtime";
|
|
5
|
+
//#region src/ui/time.tsx
|
|
6
|
+
const MINUTE = 60;
|
|
7
|
+
const HOUR = 3600;
|
|
8
|
+
const DAY = 86400;
|
|
9
|
+
/** [upper bound in seconds, unit size in seconds, unit label], tried in order. */
|
|
10
|
+
const STEPS = [
|
|
11
|
+
[
|
|
12
|
+
HOUR,
|
|
13
|
+
MINUTE,
|
|
14
|
+
"min"
|
|
15
|
+
],
|
|
16
|
+
[
|
|
17
|
+
DAY,
|
|
18
|
+
HOUR,
|
|
19
|
+
"h"
|
|
20
|
+
],
|
|
21
|
+
[
|
|
22
|
+
7 * DAY,
|
|
23
|
+
DAY,
|
|
24
|
+
"d"
|
|
25
|
+
]
|
|
26
|
+
];
|
|
27
|
+
/** Compact relative time ("3 min ago"); falls back to an ISO date after a week. */
|
|
28
|
+
function formatRelative(value, now = /* @__PURE__ */ new Date()) {
|
|
29
|
+
const then = new Date(value);
|
|
30
|
+
if (Number.isNaN(then.getTime())) return "unknown";
|
|
31
|
+
const seconds = Math.floor((now.getTime() - then.getTime()) / 1e3);
|
|
32
|
+
if (seconds < 45) return "just now";
|
|
33
|
+
const step = STEPS.find(([limit]) => seconds < limit);
|
|
34
|
+
if (!step) return then.toISOString().slice(0, 10);
|
|
35
|
+
return `${Math.max(1, Math.round(seconds / step[1]))} ${step[2]} ago`;
|
|
36
|
+
}
|
|
37
|
+
/** `<time>` showing a relative label, with the exact timestamp on hover. */
|
|
38
|
+
const RelativeTime = ({ value }) => {
|
|
39
|
+
const date = new Date(value);
|
|
40
|
+
const valid = !Number.isNaN(date.getTime());
|
|
41
|
+
return /* @__PURE__ */ jsx("time", {
|
|
42
|
+
datetime: valid ? date.toISOString() : void 0,
|
|
43
|
+
title: valid ? date.toUTCString() : "",
|
|
44
|
+
children: formatRelative(value)
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
//#endregion
|
|
48
|
+
export { RelativeTime, formatRelative };
|
|
49
|
+
|
|
50
|
+
//# sourceMappingURL=time.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time.mjs","names":[],"sources":["../../src/ui/time.tsx"],"sourcesContent":["import type { FC } from \"hono/jsx\";\n\n/* eslint-disable promise-function-async -- Hono JSX components return HtmlEscapedString | Promise<HtmlEscapedString> */\n\nconst MINUTE = 60;\nconst HOUR = 3600;\nconst DAY = 86_400;\n\n/** [upper bound in seconds, unit size in seconds, unit label], tried in order. */\nconst STEPS: [number, number, string][] = [\n [HOUR, MINUTE, \"min\"],\n [DAY, HOUR, \"h\"],\n [7 * DAY, DAY, \"d\"],\n];\n\n/** Compact relative time (\"3 min ago\"); falls back to an ISO date after a week. */\nexport function formatRelative(value: string | Date, now: Date = new Date()): string {\n const then = new Date(value);\n if (Number.isNaN(then.getTime())) {\n return \"unknown\";\n }\n const seconds = Math.floor((now.getTime() - then.getTime()) / 1000);\n if (seconds < 45) {\n return \"just now\";\n }\n const step = STEPS.find(([limit]) => seconds < limit);\n if (!step) {\n return then.toISOString().slice(0, 10);\n }\n return `${Math.max(1, Math.round(seconds / step[1]))} ${step[2]} ago`;\n}\n\n/** `<time>` showing a relative label, with the exact timestamp on hover. */\nexport const RelativeTime: FC<{ value: string | Date }> = ({ value }) => {\n const date = new Date(value);\n const valid = !Number.isNaN(date.getTime());\n return (\n <time datetime={valid ? date.toISOString() : undefined} title={valid ? date.toUTCString() : \"\"}>\n {formatRelative(value)}\n </time>\n );\n};\n"],"mappings":";;;;;AAIA,MAAM,SAAS;AACf,MAAM,OAAO;AACb,MAAM,MAAM;;AAGZ,MAAM,QAAoC;CACxC;EAAC;EAAM;EAAQ;CAAK;CACpB;EAAC;EAAK;EAAM;CAAG;CACf;EAAC,IAAI;EAAK;EAAK;CAAG;AACpB;;AAGA,SAAgB,eAAe,OAAsB,sBAAY,IAAI,KAAK,GAAW;CACnF,MAAM,OAAO,IAAI,KAAK,KAAK;CAC3B,IAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,GAC7B,OAAO;CAET,MAAM,UAAU,KAAK,OAAO,IAAI,QAAQ,IAAI,KAAK,QAAQ,KAAK,GAAI;CAClE,IAAI,UAAU,IACZ,OAAO;CAET,MAAM,OAAO,MAAM,MAAM,CAAC,WAAW,UAAU,KAAK;CACpD,IAAI,CAAC,MACH,OAAO,KAAK,YAAY,CAAC,CAAC,MAAM,GAAG,EAAE;CAEvC,OAAO,GAAG,KAAK,IAAI,GAAG,KAAK,MAAM,UAAU,KAAK,EAAE,CAAC,EAAE,GAAG,KAAK,GAAG;AAClE;;AAGA,MAAa,gBAA8C,EAAE,YAAY;CACvE,MAAM,OAAO,IAAI,KAAK,KAAK;CAC3B,MAAM,QAAQ,CAAC,OAAO,MAAM,KAAK,QAAQ,CAAC;CAC1C,OACE,oBAAC,QAAD;EAAM,UAAU,QAAQ,KAAK,YAAY,IAAI,KAAA;EAAW,OAAO,QAAQ,KAAK,YAAY,IAAI;EACzF,UAAA,eAAe,KAAK;CACjB,CAAA;AAEV"}
|
package/package.json
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@storyshelf/app",
|
|
3
|
+
"version": "0.5.3",
|
|
4
|
+
"description": "StoryShelf app: Hono app, API routes, and server-rendered UI over @storyshelf/core.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"chromatic-alternative",
|
|
7
|
+
"hono",
|
|
8
|
+
"self-hosted",
|
|
9
|
+
"storybook",
|
|
10
|
+
"visual-testing"
|
|
11
|
+
],
|
|
12
|
+
"homepage": "https://github.com/GuptaSiddhant/StoryShelf#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/GuptaSiddhant/StoryShelf/issues"
|
|
15
|
+
},
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"author": {
|
|
18
|
+
"name": "Siddhant Gupta",
|
|
19
|
+
"url": "https://guptasiddhant.com"
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/GuptaSiddhant/StoryShelf.git",
|
|
24
|
+
"directory": "packages/app"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist"
|
|
28
|
+
],
|
|
29
|
+
"type": "module",
|
|
30
|
+
"sideEffects": false,
|
|
31
|
+
"types": "./dist/index.d.mts",
|
|
32
|
+
"exports": {
|
|
33
|
+
".": {
|
|
34
|
+
"source": "./src/index.tsx",
|
|
35
|
+
"default": "./dist/index.mjs"
|
|
36
|
+
},
|
|
37
|
+
"./package.json": "./package.json"
|
|
38
|
+
},
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"exports": {
|
|
41
|
+
".": "./dist/index.mjs",
|
|
42
|
+
"./package.json": "./package.json"
|
|
43
|
+
},
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "tsdown",
|
|
48
|
+
"dev": "tsdown -w",
|
|
49
|
+
"fmt": "oxfmt ./src",
|
|
50
|
+
"lint": "oxlint --type-aware --type-check ./src",
|
|
51
|
+
"lint:fix": "nub run lint -- --fix",
|
|
52
|
+
"prepublishOnly": "nub run build",
|
|
53
|
+
"test": "vitest run",
|
|
54
|
+
"test:coverage": "vitest run --coverage",
|
|
55
|
+
"test:watch": "vitest"
|
|
56
|
+
},
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"@hono/swagger-ui": "^0.6.1",
|
|
59
|
+
"@hono/zod-openapi": "^1.1.5",
|
|
60
|
+
"@storyshelf/core": "0.5.3",
|
|
61
|
+
"@storyshelf/observability": "0.5.3",
|
|
62
|
+
"hono": "^4.11.0",
|
|
63
|
+
"zod": "^4.2.0"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@opentelemetry/api": "^1.9.0",
|
|
67
|
+
"@opentelemetry/context-async-hooks": "^2.11.0",
|
|
68
|
+
"@opentelemetry/core": "^2.11.0",
|
|
69
|
+
"@opentelemetry/sdk-trace-base": "^2.11.0",
|
|
70
|
+
"@storyshelf/auth": "0.5.3",
|
|
71
|
+
"@storyshelf/db-sqlite": "0.5.3",
|
|
72
|
+
"@types/node": "^26.0.0",
|
|
73
|
+
"@vitest/coverage-v8": "^4.0.0",
|
|
74
|
+
"adm-zip": "0.6.0",
|
|
75
|
+
"oxfmt": "^0.65.0",
|
|
76
|
+
"oxlint": "^1.80.0",
|
|
77
|
+
"oxlint-tsgolint": "^7.0.2001",
|
|
78
|
+
"pino": "^10.3.1",
|
|
79
|
+
"tsdown": "^0.22.14",
|
|
80
|
+
"typescript": "^7.0.2",
|
|
81
|
+
"vitest": "^5.0.0"
|
|
82
|
+
},
|
|
83
|
+
"jsr": {
|
|
84
|
+
"runtimeCompat": {
|
|
85
|
+
"node": true,
|
|
86
|
+
"deno": true,
|
|
87
|
+
"browser": false,
|
|
88
|
+
"workerd": false,
|
|
89
|
+
"bun": true
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|