@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,205 @@
|
|
|
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 { Fragment, jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
7
|
+
//#region src/ui/buttons.tsx
|
|
8
|
+
const btnBase = css`
|
|
9
|
+
/* btn */
|
|
10
|
+
appearance: none;
|
|
11
|
+
border: 1px solid transparent;
|
|
12
|
+
border-radius: var(--radius);
|
|
13
|
+
padding: 0.55rem 0.9rem;
|
|
14
|
+
font-weight: 600;
|
|
15
|
+
font-size: var(--text-base);
|
|
16
|
+
cursor: pointer;
|
|
17
|
+
text-decoration: none;
|
|
18
|
+
display: inline-flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
justify-content: center;
|
|
21
|
+
gap: 0.4rem;
|
|
22
|
+
line-height: 1.2;
|
|
23
|
+
/* Top-aligned so a button in a <form> (a block with a line box) is exactly
|
|
24
|
+
as tall as the button instead of picking up a baseline strut. */
|
|
25
|
+
vertical-align: top;
|
|
26
|
+
min-height: 40px;
|
|
27
|
+
transition:
|
|
28
|
+
background var(--dur-fast) var(--ease),
|
|
29
|
+
border-color var(--dur-fast) var(--ease),
|
|
30
|
+
filter var(--dur-fast) var(--ease),
|
|
31
|
+
transform var(--dur-fast) var(--ease);
|
|
32
|
+
&:active:not(:disabled) {
|
|
33
|
+
transform: translateY(1px);
|
|
34
|
+
}
|
|
35
|
+
&:disabled {
|
|
36
|
+
opacity: 0.55;
|
|
37
|
+
cursor: not-allowed;
|
|
38
|
+
}
|
|
39
|
+
`;
|
|
40
|
+
const btnPrimary = css`
|
|
41
|
+
/* btn-primary */
|
|
42
|
+
${btnBase}
|
|
43
|
+
background: var(--accent);
|
|
44
|
+
color: var(--accent-contrast);
|
|
45
|
+
border-color: var(--accent);
|
|
46
|
+
box-shadow: var(--shadow);
|
|
47
|
+
&:hover {
|
|
48
|
+
filter: brightness(1.06);
|
|
49
|
+
text-decoration: none;
|
|
50
|
+
}
|
|
51
|
+
`;
|
|
52
|
+
const btnPrimarySm = css`
|
|
53
|
+
/* btn-primary-sm */
|
|
54
|
+
${btnPrimary}
|
|
55
|
+
padding: 0.35rem 0.6rem;
|
|
56
|
+
font-size: 0.8rem;
|
|
57
|
+
min-height: 32px;
|
|
58
|
+
`;
|
|
59
|
+
const btnSecondary = css`
|
|
60
|
+
/* btn-secondary */
|
|
61
|
+
${btnBase}
|
|
62
|
+
background: var(--surface-card);
|
|
63
|
+
color: var(--text-primary);
|
|
64
|
+
border-color: var(--border);
|
|
65
|
+
box-shadow: var(--shadow);
|
|
66
|
+
&:hover {
|
|
67
|
+
background: var(--surface-subtle);
|
|
68
|
+
text-decoration: none;
|
|
69
|
+
}
|
|
70
|
+
`;
|
|
71
|
+
const btnSecondarySm = css`
|
|
72
|
+
/* btn-secondary-sm */
|
|
73
|
+
${btnSecondary}
|
|
74
|
+
padding: 0.35rem 0.6rem;
|
|
75
|
+
font-size: 0.8rem;
|
|
76
|
+
min-height: 32px;
|
|
77
|
+
`;
|
|
78
|
+
const btnOutline = css`
|
|
79
|
+
/* btn-outline */
|
|
80
|
+
${btnSecondary}
|
|
81
|
+
&:hover {
|
|
82
|
+
background: var(--surface-subtle);
|
|
83
|
+
text-decoration: none;
|
|
84
|
+
}
|
|
85
|
+
`;
|
|
86
|
+
const btnOutlineSm = css`
|
|
87
|
+
/* btn-outline-sm */
|
|
88
|
+
${btnOutline}
|
|
89
|
+
padding: 0.35rem 0.6rem;
|
|
90
|
+
font-size: 0.8rem;
|
|
91
|
+
min-height: 32px;
|
|
92
|
+
`;
|
|
93
|
+
const btnGhost = css`
|
|
94
|
+
/* btn-ghost */
|
|
95
|
+
${btnBase}
|
|
96
|
+
background: transparent;
|
|
97
|
+
color: var(--text-secondary);
|
|
98
|
+
border-color: transparent;
|
|
99
|
+
&:hover {
|
|
100
|
+
background: var(--surface-muted);
|
|
101
|
+
color: var(--text-primary);
|
|
102
|
+
text-decoration: none;
|
|
103
|
+
}
|
|
104
|
+
`;
|
|
105
|
+
const btnGhostSm = css`
|
|
106
|
+
/* btn-ghost-sm */
|
|
107
|
+
${btnGhost}
|
|
108
|
+
padding: 0.35rem 0.6rem;
|
|
109
|
+
font-size: 0.8rem;
|
|
110
|
+
min-height: 32px;
|
|
111
|
+
`;
|
|
112
|
+
const btnDanger = css`
|
|
113
|
+
/* btn-danger */
|
|
114
|
+
${btnBase}
|
|
115
|
+
background: color-mix(in srgb, var(--status-rejected) 82%, #000);
|
|
116
|
+
color: #fff;
|
|
117
|
+
border-color: color-mix(in srgb, var(--status-rejected) 82%, #000);
|
|
118
|
+
&:hover {
|
|
119
|
+
filter: brightness(1.06);
|
|
120
|
+
text-decoration: none;
|
|
121
|
+
}
|
|
122
|
+
`;
|
|
123
|
+
const btnDangerSm = css`
|
|
124
|
+
/* btn-danger-sm */
|
|
125
|
+
${btnDanger}
|
|
126
|
+
padding: 0.35rem 0.6rem;
|
|
127
|
+
font-size: 0.8rem;
|
|
128
|
+
min-height: 32px;
|
|
129
|
+
`;
|
|
130
|
+
const btnClasses = {
|
|
131
|
+
primary: {
|
|
132
|
+
md: btnPrimary,
|
|
133
|
+
sm: btnPrimarySm
|
|
134
|
+
},
|
|
135
|
+
secondary: {
|
|
136
|
+
md: btnSecondary,
|
|
137
|
+
sm: btnSecondarySm
|
|
138
|
+
},
|
|
139
|
+
outline: {
|
|
140
|
+
md: btnOutline,
|
|
141
|
+
sm: btnOutlineSm
|
|
142
|
+
},
|
|
143
|
+
ghost: {
|
|
144
|
+
md: btnGhost,
|
|
145
|
+
sm: btnGhostSm
|
|
146
|
+
},
|
|
147
|
+
danger: {
|
|
148
|
+
md: btnDanger,
|
|
149
|
+
sm: btnDangerSm
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
css`
|
|
153
|
+
/* tabs */
|
|
154
|
+
display: flex;
|
|
155
|
+
gap: 0.25rem;
|
|
156
|
+
border-bottom: 1px solid var(--border);
|
|
157
|
+
margin-bottom: 1rem;
|
|
158
|
+
overflow-x: auto;
|
|
159
|
+
`;
|
|
160
|
+
const tabsLink = css`
|
|
161
|
+
/* tabs-link */
|
|
162
|
+
padding: 0.55rem 0.75rem;
|
|
163
|
+
border-bottom: 2px solid transparent;
|
|
164
|
+
color: var(--text-secondary);
|
|
165
|
+
font-size: 0.875rem;
|
|
166
|
+
text-decoration: none;
|
|
167
|
+
white-space: nowrap;
|
|
168
|
+
font-weight: 600;
|
|
169
|
+
&:hover {
|
|
170
|
+
color: var(--text-primary);
|
|
171
|
+
text-decoration: none;
|
|
172
|
+
}
|
|
173
|
+
`;
|
|
174
|
+
css`
|
|
175
|
+
/* tabs-link-active */
|
|
176
|
+
${tabsLink}
|
|
177
|
+
color: var(--text-primary);
|
|
178
|
+
border-bottom-color: var(--accent);
|
|
179
|
+
`;
|
|
180
|
+
/** Button or link styled as a button (renders an anchor when `href` is set). */
|
|
181
|
+
const Button = ({ variant = "primary", size = "md", type = "button", href, disabled, icon, children, ...rest }) => {
|
|
182
|
+
const cls = btnClasses[variant][size];
|
|
183
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [icon ? /* @__PURE__ */ jsx(Icon, {
|
|
184
|
+
name: icon,
|
|
185
|
+
size: size === "sm" ? "sm" : "md"
|
|
186
|
+
}) : null, children] });
|
|
187
|
+
if (href) return /* @__PURE__ */ jsx("a", {
|
|
188
|
+
class: cls,
|
|
189
|
+
href,
|
|
190
|
+
"aria-disabled": disabled ? "true" : void 0,
|
|
191
|
+
...rest,
|
|
192
|
+
children: content
|
|
193
|
+
});
|
|
194
|
+
return /* @__PURE__ */ jsx("button", {
|
|
195
|
+
class: cls,
|
|
196
|
+
type,
|
|
197
|
+
disabled,
|
|
198
|
+
...rest,
|
|
199
|
+
children: content
|
|
200
|
+
});
|
|
201
|
+
};
|
|
202
|
+
//#endregion
|
|
203
|
+
export { Button };
|
|
204
|
+
|
|
205
|
+
//# sourceMappingURL=buttons.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buttons.mjs","names":[],"sources":["../../src/ui/buttons.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 ButtonVariant = \"primary\" | \"secondary\" | \"ghost\" | \"danger\" | \"outline\";\ntype ButtonSize = \"md\" | \"sm\";\n\nconst btnBase = css`\n /* btn */\n appearance: none;\n border: 1px solid transparent;\n border-radius: var(--radius);\n padding: 0.55rem 0.9rem;\n font-weight: 600;\n font-size: var(--text-base);\n cursor: pointer;\n text-decoration: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.4rem;\n line-height: 1.2;\n /* Top-aligned so a button in a <form> (a block with a line box) is exactly\n as tall as the button instead of picking up a baseline strut. */\n vertical-align: top;\n min-height: 40px;\n transition:\n background var(--dur-fast) var(--ease),\n border-color var(--dur-fast) var(--ease),\n filter var(--dur-fast) var(--ease),\n transform var(--dur-fast) var(--ease);\n &:active:not(:disabled) {\n transform: translateY(1px);\n }\n &:disabled {\n opacity: 0.55;\n cursor: not-allowed;\n }\n`;\n\nconst btnPrimary = css`\n /* btn-primary */\n ${btnBase}\n background: var(--accent);\n color: var(--accent-contrast);\n border-color: var(--accent);\n box-shadow: var(--shadow);\n &:hover {\n filter: brightness(1.06);\n text-decoration: none;\n }\n`;\n\nconst btnPrimarySm = css`\n /* btn-primary-sm */\n ${btnPrimary}\n padding: 0.35rem 0.6rem;\n font-size: 0.8rem;\n min-height: 32px;\n`;\n\nconst btnSecondary = css`\n /* btn-secondary */\n ${btnBase}\n background: var(--surface-card);\n color: var(--text-primary);\n border-color: var(--border);\n box-shadow: var(--shadow);\n &:hover {\n background: var(--surface-subtle);\n text-decoration: none;\n }\n`;\n\nconst btnSecondarySm = css`\n /* btn-secondary-sm */\n ${btnSecondary}\n padding: 0.35rem 0.6rem;\n font-size: 0.8rem;\n min-height: 32px;\n`;\n\nconst btnOutline = css`\n /* btn-outline */\n ${btnSecondary}\n &:hover {\n background: var(--surface-subtle);\n text-decoration: none;\n }\n`;\n\nconst btnOutlineSm = css`\n /* btn-outline-sm */\n ${btnOutline}\n padding: 0.35rem 0.6rem;\n font-size: 0.8rem;\n min-height: 32px;\n`;\n\nconst btnGhost = css`\n /* btn-ghost */\n ${btnBase}\n background: transparent;\n color: var(--text-secondary);\n border-color: transparent;\n &:hover {\n background: var(--surface-muted);\n color: var(--text-primary);\n text-decoration: none;\n }\n`;\n\nconst btnGhostSm = css`\n /* btn-ghost-sm */\n ${btnGhost}\n padding: 0.35rem 0.6rem;\n font-size: 0.8rem;\n min-height: 32px;\n`;\n\nconst btnDanger = css`\n /* btn-danger */\n ${btnBase}\n background: color-mix(in srgb, var(--status-rejected) 82%, #000);\n color: #fff;\n border-color: color-mix(in srgb, var(--status-rejected) 82%, #000);\n &:hover {\n filter: brightness(1.06);\n text-decoration: none;\n }\n`;\n\nconst btnDangerSm = css`\n /* btn-danger-sm */\n ${btnDanger}\n padding: 0.35rem 0.6rem;\n font-size: 0.8rem;\n min-height: 32px;\n`;\n\nconst btnClasses: Record<ButtonVariant, Record<ButtonSize, Promise<string>>> = {\n primary: { md: btnPrimary, sm: btnPrimarySm },\n secondary: { md: btnSecondary, sm: btnSecondarySm },\n outline: { md: btnOutline, sm: btnOutlineSm },\n ghost: { md: btnGhost, sm: btnGhostSm },\n danger: { md: btnDanger, sm: btnDangerSm },\n};\n\nconst tabsNav = css`\n /* tabs */\n display: flex;\n gap: 0.25rem;\n border-bottom: 1px solid var(--border);\n margin-bottom: 1rem;\n overflow-x: auto;\n`;\n\nconst tabsLink = css`\n /* tabs-link */\n padding: 0.55rem 0.75rem;\n border-bottom: 2px solid transparent;\n color: var(--text-secondary);\n font-size: 0.875rem;\n text-decoration: none;\n white-space: nowrap;\n font-weight: 600;\n &:hover {\n color: var(--text-primary);\n text-decoration: none;\n }\n`;\n\nconst tabsLinkActive = css`\n /* tabs-link-active */\n ${tabsLink}\n color: var(--text-primary);\n border-bottom-color: var(--accent);\n`;\n\n/** Button or link styled as a button (renders an anchor when `href` is set). */\nexport const Button: FC<{\n variant?: ButtonVariant;\n size?: ButtonSize;\n type?: \"button\" | \"submit\" | \"reset\";\n href?: string;\n disabled?: boolean;\n /** Leading icon from the sprite. */\n icon?: IconName;\n children?: unknown;\n [key: string]: unknown;\n}> = ({\n variant = \"primary\",\n size = \"md\",\n type = \"button\",\n href,\n disabled,\n icon,\n children,\n ...rest\n}) => {\n const cls = btnClasses[variant][size];\n const content = (\n <>\n {icon ? <Icon name={icon} size={size === \"sm\" ? \"sm\" : \"md\"} /> : null}\n {children}\n </>\n );\n if (href) {\n return (\n <a class={cls} href={href} aria-disabled={disabled ? \"true\" : undefined} {...rest}>\n {content}\n </a>\n );\n }\n return (\n <button class={cls} type={type} disabled={disabled} {...rest}>\n {content}\n </button>\n );\n};\n\n/** Tab navigation bar for section switching. */\n// eslint-disable-next-line promise-function-async -- JSX component return type\nexport const Tabs: FC<{\n tabs: { label: string; href: string; active?: boolean }[];\n label?: string;\n}> = ({ tabs, label = \"Sections\" }) => {\n return (\n <nav class={tabsNav} aria-label={label}>\n {tabs.map((tab) => (\n <a\n class={tab.active ? tabsLinkActive : tabsLink}\n href={tab.href}\n aria-current={tab.active ? \"page\" : undefined}\n >\n {tab.label}\n </a>\n ))}\n </nav>\n );\n};\n"],"mappings":";;;;;;;AAUA,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCnB,MAAM,aAAa,GAAG;;IAElB,QAAQ;;;;;;;;;;AAWZ,MAAM,eAAe,GAAG;;IAEpB,WAAW;;;;;AAMf,MAAM,eAAe,GAAG;;IAEpB,QAAQ;;;;;;;;;;AAWZ,MAAM,iBAAiB,GAAG;;IAEtB,aAAa;;;;;AAMjB,MAAM,aAAa,GAAG;;IAElB,aAAa;;;;;;AAOjB,MAAM,eAAe,GAAG;;IAEpB,WAAW;;;;;AAMf,MAAM,WAAW,GAAG;;IAEhB,QAAQ;;;;;;;;;;AAWZ,MAAM,aAAa,GAAG;;IAElB,SAAS;;;;;AAMb,MAAM,YAAY,GAAG;;IAEjB,QAAQ;;;;;;;;;AAUZ,MAAM,cAAc,GAAG;;IAEnB,UAAU;;;;;AAMd,MAAM,aAAyE;CAC7E,SAAS;EAAE,IAAI;EAAY,IAAI;CAAa;CAC5C,WAAW;EAAE,IAAI;EAAc,IAAI;CAAe;CAClD,SAAS;EAAE,IAAI;EAAY,IAAI;CAAa;CAC5C,OAAO;EAAE,IAAI;EAAU,IAAI;CAAW;CACtC,QAAQ;EAAE,IAAI;EAAW,IAAI;CAAY;AAC3C;AAEgB,GAAG;;;;;;;;AASnB,MAAM,WAAW,GAAG;;;;;;;;;;;;;;AAeG,GAAG;;IAEtB,SAAS;;;;;AAMb,MAAa,UAUP,EACJ,UAAU,WACV,OAAO,MACP,OAAO,UACP,MACA,UACA,MACA,UACA,GAAG,WACC;CACJ,MAAM,MAAM,WAAW,QAAQ,CAAC;CAChC,MAAM,UACJ,qBAAA,UAAA,EAAA,UAAA,CACG,OAAO,oBAAC,MAAD;EAAM,MAAM;EAAM,MAAM,SAAS,OAAO,OAAO;CAAO,CAAA,IAAI,MACjE,QACD,EAAA,CAAA;CAEJ,IAAI,MACF,OACE,oBAAC,KAAD;EAAG,OAAO;EAAW;EAAM,iBAAe,WAAW,SAAS,KAAA;EAAW,GAAI;EAC1E,UAAA;CACA,CAAA;CAGP,OACE,oBAAC,UAAD;EAAQ,OAAO;EAAW;EAAgB;EAAU,GAAI;EACrD,UAAA;CACK,CAAA;AAEZ"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { css } from "./css.mjs";
|
|
5
|
+
import { Button } from "./buttons.mjs";
|
|
6
|
+
import { jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
7
|
+
//#region src/ui/code-block.tsx
|
|
8
|
+
const codeWrap = css`
|
|
9
|
+
/* code-block */
|
|
10
|
+
display: flex;
|
|
11
|
+
gap: var(--space-2);
|
|
12
|
+
align-items: flex-start;
|
|
13
|
+
justify-content: space-between;
|
|
14
|
+
padding: var(--space-2) var(--space-2) var(--space-2) var(--space-3);
|
|
15
|
+
background: var(--surface-subtle);
|
|
16
|
+
border: 1px solid var(--border);
|
|
17
|
+
border-radius: var(--radius);
|
|
18
|
+
& pre {
|
|
19
|
+
flex: 1;
|
|
20
|
+
min-width: 0;
|
|
21
|
+
margin: 0;
|
|
22
|
+
padding: 0.3rem 0;
|
|
23
|
+
overflow-x: auto;
|
|
24
|
+
font-family: var(--font-mono);
|
|
25
|
+
font-size: var(--text-sm);
|
|
26
|
+
line-height: var(--leading-normal);
|
|
27
|
+
white-space: pre;
|
|
28
|
+
}
|
|
29
|
+
`;
|
|
30
|
+
/** Monospace command/snippet with a copy button (wired by the shell script). */
|
|
31
|
+
const CodeBlock = ({ code, label = "Copy" }) => {
|
|
32
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
33
|
+
class: codeWrap,
|
|
34
|
+
children: [/* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { children: code }) }), /* @__PURE__ */ jsx(Button, {
|
|
35
|
+
variant: "ghost",
|
|
36
|
+
size: "sm",
|
|
37
|
+
icon: "copy",
|
|
38
|
+
"data-copy": code,
|
|
39
|
+
"aria-label": label,
|
|
40
|
+
children: label
|
|
41
|
+
})]
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
//#endregion
|
|
45
|
+
export { CodeBlock };
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=code-block.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-block.mjs","names":[],"sources":["../../src/ui/code-block.tsx"],"sourcesContent":["import type { FC } from \"hono/jsx\";\nimport { Button } from \"./buttons.tsx\";\nimport { css } from \"./css.ts\";\n\n/* eslint-disable promise-function-async -- Hono JSX components return HtmlEscapedString | Promise<HtmlEscapedString> */\n\nconst codeWrap = css`\n /* code-block */\n display: flex;\n gap: var(--space-2);\n align-items: flex-start;\n justify-content: space-between;\n padding: var(--space-2) var(--space-2) var(--space-2) var(--space-3);\n background: var(--surface-subtle);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n & pre {\n flex: 1;\n min-width: 0;\n margin: 0;\n padding: 0.3rem 0;\n overflow-x: auto;\n font-family: var(--font-mono);\n font-size: var(--text-sm);\n line-height: var(--leading-normal);\n white-space: pre;\n }\n`;\n\n/** Monospace command/snippet with a copy button (wired by the shell script). */\nexport const CodeBlock: FC<{ code: string; label?: string }> = ({ code, label = \"Copy\" }) => {\n return (\n <div class={codeWrap}>\n <pre>\n <code>{code}</code>\n </pre>\n <Button variant=\"ghost\" size=\"sm\" icon=\"copy\" data-copy={code} aria-label={label}>\n {label}\n </Button>\n </div>\n );\n};\n"],"mappings":";;;;;;;AAMA,MAAM,WAAW,GAAG;;;;;;;;;;;;;;;;;;;;;;;AAwBpB,MAAa,aAAmD,EAAE,MAAM,QAAQ,aAAa;CAC3F,OACE,qBAAC,OAAD;EAAK,OAAO;EAAZ,UAAA,CACE,oBAAC,OAAD,EAAA,UACE,oBAAC,QAAD,EAAA,UAAO,KAAW,CAAA,EACf,CAAA,GACL,oBAAC,QAAD;GAAQ,SAAQ;GAAQ,MAAK;GAAK,MAAK;GAAO,aAAW;GAAM,cAAY;GACxE,UAAA;EACK,CAAA,CACL;;AAET"}
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { css } from "./css.mjs";
|
|
5
|
+
import { Segmented } from "./segmented.mjs";
|
|
6
|
+
import { Fragment, jsx, jsxs } from "hono/jsx/jsx-runtime";
|
|
7
|
+
//#region src/ui/compare-stage.tsx
|
|
8
|
+
/**
|
|
9
|
+
* Screenshot comparison surface. All modes share one set of images; the
|
|
10
|
+
* `data-view` / `data-zoom` / `data-flip` attributes on the root (driven by
|
|
11
|
+
* the review script) decide what is shown. Without JS it stays side-by-side.
|
|
12
|
+
*/
|
|
13
|
+
const stageRoot = css`
|
|
14
|
+
/* compare-stage */
|
|
15
|
+
display: grid;
|
|
16
|
+
gap: var(--space-3);
|
|
17
|
+
.stage__bar {
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-wrap: wrap;
|
|
20
|
+
gap: var(--space-3);
|
|
21
|
+
align-items: center;
|
|
22
|
+
justify-content: space-between;
|
|
23
|
+
}
|
|
24
|
+
.stage__grid {
|
|
25
|
+
display: grid;
|
|
26
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
27
|
+
gap: var(--space-3);
|
|
28
|
+
align-items: start;
|
|
29
|
+
}
|
|
30
|
+
.stage__pane {
|
|
31
|
+
position: relative;
|
|
32
|
+
min-width: 0;
|
|
33
|
+
max-height: 78vh;
|
|
34
|
+
overflow: auto;
|
|
35
|
+
background: var(--surface-card);
|
|
36
|
+
border: 1px solid var(--border);
|
|
37
|
+
border-radius: var(--radius);
|
|
38
|
+
}
|
|
39
|
+
.stage__label {
|
|
40
|
+
position: sticky;
|
|
41
|
+
top: 0;
|
|
42
|
+
left: 0;
|
|
43
|
+
z-index: 2;
|
|
44
|
+
display: flex;
|
|
45
|
+
align-items: center;
|
|
46
|
+
justify-content: space-between;
|
|
47
|
+
padding: 0.35rem 0.6rem;
|
|
48
|
+
background: var(--surface-subtle);
|
|
49
|
+
border-bottom: 1px solid var(--border);
|
|
50
|
+
color: var(--text-secondary);
|
|
51
|
+
font-size: var(--text-xs);
|
|
52
|
+
font-weight: 600;
|
|
53
|
+
letter-spacing: 0.06em;
|
|
54
|
+
text-transform: uppercase;
|
|
55
|
+
}
|
|
56
|
+
.stage__img {
|
|
57
|
+
display: block;
|
|
58
|
+
width: 100%;
|
|
59
|
+
height: auto;
|
|
60
|
+
background: repeating-conic-gradient(var(--surface-muted) 0% 25%, var(--surface-subtle) 0% 50%)
|
|
61
|
+
0 0 / 16px 16px;
|
|
62
|
+
}
|
|
63
|
+
.stage__empty {
|
|
64
|
+
display: grid;
|
|
65
|
+
place-items: center;
|
|
66
|
+
aspect-ratio: 16 / 9;
|
|
67
|
+
padding: var(--space-4);
|
|
68
|
+
background: var(--surface-subtle);
|
|
69
|
+
color: var(--text-secondary);
|
|
70
|
+
font-size: var(--text-sm);
|
|
71
|
+
text-align: center;
|
|
72
|
+
}
|
|
73
|
+
.stage__divider,
|
|
74
|
+
.stage__controls {
|
|
75
|
+
display: none;
|
|
76
|
+
}
|
|
77
|
+
.stage__controls {
|
|
78
|
+
align-items: center;
|
|
79
|
+
gap: var(--space-3);
|
|
80
|
+
color: var(--text-secondary);
|
|
81
|
+
font-size: var(--text-sm);
|
|
82
|
+
}
|
|
83
|
+
.stage__controls input {
|
|
84
|
+
flex: 1;
|
|
85
|
+
accent-color: var(--accent);
|
|
86
|
+
}
|
|
87
|
+
&[data-zoom="100"] .stage__img {
|
|
88
|
+
width: auto;
|
|
89
|
+
max-width: none;
|
|
90
|
+
}
|
|
91
|
+
&[data-zoom="200"] .stage__img {
|
|
92
|
+
width: auto;
|
|
93
|
+
max-width: none;
|
|
94
|
+
zoom: 2;
|
|
95
|
+
}
|
|
96
|
+
&[data-zoom="400"] .stage__img {
|
|
97
|
+
width: auto;
|
|
98
|
+
max-width: none;
|
|
99
|
+
zoom: 4;
|
|
100
|
+
}
|
|
101
|
+
/* Zoomed in, show real pixels instead of a smoothed blur. */
|
|
102
|
+
&[data-zoom="200"] .stage__img,
|
|
103
|
+
&[data-zoom="400"] .stage__img {
|
|
104
|
+
image-rendering: pixelated;
|
|
105
|
+
}
|
|
106
|
+
&[data-view="diff"] .stage__grid,
|
|
107
|
+
&[data-view="flip"] .stage__grid,
|
|
108
|
+
&[data-view="swipe"] .stage__grid,
|
|
109
|
+
&[data-view="onion"] .stage__grid {
|
|
110
|
+
grid-template-columns: minmax(0, 1fr);
|
|
111
|
+
}
|
|
112
|
+
&[data-view="diff"] .stage__pane:not([data-pane="diff"]) {
|
|
113
|
+
display: none;
|
|
114
|
+
}
|
|
115
|
+
&[data-view="flip"][data-flip="baseline"] .stage__pane:not([data-pane="baseline"]),
|
|
116
|
+
&[data-view="flip"]:not([data-flip="baseline"]) .stage__pane:not([data-pane="current"]) {
|
|
117
|
+
display: none;
|
|
118
|
+
}
|
|
119
|
+
&[data-view="swipe"] .stage__pane[data-pane="diff"],
|
|
120
|
+
&[data-view="onion"] .stage__pane[data-pane="diff"] {
|
|
121
|
+
display: none;
|
|
122
|
+
}
|
|
123
|
+
&[data-view="swipe"] .stage__pane,
|
|
124
|
+
&[data-view="onion"] .stage__pane,
|
|
125
|
+
&[data-view="swipe"] .stage__divider {
|
|
126
|
+
grid-area: 1 / 1;
|
|
127
|
+
}
|
|
128
|
+
&[data-view="swipe"] .stage__label,
|
|
129
|
+
&[data-view="onion"] .stage__label {
|
|
130
|
+
display: none;
|
|
131
|
+
}
|
|
132
|
+
&[data-view="swipe"] .stage__pane[data-pane="current"] {
|
|
133
|
+
clip-path: inset(0 0 0 var(--swipe, 50%));
|
|
134
|
+
background: transparent;
|
|
135
|
+
}
|
|
136
|
+
&[data-view="onion"] .stage__pane[data-pane="current"] {
|
|
137
|
+
opacity: var(--onion, 0.5);
|
|
138
|
+
background: transparent;
|
|
139
|
+
}
|
|
140
|
+
&[data-view="swipe"] .stage__divider {
|
|
141
|
+
position: relative;
|
|
142
|
+
display: block;
|
|
143
|
+
z-index: 3;
|
|
144
|
+
justify-self: start;
|
|
145
|
+
width: 2px;
|
|
146
|
+
height: 100%;
|
|
147
|
+
margin-left: var(--swipe, 50%);
|
|
148
|
+
background: var(--accent);
|
|
149
|
+
pointer-events: none;
|
|
150
|
+
}
|
|
151
|
+
/* The only part of the divider that takes pointer input: drag it to wipe. */
|
|
152
|
+
.stage__knob {
|
|
153
|
+
position: absolute;
|
|
154
|
+
top: 50%;
|
|
155
|
+
left: 50%;
|
|
156
|
+
display: grid;
|
|
157
|
+
place-items: center;
|
|
158
|
+
width: 32px;
|
|
159
|
+
height: 32px;
|
|
160
|
+
transform: translate(-50%, -50%);
|
|
161
|
+
background: var(--accent);
|
|
162
|
+
color: var(--accent-contrast);
|
|
163
|
+
border: 2px solid var(--surface-card);
|
|
164
|
+
border-radius: var(--radius-pill);
|
|
165
|
+
box-shadow: var(--shadow-2);
|
|
166
|
+
font-size: var(--text-lg);
|
|
167
|
+
line-height: 1;
|
|
168
|
+
cursor: ew-resize;
|
|
169
|
+
pointer-events: auto;
|
|
170
|
+
touch-action: none;
|
|
171
|
+
user-select: none;
|
|
172
|
+
}
|
|
173
|
+
&[data-view="flip"] .stage__grid {
|
|
174
|
+
cursor: pointer;
|
|
175
|
+
}
|
|
176
|
+
&[data-view="swipe"] .stage__controls[data-for="swipe"],
|
|
177
|
+
&[data-view="onion"] .stage__controls[data-for="onion"] {
|
|
178
|
+
display: flex;
|
|
179
|
+
}
|
|
180
|
+
`;
|
|
181
|
+
const MODES = [
|
|
182
|
+
{
|
|
183
|
+
label: "Side by side",
|
|
184
|
+
value: "split"
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
label: "Swipe",
|
|
188
|
+
value: "swipe"
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
label: "Onion skin",
|
|
192
|
+
value: "onion"
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
label: "Diff",
|
|
196
|
+
value: "diff"
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
label: "Flip",
|
|
200
|
+
value: "flip"
|
|
201
|
+
}
|
|
202
|
+
];
|
|
203
|
+
const ZOOMS = [
|
|
204
|
+
{
|
|
205
|
+
label: "Fit",
|
|
206
|
+
value: "fit"
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
label: "100%",
|
|
210
|
+
value: "100"
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
label: "200%",
|
|
214
|
+
value: "200"
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
label: "400%",
|
|
218
|
+
value: "400"
|
|
219
|
+
}
|
|
220
|
+
];
|
|
221
|
+
function withActive(items, active) {
|
|
222
|
+
return items.map((item) => ({
|
|
223
|
+
...item,
|
|
224
|
+
active: item.value === active
|
|
225
|
+
}));
|
|
226
|
+
}
|
|
227
|
+
const Pane = ({ kind, label, src, alt, empty, meta }) => {
|
|
228
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
229
|
+
class: "stage__pane",
|
|
230
|
+
"data-pane": kind,
|
|
231
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
232
|
+
class: "stage__label",
|
|
233
|
+
children: [/* @__PURE__ */ jsx("span", { children: label }), meta ? /* @__PURE__ */ jsx("span", { children: meta }) : null]
|
|
234
|
+
}), src ? /* @__PURE__ */ jsx("img", {
|
|
235
|
+
class: "stage__img",
|
|
236
|
+
src,
|
|
237
|
+
alt,
|
|
238
|
+
loading: "lazy",
|
|
239
|
+
decoding: "async"
|
|
240
|
+
}) : /* @__PURE__ */ jsx("div", {
|
|
241
|
+
class: "stage__empty",
|
|
242
|
+
children: empty
|
|
243
|
+
})]
|
|
244
|
+
});
|
|
245
|
+
};
|
|
246
|
+
/** Swipe and onion-skin sliders; each is shown only in its own mode (CSS). */
|
|
247
|
+
const RangeControls = () => {
|
|
248
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("label", {
|
|
249
|
+
class: "stage__controls",
|
|
250
|
+
"data-for": "swipe",
|
|
251
|
+
children: [
|
|
252
|
+
/* @__PURE__ */ jsx("span", { children: "Baseline" }),
|
|
253
|
+
/* @__PURE__ */ jsx("input", {
|
|
254
|
+
type: "range",
|
|
255
|
+
min: "0",
|
|
256
|
+
max: "100",
|
|
257
|
+
value: "50",
|
|
258
|
+
"data-compare-swipe": true,
|
|
259
|
+
"aria-label": "Swipe"
|
|
260
|
+
}),
|
|
261
|
+
/* @__PURE__ */ jsx("span", { children: "Current" })
|
|
262
|
+
]
|
|
263
|
+
}), /* @__PURE__ */ jsxs("label", {
|
|
264
|
+
class: "stage__controls",
|
|
265
|
+
"data-for": "onion",
|
|
266
|
+
children: [
|
|
267
|
+
/* @__PURE__ */ jsx("span", { children: "Baseline" }),
|
|
268
|
+
/* @__PURE__ */ jsx("input", {
|
|
269
|
+
type: "range",
|
|
270
|
+
min: "0",
|
|
271
|
+
max: "100",
|
|
272
|
+
value: "50",
|
|
273
|
+
"data-compare-onion": true,
|
|
274
|
+
"aria-label": "Opacity"
|
|
275
|
+
}),
|
|
276
|
+
/* @__PURE__ */ jsx("span", { children: "Current" })
|
|
277
|
+
]
|
|
278
|
+
})] });
|
|
279
|
+
};
|
|
280
|
+
/** Baseline / current / diff comparison with swipe, onion-skin, flip, and zoom. */
|
|
281
|
+
const CompareStage = ({ baselineSrc, currentSrc, diffSrc, subject, baselineEmpty = "New story — no baseline yet. Approve to set the baseline.", diffEmpty = "No diff", diffMeta }) => {
|
|
282
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
283
|
+
class: stageRoot,
|
|
284
|
+
"data-compare": true,
|
|
285
|
+
"data-view": "split",
|
|
286
|
+
"data-zoom": "fit",
|
|
287
|
+
"data-flip": "current",
|
|
288
|
+
children: [
|
|
289
|
+
/* @__PURE__ */ jsxs("div", {
|
|
290
|
+
class: "stage__bar",
|
|
291
|
+
children: [/* @__PURE__ */ jsx(Segmented, {
|
|
292
|
+
label: "Comparison mode",
|
|
293
|
+
"data-compare-mode": true,
|
|
294
|
+
items: withActive(MODES, "split")
|
|
295
|
+
}), /* @__PURE__ */ jsx(Segmented, {
|
|
296
|
+
label: "Zoom",
|
|
297
|
+
"data-compare-zoom": true,
|
|
298
|
+
items: withActive(ZOOMS, "fit")
|
|
299
|
+
})]
|
|
300
|
+
}),
|
|
301
|
+
/* @__PURE__ */ jsxs("div", {
|
|
302
|
+
class: "stage__grid",
|
|
303
|
+
children: [
|
|
304
|
+
/* @__PURE__ */ jsx(Pane, {
|
|
305
|
+
kind: "baseline",
|
|
306
|
+
label: "Baseline",
|
|
307
|
+
src: baselineSrc,
|
|
308
|
+
alt: `Baseline for ${subject}`,
|
|
309
|
+
empty: baselineEmpty
|
|
310
|
+
}),
|
|
311
|
+
/* @__PURE__ */ jsx(Pane, {
|
|
312
|
+
kind: "current",
|
|
313
|
+
label: "Current",
|
|
314
|
+
src: currentSrc,
|
|
315
|
+
alt: `Current for ${subject}`,
|
|
316
|
+
empty: "No screenshot"
|
|
317
|
+
}),
|
|
318
|
+
/* @__PURE__ */ jsx(Pane, {
|
|
319
|
+
kind: "diff",
|
|
320
|
+
label: "Diff",
|
|
321
|
+
src: diffSrc,
|
|
322
|
+
alt: `Diff for ${subject}`,
|
|
323
|
+
empty: diffEmpty,
|
|
324
|
+
meta: diffMeta
|
|
325
|
+
}),
|
|
326
|
+
/* @__PURE__ */ jsx("span", {
|
|
327
|
+
class: "stage__divider",
|
|
328
|
+
"aria-hidden": "true",
|
|
329
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
330
|
+
class: "stage__knob",
|
|
331
|
+
"data-compare-knob": true,
|
|
332
|
+
children: "↔"
|
|
333
|
+
})
|
|
334
|
+
})
|
|
335
|
+
]
|
|
336
|
+
}),
|
|
337
|
+
/* @__PURE__ */ jsx(RangeControls, {})
|
|
338
|
+
]
|
|
339
|
+
});
|
|
340
|
+
};
|
|
341
|
+
//#endregion
|
|
342
|
+
export { CompareStage };
|
|
343
|
+
|
|
344
|
+
//# sourceMappingURL=compare-stage.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare-stage.mjs","names":[],"sources":["../../src/ui/compare-stage.tsx"],"sourcesContent":["import type { FC } from \"hono/jsx\";\nimport { css } from \"./css.ts\";\nimport { Segmented } from \"./segmented.tsx\";\n\n/* eslint-disable promise-function-async -- Hono JSX components return HtmlEscapedString | Promise<HtmlEscapedString> */\n\n/**\n * Screenshot comparison surface. All modes share one set of images; the\n * `data-view` / `data-zoom` / `data-flip` attributes on the root (driven by\n * the review script) decide what is shown. Without JS it stays side-by-side.\n */\nconst stageRoot = css`\n /* compare-stage */\n display: grid;\n gap: var(--space-3);\n .stage__bar {\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-3);\n align-items: center;\n justify-content: space-between;\n }\n .stage__grid {\n display: grid;\n grid-template-columns: repeat(3, minmax(0, 1fr));\n gap: var(--space-3);\n align-items: start;\n }\n .stage__pane {\n position: relative;\n min-width: 0;\n max-height: 78vh;\n overflow: auto;\n background: var(--surface-card);\n border: 1px solid var(--border);\n border-radius: var(--radius);\n }\n .stage__label {\n position: sticky;\n top: 0;\n left: 0;\n z-index: 2;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.35rem 0.6rem;\n background: var(--surface-subtle);\n border-bottom: 1px solid var(--border);\n color: var(--text-secondary);\n font-size: var(--text-xs);\n font-weight: 600;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n }\n .stage__img {\n display: block;\n width: 100%;\n height: auto;\n background: repeating-conic-gradient(var(--surface-muted) 0% 25%, var(--surface-subtle) 0% 50%)\n 0 0 / 16px 16px;\n }\n .stage__empty {\n display: grid;\n place-items: center;\n aspect-ratio: 16 / 9;\n padding: var(--space-4);\n background: var(--surface-subtle);\n color: var(--text-secondary);\n font-size: var(--text-sm);\n text-align: center;\n }\n .stage__divider,\n .stage__controls {\n display: none;\n }\n .stage__controls {\n align-items: center;\n gap: var(--space-3);\n color: var(--text-secondary);\n font-size: var(--text-sm);\n }\n .stage__controls input {\n flex: 1;\n accent-color: var(--accent);\n }\n &[data-zoom=\"100\"] .stage__img {\n width: auto;\n max-width: none;\n }\n &[data-zoom=\"200\"] .stage__img {\n width: auto;\n max-width: none;\n zoom: 2;\n }\n &[data-zoom=\"400\"] .stage__img {\n width: auto;\n max-width: none;\n zoom: 4;\n }\n /* Zoomed in, show real pixels instead of a smoothed blur. */\n &[data-zoom=\"200\"] .stage__img,\n &[data-zoom=\"400\"] .stage__img {\n image-rendering: pixelated;\n }\n &[data-view=\"diff\"] .stage__grid,\n &[data-view=\"flip\"] .stage__grid,\n &[data-view=\"swipe\"] .stage__grid,\n &[data-view=\"onion\"] .stage__grid {\n grid-template-columns: minmax(0, 1fr);\n }\n &[data-view=\"diff\"] .stage__pane:not([data-pane=\"diff\"]) {\n display: none;\n }\n &[data-view=\"flip\"][data-flip=\"baseline\"] .stage__pane:not([data-pane=\"baseline\"]),\n &[data-view=\"flip\"]:not([data-flip=\"baseline\"]) .stage__pane:not([data-pane=\"current\"]) {\n display: none;\n }\n &[data-view=\"swipe\"] .stage__pane[data-pane=\"diff\"],\n &[data-view=\"onion\"] .stage__pane[data-pane=\"diff\"] {\n display: none;\n }\n &[data-view=\"swipe\"] .stage__pane,\n &[data-view=\"onion\"] .stage__pane,\n &[data-view=\"swipe\"] .stage__divider {\n grid-area: 1 / 1;\n }\n &[data-view=\"swipe\"] .stage__label,\n &[data-view=\"onion\"] .stage__label {\n display: none;\n }\n &[data-view=\"swipe\"] .stage__pane[data-pane=\"current\"] {\n clip-path: inset(0 0 0 var(--swipe, 50%));\n background: transparent;\n }\n &[data-view=\"onion\"] .stage__pane[data-pane=\"current\"] {\n opacity: var(--onion, 0.5);\n background: transparent;\n }\n &[data-view=\"swipe\"] .stage__divider {\n position: relative;\n display: block;\n z-index: 3;\n justify-self: start;\n width: 2px;\n height: 100%;\n margin-left: var(--swipe, 50%);\n background: var(--accent);\n pointer-events: none;\n }\n /* The only part of the divider that takes pointer input: drag it to wipe. */\n .stage__knob {\n position: absolute;\n top: 50%;\n left: 50%;\n display: grid;\n place-items: center;\n width: 32px;\n height: 32px;\n transform: translate(-50%, -50%);\n background: var(--accent);\n color: var(--accent-contrast);\n border: 2px solid var(--surface-card);\n border-radius: var(--radius-pill);\n box-shadow: var(--shadow-2);\n font-size: var(--text-lg);\n line-height: 1;\n cursor: ew-resize;\n pointer-events: auto;\n touch-action: none;\n user-select: none;\n }\n &[data-view=\"flip\"] .stage__grid {\n cursor: pointer;\n }\n &[data-view=\"swipe\"] .stage__controls[data-for=\"swipe\"],\n &[data-view=\"onion\"] .stage__controls[data-for=\"onion\"] {\n display: flex;\n }\n`;\n\nconst MODES = [\n { label: \"Side by side\", value: \"split\" },\n { label: \"Swipe\", value: \"swipe\" },\n { label: \"Onion skin\", value: \"onion\" },\n { label: \"Diff\", value: \"diff\" },\n { label: \"Flip\", value: \"flip\" },\n];\n\nconst ZOOMS = [\n { label: \"Fit\", value: \"fit\" },\n { label: \"100%\", value: \"100\" },\n { label: \"200%\", value: \"200\" },\n { label: \"400%\", value: \"400\" },\n];\n\nfunction withActive(\n items: { label: string; value: string }[],\n active: string,\n): { label: string; value: string; active: boolean }[] {\n return items.map((item) => ({ ...item, active: item.value === active }));\n}\n\ninterface PaneProps {\n kind: \"baseline\" | \"current\" | \"diff\";\n label: string;\n src: string | null;\n alt: string;\n empty: unknown;\n meta?: string;\n}\n\nconst Pane: FC<PaneProps> = ({ kind, label, src, alt, empty, meta }) => {\n return (\n <div class=\"stage__pane\" data-pane={kind}>\n <div class=\"stage__label\">\n <span>{label}</span>\n {meta ? <span>{meta}</span> : null}\n </div>\n {src ? (\n <img class=\"stage__img\" src={src} alt={alt} loading=\"lazy\" decoding=\"async\" />\n ) : (\n <div class=\"stage__empty\">{empty}</div>\n )}\n </div>\n );\n};\n\n/** Swipe and onion-skin sliders; each is shown only in its own mode (CSS). */\nconst RangeControls: FC = () => {\n return (\n <>\n <label class=\"stage__controls\" data-for=\"swipe\">\n <span>Baseline</span>\n <input type=\"range\" min=\"0\" max=\"100\" value=\"50\" data-compare-swipe aria-label=\"Swipe\" />\n <span>Current</span>\n </label>\n <label class=\"stage__controls\" data-for=\"onion\">\n <span>Baseline</span>\n <input type=\"range\" min=\"0\" max=\"100\" value=\"50\" data-compare-onion aria-label=\"Opacity\" />\n <span>Current</span>\n </label>\n </>\n );\n};\n\n/** Props for the screenshot comparison surface. */\nexport interface CompareStageProps {\n /** Baseline image URL, or null when the story has no baseline yet. */\n baselineSrc: string | null;\n currentSrc: string;\n /** Diff overlay URL, or null when none was produced. */\n diffSrc: string | null;\n /** Accessible description shared by the images, e.g. \"Button / Primary\". */\n subject: string;\n baselineEmpty?: unknown;\n diffEmpty?: unknown;\n diffMeta?: string;\n}\n\n/** Baseline / current / diff comparison with swipe, onion-skin, flip, and zoom. */\nexport const CompareStage: FC<CompareStageProps> = ({\n baselineSrc,\n currentSrc,\n diffSrc,\n subject,\n baselineEmpty = \"New story — no baseline yet. Approve to set the baseline.\",\n diffEmpty = \"No diff\",\n diffMeta,\n}) => {\n return (\n <div class={stageRoot} data-compare data-view=\"split\" data-zoom=\"fit\" data-flip=\"current\">\n <div class=\"stage__bar\">\n <Segmented label=\"Comparison mode\" data-compare-mode items={withActive(MODES, \"split\")} />\n <Segmented label=\"Zoom\" data-compare-zoom items={withActive(ZOOMS, \"fit\")} />\n </div>\n <div class=\"stage__grid\">\n <Pane\n kind=\"baseline\"\n label=\"Baseline\"\n src={baselineSrc}\n alt={`Baseline for ${subject}`}\n empty={baselineEmpty}\n />\n <Pane\n kind=\"current\"\n label=\"Current\"\n src={currentSrc}\n alt={`Current for ${subject}`}\n empty=\"No screenshot\"\n />\n <Pane\n kind=\"diff\"\n label=\"Diff\"\n src={diffSrc}\n alt={`Diff for ${subject}`}\n empty={diffEmpty}\n meta={diffMeta}\n />\n <span class=\"stage__divider\" aria-hidden=\"true\">\n <span class=\"stage__knob\" data-compare-knob>\n ↔\n </span>\n </span>\n </div>\n <RangeControls />\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;AAWA,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyKrB,MAAM,QAAQ;CACZ;EAAE,OAAO;EAAgB,OAAO;CAAQ;CACxC;EAAE,OAAO;EAAS,OAAO;CAAQ;CACjC;EAAE,OAAO;EAAc,OAAO;CAAQ;CACtC;EAAE,OAAO;EAAQ,OAAO;CAAO;CAC/B;EAAE,OAAO;EAAQ,OAAO;CAAO;AACjC;AAEA,MAAM,QAAQ;CACZ;EAAE,OAAO;EAAO,OAAO;CAAM;CAC7B;EAAE,OAAO;EAAQ,OAAO;CAAM;CAC9B;EAAE,OAAO;EAAQ,OAAO;CAAM;CAC9B;EAAE,OAAO;EAAQ,OAAO;CAAM;AAChC;AAEA,SAAS,WACP,OACA,QACqD;CACrD,OAAO,MAAM,KAAK,UAAU;EAAE,GAAG;EAAM,QAAQ,KAAK,UAAU;CAAO,EAAE;AACzE;AAWA,MAAM,QAAuB,EAAE,MAAM,OAAO,KAAK,KAAK,OAAO,WAAW;CACtE,OACE,qBAAC,OAAD;EAAK,OAAM;EAAc,aAAW;EAApC,UAAA,CACE,qBAAC,OAAD;GAAK,OAAM;GAAX,UAAA,CACE,oBAAC,QAAD,EAAA,UAAO,MAAY,CAAA,GAClB,OAAO,oBAAC,QAAD,EAAA,UAAO,KAAW,CAAA,IAAI,IAC3B;EACJ,CAAA,GAAA,MACC,oBAAC,OAAD;GAAK,OAAM;GAAkB;GAAU;GAAK,SAAQ;GAAO,UAAS;EAAS,CAAA,IAE7E,oBAAC,OAAD;GAAK,OAAM;GAAgB,UAAA;EAAW,CAAA,CAErC;;AAET;;AAGA,MAAM,sBAA0B;CAC9B,OACE,qBAAA,UAAA,EAAA,UAAA,CACE,qBAAC,SAAD;EAAO,OAAM;EAAkB,YAAS;EAAxC,UAAA;GACE,oBAAC,QAAD,EAAA,UAAM,WAAc,CAAA;GACpB,oBAAC,SAAD;IAAO,MAAK;IAAQ,KAAI;IAAI,KAAI;IAAM,OAAM;IAAK,sBAAA;IAAmB,cAAW;GAAS,CAAA;GACxF,oBAAC,QAAD,EAAA,UAAM,UAAa,CAAA;EACd;CACP,CAAA,GAAA,qBAAC,SAAD;EAAO,OAAM;EAAkB,YAAS;EAAxC,UAAA;GACE,oBAAC,QAAD,EAAA,UAAM,WAAc,CAAA;GACpB,oBAAC,SAAD;IAAO,MAAK;IAAQ,KAAI;IAAI,KAAI;IAAM,OAAM;IAAK,sBAAA;IAAmB,cAAW;GAAW,CAAA;GAC1F,oBAAC,QAAD,EAAA,UAAM,UAAa,CAAA;EACd;CACP,CAAA,CAAA,EAAA,CAAA;AAEN;;AAiBA,MAAa,gBAAuC,EAClD,aACA,YACA,SACA,SACA,gBAAgB,6DAChB,YAAY,WACZ,eACI;CACJ,OACE,qBAAC,OAAD;EAAK,OAAO;EAAW,gBAAA;EAAa,aAAU;EAAQ,aAAU;EAAM,aAAU;EAAhF,UAAA;GACE,qBAAC,OAAD;IAAK,OAAM;IAAX,UAAA,CACE,oBAAC,WAAD;KAAW,OAAM;KAAkB,qBAAA;KAAkB,OAAO,WAAW,OAAO,OAAO;IAAI,CAAA,GACzF,oBAAC,WAAD;KAAW,OAAM;KAAO,qBAAA;KAAkB,OAAO,WAAW,OAAO,KAAK;IAAI,CAAA,CACzE;;GACL,qBAAC,OAAD;IAAK,OAAM;IAAX,UAAA;KACE,oBAAC,MAAD;MACE,MAAK;MACL,OAAM;MACN,KAAK;MACL,KAAK,gBAAgB;MACrB,OAAO;KACR,CAAA;KACD,oBAAC,MAAD;MACE,MAAK;MACL,OAAM;MACN,KAAK;MACL,KAAK,eAAe;MACpB,OAAM;KACP,CAAA;KACD,oBAAC,MAAD;MACE,MAAK;MACL,OAAM;MACN,KAAK;MACL,KAAK,YAAY;MACjB,OAAO;MACP,MAAM;KACP,CAAA;KACD,oBAAC,QAAD;MAAM,OAAM;MAAiB,eAAY;MACvC,UAAA,oBAAC,QAAD;OAAM,OAAM;OAAc,qBAAA;OAAkB,UAAA;MAEtC,CAAA;KACF,CAAA;IACH;;GACL,oBAAC,eAAD,CAAgB,CAAA;EACb;;AAET"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import "node:path";
|
|
2
|
+
import "node:url";
|
|
3
|
+
import.meta.url;
|
|
4
|
+
import { getStore } from "../store.mjs";
|
|
5
|
+
import { getCsrfToken } from "../middleware/csrf.mjs";
|
|
6
|
+
import { jsx } from "hono/jsx/jsx-runtime";
|
|
7
|
+
//#region src/ui/csrf-field.tsx
|
|
8
|
+
/** Hidden CSRF token so native (non-HTMX) form posts pass the csrf middleware. */
|
|
9
|
+
function csrfField() {
|
|
10
|
+
const { config, sessionId } = getStore();
|
|
11
|
+
return /* @__PURE__ */ jsx("input", {
|
|
12
|
+
type: "hidden",
|
|
13
|
+
name: "csrf_token",
|
|
14
|
+
value: getCsrfToken(config.secret, sessionId)
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
export { csrfField };
|
|
19
|
+
|
|
20
|
+
//# sourceMappingURL=csrf-field.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csrf-field.mjs","names":[],"sources":["../../src/ui/csrf-field.tsx"],"sourcesContent":["import { getCsrfToken } from \"../middleware/csrf.ts\";\nimport { getStore } from \"../store.ts\";\n\n/** Hidden CSRF token so native (non-HTMX) form posts pass the csrf middleware. */\nexport function csrfField(): unknown {\n const { config, sessionId } = getStore();\n return <input type=\"hidden\" name=\"csrf_token\" value={getCsrfToken(config.secret, sessionId)} />;\n}\n"],"mappings":";;;;;;;;AAIA,SAAgB,YAAqB;CACnC,MAAM,EAAE,QAAQ,cAAc,SAAS;CACvC,OAAO,oBAAC,SAAD;EAAO,MAAK;EAAS,MAAK;EAAa,OAAO,aAAa,OAAO,QAAQ,SAAS;CAAI,CAAA;AAChG"}
|