@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,341 @@
|
|
|
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/feedback.tsx
|
|
8
|
+
const badgeBase = css`
|
|
9
|
+
/* badge */
|
|
10
|
+
display: inline-flex;
|
|
11
|
+
align-items: center;
|
|
12
|
+
gap: 0.3rem;
|
|
13
|
+
padding: 0.1rem 0.55rem;
|
|
14
|
+
border-radius: var(--radius-pill);
|
|
15
|
+
font-size: var(--text-xs);
|
|
16
|
+
font-weight: 600;
|
|
17
|
+
line-height: 1.5;
|
|
18
|
+
border: 1px solid var(--border);
|
|
19
|
+
background: var(--surface-muted);
|
|
20
|
+
color: var(--text-secondary);
|
|
21
|
+
white-space: nowrap;
|
|
22
|
+
`;
|
|
23
|
+
const badgeTones = {
|
|
24
|
+
neutral: css`
|
|
25
|
+
/* badge-neutral */
|
|
26
|
+
${badgeBase}
|
|
27
|
+
`,
|
|
28
|
+
success: css`
|
|
29
|
+
/* badge-success */
|
|
30
|
+
${badgeBase}
|
|
31
|
+
background: var(--status-approved-bg);
|
|
32
|
+
color: var(--status-approved-fg);
|
|
33
|
+
border-color: var(--status-approved-border);
|
|
34
|
+
`,
|
|
35
|
+
warning: css`
|
|
36
|
+
/* badge-warning */
|
|
37
|
+
${badgeBase}
|
|
38
|
+
background: var(--status-new-bg);
|
|
39
|
+
color: var(--status-new-fg);
|
|
40
|
+
border-color: var(--status-new-border);
|
|
41
|
+
`,
|
|
42
|
+
danger: css`
|
|
43
|
+
/* badge-danger */
|
|
44
|
+
${badgeBase}
|
|
45
|
+
background: var(--status-rejected-bg);
|
|
46
|
+
color: var(--status-rejected-fg);
|
|
47
|
+
border-color: var(--status-rejected-border);
|
|
48
|
+
`,
|
|
49
|
+
info: css`
|
|
50
|
+
/* badge-info */
|
|
51
|
+
${badgeBase}
|
|
52
|
+
background: var(--status-info-bg);
|
|
53
|
+
color: var(--accent-fg);
|
|
54
|
+
border-color: var(--status-info-border);
|
|
55
|
+
`
|
|
56
|
+
};
|
|
57
|
+
/** Status glyph per tone so state never relies on color alone. */
|
|
58
|
+
const toneIcons = {
|
|
59
|
+
neutral: "info",
|
|
60
|
+
success: "check-circle",
|
|
61
|
+
warning: "alert-triangle",
|
|
62
|
+
danger: "x-circle",
|
|
63
|
+
info: "info"
|
|
64
|
+
};
|
|
65
|
+
const alertBase = css`
|
|
66
|
+
/* alert */
|
|
67
|
+
display: flex;
|
|
68
|
+
gap: 0.6rem;
|
|
69
|
+
align-items: flex-start;
|
|
70
|
+
border-radius: var(--radius);
|
|
71
|
+
padding: 0.75rem 0.9rem;
|
|
72
|
+
border: 1px solid var(--border);
|
|
73
|
+
background: var(--surface-card);
|
|
74
|
+
font-size: var(--text-base);
|
|
75
|
+
`;
|
|
76
|
+
const alertTones = {
|
|
77
|
+
neutral: css`
|
|
78
|
+
/* alert-neutral */
|
|
79
|
+
${alertBase}
|
|
80
|
+
`,
|
|
81
|
+
info: css`
|
|
82
|
+
/* alert-info */
|
|
83
|
+
${alertBase}
|
|
84
|
+
border-color: var(--status-info-border);
|
|
85
|
+
background: var(--status-info-bg);
|
|
86
|
+
`,
|
|
87
|
+
success: css`
|
|
88
|
+
/* alert-success */
|
|
89
|
+
${alertBase}
|
|
90
|
+
border-color: var(--status-approved-border);
|
|
91
|
+
background: var(--status-approved-bg);
|
|
92
|
+
`,
|
|
93
|
+
warning: css`
|
|
94
|
+
/* alert-warning */
|
|
95
|
+
${alertBase}
|
|
96
|
+
border-color: var(--status-new-border);
|
|
97
|
+
background: var(--status-new-bg);
|
|
98
|
+
`,
|
|
99
|
+
danger: css`
|
|
100
|
+
/* alert-danger */
|
|
101
|
+
${alertBase}
|
|
102
|
+
border-color: var(--status-rejected-border);
|
|
103
|
+
background: var(--status-rejected-bg);
|
|
104
|
+
`
|
|
105
|
+
};
|
|
106
|
+
const alertIconTones = {
|
|
107
|
+
neutral: css`
|
|
108
|
+
/* alert-icon-neutral */
|
|
109
|
+
margin-top: 0.1rem;
|
|
110
|
+
color: var(--text-secondary);
|
|
111
|
+
`,
|
|
112
|
+
info: css`
|
|
113
|
+
/* alert-icon-info */
|
|
114
|
+
margin-top: 0.1rem;
|
|
115
|
+
color: var(--accent-fg);
|
|
116
|
+
`,
|
|
117
|
+
success: css`
|
|
118
|
+
/* alert-icon-success */
|
|
119
|
+
margin-top: 0.1rem;
|
|
120
|
+
color: var(--status-approved);
|
|
121
|
+
`,
|
|
122
|
+
warning: css`
|
|
123
|
+
/* alert-icon-warning */
|
|
124
|
+
margin-top: 0.1rem;
|
|
125
|
+
color: var(--status-new);
|
|
126
|
+
`,
|
|
127
|
+
danger: css`
|
|
128
|
+
/* alert-icon-danger */
|
|
129
|
+
margin-top: 0.1rem;
|
|
130
|
+
color: var(--status-rejected);
|
|
131
|
+
`
|
|
132
|
+
};
|
|
133
|
+
const emptyWrap = css`
|
|
134
|
+
/* empty */
|
|
135
|
+
text-align: center;
|
|
136
|
+
padding: 2rem 1rem;
|
|
137
|
+
border: 1px dashed var(--border);
|
|
138
|
+
border-radius: var(--radius);
|
|
139
|
+
background: var(--surface-card);
|
|
140
|
+
`;
|
|
141
|
+
const emptyTitle = css`
|
|
142
|
+
/* empty-title */
|
|
143
|
+
margin: 0 0 0.3rem;
|
|
144
|
+
font-size: 1.05rem;
|
|
145
|
+
font-weight: 650;
|
|
146
|
+
letter-spacing: -0.01em;
|
|
147
|
+
`;
|
|
148
|
+
const emptyDesc = css`
|
|
149
|
+
/* empty-desc */
|
|
150
|
+
margin: 0 auto;
|
|
151
|
+
color: var(--text-secondary);
|
|
152
|
+
font-size: 0.875rem;
|
|
153
|
+
max-width: 50ch;
|
|
154
|
+
`;
|
|
155
|
+
const emptyAction = css`
|
|
156
|
+
/* empty-action */
|
|
157
|
+
margin-top: 1rem;
|
|
158
|
+
`;
|
|
159
|
+
const statWrap = css`
|
|
160
|
+
/* stat */
|
|
161
|
+
text-align: center;
|
|
162
|
+
padding: 0.5rem;
|
|
163
|
+
`;
|
|
164
|
+
const statValueBase = css`
|
|
165
|
+
/* stat-value */
|
|
166
|
+
font-size: 1.35rem;
|
|
167
|
+
font-weight: 700;
|
|
168
|
+
line-height: 1.1;
|
|
169
|
+
font-variant-numeric: tabular-nums;
|
|
170
|
+
`;
|
|
171
|
+
const statValueTones = {
|
|
172
|
+
neutral: css`
|
|
173
|
+
/* stat-value-neutral */
|
|
174
|
+
${statValueBase}
|
|
175
|
+
`,
|
|
176
|
+
success: css`
|
|
177
|
+
/* stat-value-success */
|
|
178
|
+
${statValueBase}
|
|
179
|
+
color: var(--status-approved);
|
|
180
|
+
`,
|
|
181
|
+
warning: css`
|
|
182
|
+
/* stat-value-warning */
|
|
183
|
+
${statValueBase}
|
|
184
|
+
color: var(--status-new);
|
|
185
|
+
`
|
|
186
|
+
};
|
|
187
|
+
const statLabel = css`
|
|
188
|
+
/* stat-label */
|
|
189
|
+
color: var(--text-secondary);
|
|
190
|
+
font-size: 0.72rem;
|
|
191
|
+
text-transform: uppercase;
|
|
192
|
+
letter-spacing: 0.06em;
|
|
193
|
+
`;
|
|
194
|
+
const metaText = css`
|
|
195
|
+
/* meta */
|
|
196
|
+
margin: 0;
|
|
197
|
+
color: var(--text-secondary);
|
|
198
|
+
font-size: 0.82rem;
|
|
199
|
+
`;
|
|
200
|
+
const metaMono = css`
|
|
201
|
+
/* meta-mono */
|
|
202
|
+
${metaText}
|
|
203
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
204
|
+
`;
|
|
205
|
+
const metaCode = css`
|
|
206
|
+
/* meta-code */
|
|
207
|
+
${metaMono}
|
|
208
|
+
font-size: 0.85em;
|
|
209
|
+
`;
|
|
210
|
+
const metaPre = css`
|
|
211
|
+
/* meta-pre */
|
|
212
|
+
${metaMono}
|
|
213
|
+
white-space: pre-wrap;
|
|
214
|
+
word-break: break-word;
|
|
215
|
+
background: var(--surface-card);
|
|
216
|
+
border: 1px solid var(--border);
|
|
217
|
+
border-radius: var(--radius-sm);
|
|
218
|
+
padding: 0.75rem;
|
|
219
|
+
font-size: 0.8rem;
|
|
220
|
+
overflow: auto;
|
|
221
|
+
`;
|
|
222
|
+
const metaDanger = css`
|
|
223
|
+
/* meta-danger */
|
|
224
|
+
${metaText}
|
|
225
|
+
color: var(--status-rejected);
|
|
226
|
+
`;
|
|
227
|
+
const metaCenter = css`
|
|
228
|
+
/* meta-center */
|
|
229
|
+
${metaText}
|
|
230
|
+
text-align: center;
|
|
231
|
+
`;
|
|
232
|
+
/** Small status pill with a color tone. */
|
|
233
|
+
const Badge = ({ tone = "neutral", icon = false, children }) => {
|
|
234
|
+
return /* @__PURE__ */ jsxs("span", {
|
|
235
|
+
class: badgeTones[tone],
|
|
236
|
+
children: [icon ? /* @__PURE__ */ jsx(Icon, {
|
|
237
|
+
name: toneIcons[tone],
|
|
238
|
+
size: "sm"
|
|
239
|
+
}) : null, children]
|
|
240
|
+
});
|
|
241
|
+
};
|
|
242
|
+
/** Map a build or snapshot status string to its badge tone. */
|
|
243
|
+
function statusTone(status) {
|
|
244
|
+
if (isSuccessStatus(status)) return "success";
|
|
245
|
+
if (isDangerStatus(status)) return "danger";
|
|
246
|
+
if (isWarningStatus(status)) return "warning";
|
|
247
|
+
if (isInfoStatus(status)) return "info";
|
|
248
|
+
return "neutral";
|
|
249
|
+
}
|
|
250
|
+
function isSuccessStatus(status) {
|
|
251
|
+
return status === "approved" || status === "unchanged" || status === "completed";
|
|
252
|
+
}
|
|
253
|
+
function isDangerStatus(status) {
|
|
254
|
+
return status === "rejected" || status === "failed";
|
|
255
|
+
}
|
|
256
|
+
function isWarningStatus(status) {
|
|
257
|
+
return status === "changed" || status === "new" || status === "reviewing";
|
|
258
|
+
}
|
|
259
|
+
function isInfoStatus(status) {
|
|
260
|
+
return status === "capturing" || status === "comparing" || status === "pending" || status === "queued" || status === "running";
|
|
261
|
+
}
|
|
262
|
+
/** Alert banner with an optional title. */
|
|
263
|
+
const Alert = ({ tone = "info", title, children }) => {
|
|
264
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
265
|
+
class: alertTones[tone],
|
|
266
|
+
role: "alert",
|
|
267
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
268
|
+
class: alertIconTones[tone],
|
|
269
|
+
children: /* @__PURE__ */ jsx(Icon, { name: toneIcons[tone] })
|
|
270
|
+
}), /* @__PURE__ */ jsxs("div", { children: [title ? /* @__PURE__ */ jsx("strong", {
|
|
271
|
+
class: alertTitle,
|
|
272
|
+
children: title
|
|
273
|
+
}) : null, /* @__PURE__ */ jsx("div", { children })] })]
|
|
274
|
+
});
|
|
275
|
+
};
|
|
276
|
+
const alertTitle = css`
|
|
277
|
+
/* alert-title */
|
|
278
|
+
display: block;
|
|
279
|
+
margin-bottom: 0.2rem;
|
|
280
|
+
font-weight: 650;
|
|
281
|
+
`;
|
|
282
|
+
/** Centered empty state with an optional title and call-to-action. */ const EmptyState = ({ title, description, action }) => {
|
|
283
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
284
|
+
class: emptyWrap,
|
|
285
|
+
children: [
|
|
286
|
+
title ? /* @__PURE__ */ jsx("h2", {
|
|
287
|
+
class: emptyTitle,
|
|
288
|
+
children: title
|
|
289
|
+
}) : null,
|
|
290
|
+
description ? /* @__PURE__ */ jsx("p", {
|
|
291
|
+
class: emptyDesc,
|
|
292
|
+
children: description
|
|
293
|
+
}) : null,
|
|
294
|
+
action ? /* @__PURE__ */ jsx("div", {
|
|
295
|
+
class: emptyAction,
|
|
296
|
+
children: action
|
|
297
|
+
}) : null
|
|
298
|
+
]
|
|
299
|
+
});
|
|
300
|
+
};
|
|
301
|
+
/** Centered statistic value with a label and an optional value tone. */
|
|
302
|
+
const Stat = ({ label, value, tone = "neutral" }) => {
|
|
303
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
304
|
+
class: statWrap,
|
|
305
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
306
|
+
class: statValueTones[tone],
|
|
307
|
+
children: String(value)
|
|
308
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
309
|
+
class: statLabel,
|
|
310
|
+
children: label
|
|
311
|
+
})]
|
|
312
|
+
});
|
|
313
|
+
};
|
|
314
|
+
/** Muted secondary text for metadata and descriptions (not a form hint). */
|
|
315
|
+
const Meta = ({ as = "p", mono = false, tone = "neutral", center = false, children }) => {
|
|
316
|
+
const cls = as === "pre" ? metaPre : as === "code" ? metaCode : tone === "danger" ? metaDanger : center ? metaCenter : mono ? metaMono : metaText;
|
|
317
|
+
if (as === "span") return /* @__PURE__ */ jsx("span", {
|
|
318
|
+
class: cls,
|
|
319
|
+
children
|
|
320
|
+
});
|
|
321
|
+
if (as === "div") return /* @__PURE__ */ jsx("div", {
|
|
322
|
+
class: cls,
|
|
323
|
+
children
|
|
324
|
+
});
|
|
325
|
+
if (as === "code") return /* @__PURE__ */ jsx("code", {
|
|
326
|
+
class: cls,
|
|
327
|
+
children
|
|
328
|
+
});
|
|
329
|
+
if (as === "pre") return /* @__PURE__ */ jsx("pre", {
|
|
330
|
+
class: cls,
|
|
331
|
+
children
|
|
332
|
+
});
|
|
333
|
+
return /* @__PURE__ */ jsx("p", {
|
|
334
|
+
class: cls,
|
|
335
|
+
children
|
|
336
|
+
});
|
|
337
|
+
};
|
|
338
|
+
//#endregion
|
|
339
|
+
export { Alert, Badge, EmptyState, Meta, Stat, statusTone };
|
|
340
|
+
|
|
341
|
+
//# sourceMappingURL=feedback.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feedback.mjs","names":[],"sources":["../../src/ui/feedback.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\ntype BadgeTone = \"neutral\" | \"success\" | \"warning\" | \"danger\" | \"info\";\ntype StatTone = \"neutral\" | \"success\" | \"warning\";\n\nconst badgeBase = css`\n /* badge */\n display: inline-flex;\n align-items: center;\n gap: 0.3rem;\n padding: 0.1rem 0.55rem;\n border-radius: var(--radius-pill);\n font-size: var(--text-xs);\n font-weight: 600;\n line-height: 1.5;\n border: 1px solid var(--border);\n background: var(--surface-muted);\n color: var(--text-secondary);\n white-space: nowrap;\n`;\n\nconst badgeTones: Record<BadgeTone, Promise<string>> = {\n neutral: css`\n /* badge-neutral */\n ${badgeBase}\n `,\n success: css`\n /* badge-success */\n ${badgeBase}\n background: var(--status-approved-bg);\n color: var(--status-approved-fg);\n border-color: var(--status-approved-border);\n `,\n warning: css`\n /* badge-warning */\n ${badgeBase}\n background: var(--status-new-bg);\n color: var(--status-new-fg);\n border-color: var(--status-new-border);\n `,\n danger: css`\n /* badge-danger */\n ${badgeBase}\n background: var(--status-rejected-bg);\n color: var(--status-rejected-fg);\n border-color: var(--status-rejected-border);\n `,\n info: css`\n /* badge-info */\n ${badgeBase}\n background: var(--status-info-bg);\n color: var(--accent-fg);\n border-color: var(--status-info-border);\n `,\n};\n\n/** Status glyph per tone so state never relies on color alone. */\nconst toneIcons: Record<BadgeTone, IconName> = {\n neutral: \"info\",\n success: \"check-circle\",\n warning: \"alert-triangle\",\n danger: \"x-circle\",\n info: \"info\",\n};\n\nconst alertBase = css`\n /* alert */\n display: flex;\n gap: 0.6rem;\n align-items: flex-start;\n border-radius: var(--radius);\n padding: 0.75rem 0.9rem;\n border: 1px solid var(--border);\n background: var(--surface-card);\n font-size: var(--text-base);\n`;\n\nconst alertTones: Record<BadgeTone, Promise<string>> = {\n neutral: css`\n /* alert-neutral */\n ${alertBase}\n `,\n info: css`\n /* alert-info */\n ${alertBase}\n border-color: var(--status-info-border);\n background: var(--status-info-bg);\n `,\n success: css`\n /* alert-success */\n ${alertBase}\n border-color: var(--status-approved-border);\n background: var(--status-approved-bg);\n `,\n warning: css`\n /* alert-warning */\n ${alertBase}\n border-color: var(--status-new-border);\n background: var(--status-new-bg);\n `,\n danger: css`\n /* alert-danger */\n ${alertBase}\n border-color: var(--status-rejected-border);\n background: var(--status-rejected-bg);\n `,\n};\n\nconst alertIconTones: Record<BadgeTone, Promise<string>> = {\n neutral: css`\n /* alert-icon-neutral */\n margin-top: 0.1rem;\n color: var(--text-secondary);\n `,\n info: css`\n /* alert-icon-info */\n margin-top: 0.1rem;\n color: var(--accent-fg);\n `,\n success: css`\n /* alert-icon-success */\n margin-top: 0.1rem;\n color: var(--status-approved);\n `,\n warning: css`\n /* alert-icon-warning */\n margin-top: 0.1rem;\n color: var(--status-new);\n `,\n danger: css`\n /* alert-icon-danger */\n margin-top: 0.1rem;\n color: var(--status-rejected);\n `,\n};\n\nconst emptyWrap = css`\n /* empty */\n text-align: center;\n padding: 2rem 1rem;\n border: 1px dashed var(--border);\n border-radius: var(--radius);\n background: var(--surface-card);\n`;\n\nconst emptyTitle = css`\n /* empty-title */\n margin: 0 0 0.3rem;\n font-size: 1.05rem;\n font-weight: 650;\n letter-spacing: -0.01em;\n`;\n\nconst emptyDesc = css`\n /* empty-desc */\n margin: 0 auto;\n color: var(--text-secondary);\n font-size: 0.875rem;\n max-width: 50ch;\n`;\n\nconst emptyAction = css`\n /* empty-action */\n margin-top: 1rem;\n`;\n\nconst statWrap = css`\n /* stat */\n text-align: center;\n padding: 0.5rem;\n`;\n\nconst statValueBase = css`\n /* stat-value */\n font-size: 1.35rem;\n font-weight: 700;\n line-height: 1.1;\n font-variant-numeric: tabular-nums;\n`;\n\nconst statValueTones: Record<StatTone, Promise<string>> = {\n neutral: css`\n /* stat-value-neutral */\n ${statValueBase}\n `,\n success: css`\n /* stat-value-success */\n ${statValueBase}\n color: var(--status-approved);\n `,\n warning: css`\n /* stat-value-warning */\n ${statValueBase}\n color: var(--status-new);\n `,\n};\n\nconst statLabel = css`\n /* stat-label */\n color: var(--text-secondary);\n font-size: 0.72rem;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n`;\n\nconst metaText = css`\n /* meta */\n margin: 0;\n color: var(--text-secondary);\n font-size: 0.82rem;\n`;\n\nconst metaMono = css`\n /* meta-mono */\n ${metaText}\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n`;\n\nconst metaCode = css`\n /* meta-code */\n ${metaMono}\n font-size: 0.85em;\n`;\n\nconst metaPre = css`\n /* meta-pre */\n ${metaMono}\n white-space: pre-wrap;\n word-break: break-word;\n background: var(--surface-card);\n border: 1px solid var(--border);\n border-radius: var(--radius-sm);\n padding: 0.75rem;\n font-size: 0.8rem;\n overflow: auto;\n`;\n\nconst metaDanger = css`\n /* meta-danger */\n ${metaText}\n color: var(--status-rejected);\n`;\n\nconst metaCenter = css`\n /* meta-center */\n ${metaText}\n text-align: center;\n`;\n\ntype MetaAs = \"p\" | \"span\" | \"div\" | \"code\" | \"pre\";\ntype MetaTone = \"neutral\" | \"danger\";\n\n/** Small status pill with a color tone. */\n// eslint-disable-next-line promise-function-async -- JSX component return type\nexport const Badge: FC<{ tone?: BadgeTone; icon?: boolean; children?: unknown }> = ({\n tone = \"neutral\",\n icon = false,\n children,\n}) => {\n return (\n <span class={badgeTones[tone]}>\n {icon ? <Icon name={toneIcons[tone]} size=\"sm\" /> : null}\n {children}\n </span>\n );\n};\n\n/** Map a build or snapshot status string to its badge tone. */\nexport function statusTone(status: string): BadgeTone {\n if (isSuccessStatus(status)) {\n return \"success\";\n }\n if (isDangerStatus(status)) {\n return \"danger\";\n }\n if (isWarningStatus(status)) {\n return \"warning\";\n }\n if (isInfoStatus(status)) {\n return \"info\";\n }\n return \"neutral\";\n}\n\nfunction isSuccessStatus(status: string): boolean {\n return status === \"approved\" || status === \"unchanged\" || status === \"completed\";\n}\n\nfunction isDangerStatus(status: string): boolean {\n return status === \"rejected\" || status === \"failed\";\n}\n\nfunction isWarningStatus(status: string): boolean {\n return status === \"changed\" || status === \"new\" || status === \"reviewing\";\n}\n\nfunction isInfoStatus(status: string): boolean {\n return (\n status === \"capturing\" ||\n status === \"comparing\" ||\n status === \"pending\" ||\n status === \"queued\" ||\n status === \"running\"\n );\n}\n\n/** Alert banner with an optional title. */\n// eslint-disable-next-line promise-function-async -- JSX component return type\nexport const Alert: FC<{ tone?: BadgeTone; title?: string; children?: unknown }> = ({\n tone = \"info\",\n title,\n children,\n}) => {\n return (\n <div class={alertTones[tone]} role=\"alert\">\n <span class={alertIconTones[tone]}>\n <Icon name={toneIcons[tone]} />\n </span>\n <div>\n {title ? <strong class={alertTitle}>{title}</strong> : null}\n <div>{children}</div>\n </div>\n </div>\n );\n};\n\nconst alertTitle = css`\n /* alert-title */\n display: block;\n margin-bottom: 0.2rem;\n font-weight: 650;\n`;\n\n/** Centered empty state with an optional title and call-to-action. */ // eslint-disable-next-line promise-function-async -- JSX component return type\nexport const EmptyState: FC<{ title?: string; description?: string; action?: unknown }> = ({\n title,\n description,\n action,\n}) => {\n return (\n <div class={emptyWrap}>\n {title ? <h2 class={emptyTitle}>{title}</h2> : null}\n {description ? <p class={emptyDesc}>{description}</p> : null}\n {action ? <div class={emptyAction}>{action}</div> : null}\n </div>\n );\n};\n\n/** Centered statistic value with a label and an optional value tone. */\n// eslint-disable-next-line promise-function-async -- JSX component return type\nexport const Stat: FC<{ label: string; value: string | number; tone?: StatTone }> = ({\n label,\n value,\n tone = \"neutral\",\n}) => {\n return (\n <div class={statWrap}>\n <div class={statValueTones[tone]}>{String(value)}</div>\n <div class={statLabel}>{label}</div>\n </div>\n );\n};\n\n/** Muted secondary text for metadata and descriptions (not a form hint). */\n// eslint-disable-next-line promise-function-async -- JSX component return type\nexport const Meta: FC<{\n as?: MetaAs;\n mono?: boolean;\n tone?: MetaTone;\n center?: boolean;\n children?: unknown;\n}> = ({ as = \"p\", mono = false, tone = \"neutral\", center = false, children }) => {\n const cls =\n as === \"pre\"\n ? metaPre\n : as === \"code\"\n ? metaCode\n : tone === \"danger\"\n ? metaDanger\n : center\n ? metaCenter\n : mono\n ? metaMono\n : metaText;\n if (as === \"span\") {\n return <span class={cls}>{children}</span>;\n }\n if (as === \"div\") {\n return <div class={cls}>{children}</div>;\n }\n if (as === \"code\") {\n return <code class={cls}>{children}</code>;\n }\n if (as === \"pre\") {\n return <pre class={cls}>{children}</pre>;\n }\n return <p class={cls}>{children}</p>;\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
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/filter-input.tsx
|
|
8
|
+
const filterWrap = css`
|
|
9
|
+
/* filter-input */
|
|
10
|
+
position: relative;
|
|
11
|
+
display: inline-flex;
|
|
12
|
+
align-items: center;
|
|
13
|
+
min-width: min(100%, 260px);
|
|
14
|
+
& svg {
|
|
15
|
+
position: absolute;
|
|
16
|
+
left: 0.65rem;
|
|
17
|
+
color: var(--text-muted);
|
|
18
|
+
pointer-events: none;
|
|
19
|
+
}
|
|
20
|
+
& input {
|
|
21
|
+
width: 100%;
|
|
22
|
+
min-height: 36px;
|
|
23
|
+
padding: 0.35rem 0.7rem 0.35rem 2rem;
|
|
24
|
+
background: var(--surface-card);
|
|
25
|
+
color: var(--text-primary);
|
|
26
|
+
border: 1px solid var(--border);
|
|
27
|
+
border-radius: var(--radius);
|
|
28
|
+
font: inherit;
|
|
29
|
+
font-size: var(--text-base);
|
|
30
|
+
}
|
|
31
|
+
& input::placeholder {
|
|
32
|
+
color: var(--text-muted);
|
|
33
|
+
}
|
|
34
|
+
`;
|
|
35
|
+
/**
|
|
36
|
+
* Client-side filter box. Matches its text against `data-filter-text` on
|
|
37
|
+
* elements marked `data-filter-item` inside the nearest `[data-filter-scope]`
|
|
38
|
+
* (or the whole page). Pair with a `data-filter-empty` element to show a
|
|
39
|
+
* "no matches" message.
|
|
40
|
+
*/
|
|
41
|
+
const FilterInput = ({ label, placeholder = "Filter…" }) => {
|
|
42
|
+
return /* @__PURE__ */ jsxs("label", {
|
|
43
|
+
class: filterWrap,
|
|
44
|
+
children: [/* @__PURE__ */ jsx(Icon, {
|
|
45
|
+
name: "search",
|
|
46
|
+
size: "sm"
|
|
47
|
+
}), /* @__PURE__ */ jsx("input", {
|
|
48
|
+
type: "search",
|
|
49
|
+
"data-filter-input": true,
|
|
50
|
+
"aria-label": label,
|
|
51
|
+
placeholder
|
|
52
|
+
})]
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
//#endregion
|
|
56
|
+
export { FilterInput };
|
|
57
|
+
|
|
58
|
+
//# sourceMappingURL=filter-input.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter-input.mjs","names":[],"sources":["../../src/ui/filter-input.tsx"],"sourcesContent":["import type { FC } from \"hono/jsx\";\nimport { css } from \"./css.ts\";\nimport { Icon } from \"./icons/icon.tsx\";\n\n/* eslint-disable promise-function-async -- Hono JSX components return HtmlEscapedString | Promise<HtmlEscapedString> */\n\nconst filterWrap = css`\n /* filter-input */\n position: relative;\n display: inline-flex;\n align-items: center;\n min-width: min(100%, 260px);\n & svg {\n position: absolute;\n left: 0.65rem;\n color: var(--text-muted);\n pointer-events: none;\n }\n & input {\n width: 100%;\n min-height: 36px;\n padding: 0.35rem 0.7rem 0.35rem 2rem;\n background: var(--surface-card);\n color: var(--text-primary);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n font: inherit;\n font-size: var(--text-base);\n }\n & input::placeholder {\n color: var(--text-muted);\n }\n`;\n\n/**\n * Client-side filter box. Matches its text against `data-filter-text` on\n * elements marked `data-filter-item` inside the nearest `[data-filter-scope]`\n * (or the whole page). Pair with a `data-filter-empty` element to show a\n * \"no matches\" message.\n */\nexport const FilterInput: FC<{ label: string; placeholder?: string }> = ({\n label,\n placeholder = \"Filter…\",\n}) => {\n return (\n <label class={filterWrap}>\n <Icon name=\"search\" size=\"sm\" />\n <input type=\"search\" data-filter-input aria-label={label} placeholder={placeholder} />\n </label>\n );\n};\n"],"mappings":";;;;;;;AAMA,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCtB,MAAa,eAA4D,EACvE,OACA,cAAc,gBACV;CACJ,OACE,qBAAC,SAAD;EAAO,OAAO;EAAd,UAAA,CACE,oBAAC,MAAD;GAAM,MAAK;GAAS,MAAK;EAAM,CAAA,GAC/B,oBAAC,SAAD;GAAO,MAAK;GAAS,qBAAA;GAAkB,cAAY;GAAoB;EAAc,CAAA,CAChF;;AAEX"}
|