dsh-prompt-vcs 0.4.0 → 0.5.0
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/CHANGELOG.md +32 -0
- package/lib/client/ui.js +42 -24
- package/lib/vcs.web.js +25 -23
- package/lib/vcs.web.js.map +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
**Fixed: panels did not follow the host theme.**
|
|
6
|
+
|
|
7
|
+
ui.tsx read invented custom properties — `--accent`, `--border`, `--bg-primary`,
|
|
8
|
+
`--text-primary` and friends. dsh defines none of them, so the hardcoded
|
|
9
|
+
fallbacks applied in every theme: each panel rendered its own fixed palette
|
|
10
|
+
instead of following the host.
|
|
11
|
+
|
|
12
|
+
The visible symptom was `surface: 'var(--bg-primary, #fff)'` on the modal,
|
|
13
|
+
input, select and textarea backgrounds — **white panels in dark mode**, with text
|
|
14
|
+
in the host's near-white label color.
|
|
15
|
+
|
|
16
|
+
Every name is now a real dsh token, pinned in `tests/ui-tokens.test.mjs`:
|
|
17
|
+
|
|
18
|
+
- accent → `--dsw-alias-brand-primary`
|
|
19
|
+
- surfaces → `--dsw-alias-bg-layer-1` / `-2`
|
|
20
|
+
- border → `--dsw-alias-border-l2`
|
|
21
|
+
- text / muted → `--dsw-alias-label-primary` / `--dsw-alias-label-tertiary`
|
|
22
|
+
- state colors → `--dsw-alias-state-*-primary` with `-tertiary` / `-secondary` tints
|
|
23
|
+
- modal scrim → `--dsw-alias-bg-mask-1`
|
|
24
|
+
- shadows → `--dsw-elevation-panel` / `--dsw-elevation-prominent`
|
|
25
|
+
|
|
26
|
+
`#fff` on the primary button became `--dsw-alias-label-primary-inverted`: dsh's
|
|
27
|
+
brand color is near-black in light mode and near-white in dark, so the literal
|
|
28
|
+
white would have disappeared against the fill.
|
|
29
|
+
|
|
30
|
+
Fallbacks are gone on purpose. If a token were ever missing, the declaration
|
|
31
|
+
becomes invalid at computed-value time and the property inherits, which degrades
|
|
32
|
+
gracefully — a hardcoded fallback instead bakes in a color that is wrong in one
|
|
33
|
+
of the two themes. The guard test fails on any fallback for exactly that reason.
|
|
34
|
+
|
|
3
35
|
## 0.2.0
|
|
4
36
|
|
|
5
37
|
Ecosystem sync: every plugin in this suite shares one version, so a version number
|
package/lib/client/ui.js
CHANGED
|
@@ -10,18 +10,36 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
10
10
|
*/
|
|
11
11
|
import { createContext, useCallback, useContext, useEffect, useMemo, useState, } from 'react';
|
|
12
12
|
// ─── Theme tokens ──────────────────────────────────────────────
|
|
13
|
+
/**
|
|
14
|
+
* Host theme tokens.
|
|
15
|
+
*
|
|
16
|
+
* Every name here is a real dsh token, read out of the shipped stylesheets
|
|
17
|
+
* (`@deepseek-ai/dsh-client-ui-theme`), which define them on `body`. An earlier
|
|
18
|
+
* version used invented names — `--accent`, `--border`, `--bg-primary`,
|
|
19
|
+
* `--text-primary` and friends — that dsh does not define at all. The
|
|
20
|
+
* hardcoded fallbacks then applied in every theme, so each panel rendered its
|
|
21
|
+
* own fixed palette instead of following the host, and the modal and input
|
|
22
|
+
* surfaces stayed white in dark mode.
|
|
23
|
+
*
|
|
24
|
+
* No fallbacks on purpose: if a name were ever missing, the declaration becomes
|
|
25
|
+
* invalid at computed-value time and the property inherits, which degrades
|
|
26
|
+
* gracefully, whereas a hardcoded fallback bakes in a color that is wrong in one
|
|
27
|
+
* of the two themes. `tests/ui-tokens.test.mjs` enforces the name list.
|
|
28
|
+
*/
|
|
13
29
|
const T = {
|
|
14
|
-
accent: 'var(--
|
|
15
|
-
accentHover: 'var(--
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
30
|
+
accent: 'var(--dsw-alias-brand-primary)',
|
|
31
|
+
accentHover: 'var(--dsw-alias-button-primary-hover)',
|
|
32
|
+
inverted: 'var(--dsw-alias-label-primary-inverted)',
|
|
33
|
+
error: 'var(--dsw-alias-state-error-primary)',
|
|
34
|
+
success: 'var(--dsw-alias-state-success-primary)',
|
|
35
|
+
warning: 'var(--dsw-alias-state-warn-primary)',
|
|
36
|
+
bg: 'var(--dsw-alias-bg-layer-2)',
|
|
37
|
+
bgHover: 'var(--dsw-alias-interactive-bg-hover)',
|
|
38
|
+
border: 'var(--dsw-alias-border-l2)',
|
|
39
|
+
text: 'var(--dsw-alias-label-primary)',
|
|
40
|
+
muted: 'var(--dsw-alias-label-tertiary)',
|
|
41
|
+
surface: 'var(--dsw-alias-bg-layer-1)',
|
|
42
|
+
mask: 'var(--dsw-alias-bg-mask-1)',
|
|
25
43
|
};
|
|
26
44
|
// ─── Style injection (once) ────────────────────────────────────
|
|
27
45
|
let injected = false;
|
|
@@ -59,13 +77,13 @@ export function Modal({ title, onClose, children, footer, width = 520, }) {
|
|
|
59
77
|
display: 'flex',
|
|
60
78
|
alignItems: 'center',
|
|
61
79
|
justifyContent: 'center',
|
|
62
|
-
background:
|
|
80
|
+
background: T.mask,
|
|
63
81
|
backdropFilter: 'blur(3px)',
|
|
64
82
|
WebkitBackdropFilter: 'blur(3px)',
|
|
65
83
|
}, onClick: onClose, children: _jsxs("div", { className: "dsh-modal-body", style: {
|
|
66
84
|
background: T.surface,
|
|
67
85
|
borderRadius: 14,
|
|
68
|
-
boxShadow: '
|
|
86
|
+
boxShadow: 'var(--dsw-elevation-prominent)',
|
|
69
87
|
width: `min(92vw, ${width}px)`,
|
|
70
88
|
maxHeight: '85vh',
|
|
71
89
|
display: 'flex',
|
|
@@ -118,7 +136,7 @@ export function Button({ variant = 'secondary', size = 'md', children, style, ..
|
|
|
118
136
|
fontFamily: 'inherit',
|
|
119
137
|
};
|
|
120
138
|
const variants = {
|
|
121
|
-
primary: { background: T.accent, color:
|
|
139
|
+
primary: { background: T.accent, color: T.inverted },
|
|
122
140
|
secondary: {
|
|
123
141
|
background: T.bg,
|
|
124
142
|
color: T.text,
|
|
@@ -164,11 +182,11 @@ export function StatCard({ value, label, color, unit, }) {
|
|
|
164
182
|
// ─── Badge ─────────────────────────────────────────────────────
|
|
165
183
|
export function Badge({ children, color = 'default', }) {
|
|
166
184
|
const colors = {
|
|
167
|
-
default: { bg:
|
|
168
|
-
success: { bg: '
|
|
169
|
-
error: { bg: '
|
|
170
|
-
warning: { bg: '
|
|
171
|
-
info: { bg: '
|
|
185
|
+
default: { bg: T.bgHover, fg: T.muted },
|
|
186
|
+
success: { bg: 'var(--dsw-alias-state-success-tertiary)', fg: T.success },
|
|
187
|
+
error: { bg: 'var(--dsw-alias-state-error-secondary)', fg: T.error },
|
|
188
|
+
warning: { bg: 'var(--dsw-alias-state-warn-tertiary)', fg: T.warning },
|
|
189
|
+
info: { bg: 'var(--dsw-alias-state-business-tertiary)', fg: T.accent },
|
|
172
190
|
};
|
|
173
191
|
const c = colors[color];
|
|
174
192
|
return (_jsx("span", { style: {
|
|
@@ -252,8 +270,8 @@ export function ToastProvider({ children }) {
|
|
|
252
270
|
borderRadius: 8,
|
|
253
271
|
fontSize: 13,
|
|
254
272
|
fontWeight: 500,
|
|
255
|
-
boxShadow: '
|
|
256
|
-
color:
|
|
273
|
+
boxShadow: 'var(--dsw-elevation-panel)',
|
|
274
|
+
color: T.inverted,
|
|
257
275
|
background: t.type === 'success'
|
|
258
276
|
? T.success
|
|
259
277
|
: t.type === 'error'
|
|
@@ -318,7 +336,7 @@ export const tableStyles = {
|
|
|
318
336
|
td: {
|
|
319
337
|
padding: '8px 12px 8px 0',
|
|
320
338
|
fontSize: 13,
|
|
321
|
-
borderBottom:
|
|
339
|
+
borderBottom: `1px solid ${T.border}`,
|
|
322
340
|
},
|
|
323
341
|
clickRow: { cursor: 'pointer' },
|
|
324
342
|
};
|
|
@@ -332,7 +350,7 @@ export function CodeBlock({ children, maxHeight = 200, style, }) {
|
|
|
332
350
|
overflow: 'auto',
|
|
333
351
|
padding: 10,
|
|
334
352
|
borderRadius: 7,
|
|
335
|
-
background:
|
|
353
|
+
background: T.bg,
|
|
336
354
|
border: `1px solid ${T.border}`,
|
|
337
355
|
margin: 0,
|
|
338
356
|
...style,
|
|
@@ -343,7 +361,7 @@ export function ProgressBar({ pct, color, height = 18, }) {
|
|
|
343
361
|
return (_jsx("div", { style: {
|
|
344
362
|
width: '100%',
|
|
345
363
|
height,
|
|
346
|
-
background:
|
|
364
|
+
background: T.bgHover,
|
|
347
365
|
borderRadius: 5,
|
|
348
366
|
overflow: 'hidden',
|
|
349
367
|
position: 'relative',
|
package/lib/vcs.web.js
CHANGED
|
@@ -95,17 +95,19 @@ var import_react2 = require("react");
|
|
|
95
95
|
var import_react = require("react");
|
|
96
96
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
97
97
|
var T = {
|
|
98
|
-
accent: "var(--
|
|
99
|
-
accentHover: "var(--
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
98
|
+
accent: "var(--dsw-alias-brand-primary)",
|
|
99
|
+
accentHover: "var(--dsw-alias-button-primary-hover)",
|
|
100
|
+
inverted: "var(--dsw-alias-label-primary-inverted)",
|
|
101
|
+
error: "var(--dsw-alias-state-error-primary)",
|
|
102
|
+
success: "var(--dsw-alias-state-success-primary)",
|
|
103
|
+
warning: "var(--dsw-alias-state-warn-primary)",
|
|
104
|
+
bg: "var(--dsw-alias-bg-layer-2)",
|
|
105
|
+
bgHover: "var(--dsw-alias-interactive-bg-hover)",
|
|
106
|
+
border: "var(--dsw-alias-border-l2)",
|
|
107
|
+
text: "var(--dsw-alias-label-primary)",
|
|
108
|
+
muted: "var(--dsw-alias-label-tertiary)",
|
|
109
|
+
surface: "var(--dsw-alias-bg-layer-1)",
|
|
110
|
+
mask: "var(--dsw-alias-bg-mask-1)"
|
|
109
111
|
};
|
|
110
112
|
var injected = false;
|
|
111
113
|
function ensureStyles() {
|
|
@@ -149,7 +151,7 @@ function Modal({
|
|
|
149
151
|
display: "flex",
|
|
150
152
|
alignItems: "center",
|
|
151
153
|
justifyContent: "center",
|
|
152
|
-
background:
|
|
154
|
+
background: T.mask,
|
|
153
155
|
backdropFilter: "blur(3px)",
|
|
154
156
|
WebkitBackdropFilter: "blur(3px)"
|
|
155
157
|
},
|
|
@@ -161,7 +163,7 @@ function Modal({
|
|
|
161
163
|
style: {
|
|
162
164
|
background: T.surface,
|
|
163
165
|
borderRadius: 14,
|
|
164
|
-
boxShadow: "
|
|
166
|
+
boxShadow: "var(--dsw-elevation-prominent)",
|
|
165
167
|
width: `min(92vw, ${width}px)`,
|
|
166
168
|
maxHeight: "85vh",
|
|
167
169
|
display: "flex",
|
|
@@ -279,7 +281,7 @@ function Button({
|
|
|
279
281
|
fontFamily: "inherit"
|
|
280
282
|
};
|
|
281
283
|
const variants = {
|
|
282
|
-
primary: { background: T.accent, color:
|
|
284
|
+
primary: { background: T.accent, color: T.inverted },
|
|
283
285
|
secondary: {
|
|
284
286
|
background: T.bg,
|
|
285
287
|
color: T.text,
|
|
@@ -370,11 +372,11 @@ function Badge({
|
|
|
370
372
|
color = "default"
|
|
371
373
|
}) {
|
|
372
374
|
const colors = {
|
|
373
|
-
default: { bg:
|
|
374
|
-
success: { bg: "
|
|
375
|
-
error: { bg: "
|
|
376
|
-
warning: { bg: "
|
|
377
|
-
info: { bg: "
|
|
375
|
+
default: { bg: T.bgHover, fg: T.muted },
|
|
376
|
+
success: { bg: "var(--dsw-alias-state-success-tertiary)", fg: T.success },
|
|
377
|
+
error: { bg: "var(--dsw-alias-state-error-secondary)", fg: T.error },
|
|
378
|
+
warning: { bg: "var(--dsw-alias-state-warn-tertiary)", fg: T.warning },
|
|
379
|
+
info: { bg: "var(--dsw-alias-state-business-tertiary)", fg: T.accent }
|
|
378
380
|
};
|
|
379
381
|
const c = colors[color];
|
|
380
382
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
@@ -427,8 +429,8 @@ function ToastProvider({ children }) {
|
|
|
427
429
|
borderRadius: 8,
|
|
428
430
|
fontSize: 13,
|
|
429
431
|
fontWeight: 500,
|
|
430
|
-
boxShadow: "
|
|
431
|
-
color:
|
|
432
|
+
boxShadow: "var(--dsw-elevation-panel)",
|
|
433
|
+
color: T.inverted,
|
|
432
434
|
background: t.type === "success" ? T.success : t.type === "error" ? T.error : T.accent,
|
|
433
435
|
maxWidth: 360
|
|
434
436
|
},
|
|
@@ -532,7 +534,7 @@ var tableStyles = {
|
|
|
532
534
|
td: {
|
|
533
535
|
padding: "8px 12px 8px 0",
|
|
534
536
|
fontSize: 13,
|
|
535
|
-
borderBottom:
|
|
537
|
+
borderBottom: `1px solid ${T.border}`
|
|
536
538
|
},
|
|
537
539
|
clickRow: { cursor: "pointer" }
|
|
538
540
|
};
|
|
@@ -552,7 +554,7 @@ function CodeBlock({
|
|
|
552
554
|
overflow: "auto",
|
|
553
555
|
padding: 10,
|
|
554
556
|
borderRadius: 7,
|
|
555
|
-
background:
|
|
557
|
+
background: T.bg,
|
|
556
558
|
border: `1px solid ${T.border}`,
|
|
557
559
|
margin: 0,
|
|
558
560
|
...style
|
package/lib/vcs.web.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/client/index.tsx", "../src/client/locales.ts", "../src/client/view.tsx", "../src/client/ui.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * Browser half of dsh-prompt-vcs: its page inside Settings.\n *\n * Two services are injected by their runtime identity, not by the packages that provide\n * them: the settings shell is `slots`, the dictionary registry is `locale`. The label and\n * every string inside the component are translated through this plugin's own namespace,\n * which is why the page follows the UI language instead of the plugin author's.\n *\n * @module client\n */\n\nimport type { ComponentType } from 'react'\n\nimport { NS, en, zh } from './locales.js'\nimport { PromptVcsPanel } from './view.js'\n\nexport const inject = ['slots', 'locale']\n\ninterface SlotsService {\n inject(name: string, register: () => void): void\n register(\n options: { name: string; id: string; order: number; label: () => string; locale: string },\n component: ComponentType<{ t: (key: string, params?: Record<string, unknown>) => string }>,\n ): unknown\n}\n\ninterface ClientContext {\n slots: SlotsService\n locale: {\n register(ns: string, dicts: { zh: unknown; en: unknown }): () => void\n bind(ns: string): (key: string, params?: Record<string, unknown>) => string\n }\n effect(callback: () => unknown, label?: string): unknown\n}\n\nexport function apply(ctx: ClientContext): void {\n // `zh` is the key-set source of truth, matching the official client plugins.\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-prompt-vcs: dictionaries')\n\n ctx.slots.inject('settings.section', () => ctx.slots.register(\n {\n name: 'settings.section',\n id: 'prompt-vcs',\n order: 44,\n label: () => ctx.locale.bind(NS)('nav'),\n locale: NS,\n },\n PromptVcsPanel,\n ))\n}\n", "/**\n * Dictionaries for the Prompt VCS page.\n *\n * `zh` is the key-set source of truth, as in the official client plugins, and `en` is\n * typed against it: a key translated in one language but not the other fails the build\n * instead of silently rendering the raw key.\n *\n * @module client/locales\n */\n\n/** Dictionary namespace owned by this plugin. */\nexport const NS = 'promptVcs'\n\n/** Simplified Chinese dictionary (the key-set source of truth). */\nexport const zh = {\n 'nav': '\u63D0\u793A\u8BCD\u7248\u672C',\n 'title': '\u63D0\u793A\u8BCD\u7248\u672C',\n 'hash': '\u7248\u672C\u53F7',\n 'date': '\u65F6\u95F4',\n 'by': '\u4FEE\u6539\u65B9',\n 'file': '\u6587\u4EF6',\n 'added': '\u65B0\u589E',\n 'removed': '\u5220\u9664',\n 'empty': '\u8FD8\u6CA1\u6709\u8BB0\u5F55\u5230\u6307\u4EE4\u6539\u52A8\u3002',\n 'refresh': '\u5237\u65B0',\n 'loading': '\u6B63\u5728\u52A0\u8F7D\u2026',\n 'failed': '\u52A0\u8F7D\u5931\u8D25',\n 'retry': '\u91CD\u8BD5',\n 'totalChanges': '\u603B\u53D8\u66F4\u6570',\n 'filesTracked': '\u8DDF\u8E2A\u6587\u4EF6\u6570',\n 'watched': '\u76D1\u63A7\u6587\u4EF6',\n 'files': '\u6587\u4EF6',\n 'changes': '\u53D8\u66F4\u6B21\u6570',\n 'lastChanged': '\u6700\u540E\u53D8\u66F4',\n 'size': '\u5927\u5C0F',\n 'timeline': '\u65F6\u95F4\u7EBF',\n 'diff': '\u5DEE\u5F02',\n 'before': '\u53D8\u66F4\u524D',\n 'after': '\u53D8\u66F4\u540E',\n 'rollback': '\u56DE\u6EDA',\n 'confirmRollback': '\u786E\u5B9A\u56DE\u6EDA\u5230\u6B64\u53D8\u66F4\u4E4B\u524D\u7684\u72B6\u6001\uFF1F',\n 'notFound': '\u672A\u627E\u5230\u53D8\u66F4\u8BB0\u5F55',\n}\n\n/** English dictionary, checked complete against the zh key set. */\nexport const en: typeof zh = {\n 'nav': 'Prompt VCS',\n 'title': 'Prompt VCS',\n 'hash': 'Hash',\n 'date': 'Date',\n 'by': 'Changed by',\n 'file': 'File',\n 'added': 'Added',\n 'removed': 'Removed',\n 'empty': 'No instruction changes recorded yet.',\n 'refresh': 'Refresh',\n 'loading': 'Loading\u2026',\n 'failed': 'Failed to load',\n 'retry': 'Retry',\n 'totalChanges': 'Total Changes',\n 'filesTracked': 'Files Tracked',\n 'watched': 'Watched',\n 'files': 'Files',\n 'changes': 'Changes',\n 'lastChanged': 'Last Changed',\n 'size': 'Size',\n 'timeline': 'Timeline',\n 'diff': 'Diff',\n 'before': 'Before',\n 'after': 'After',\n 'rollback': 'Rollback',\n 'confirmRollback': 'Rollback to before this change?',\n 'notFound': 'Change not found',\n}\n", "/**\n * Pure rendering half of the promptVcs page. Uses shared UI kit.\n * @module client/view\n */\n\nimport { useCallback, useState } from 'react'\nimport type { ReactNode } from 'react'\n\nimport type { PanelPayload, Change } from '../types.js'\nimport {\n Badge, Button, Card, CodeBlock, ConfirmDialog, EmptyState, Modal,\n SectionTitle, Spinner, StatCard, ToastProvider, tableStyles, usePanel, useToast,\n} from './ui.js'\n\nexport type Translate = (key: string, params?: Record<string, unknown>) => string\nexport interface PanelProps { t: Translate }\n\nconst PANEL_PATH = '/api/vcs.panel'\nconst CHANGE_PATH = '/api/vcs.change'\nconst ROLLBACK_PATH = '/api/vcs.rollback'\n\nfunction ChangeModal({ hash, t, onClose, onRollback }: {\n hash: string; t: Translate; onClose: () => void; onRollback: () => void\n}): ReactNode {\n const toast = useToast()\n const [change, setChange] = useState<Change | null>(null)\n const [loading, setLoading] = useState(true)\n const [confirmRb, setConfirmRb] = useState(false)\n const [rolling, setRolling] = useState(false)\n\n useCallback(() => {\n setLoading(true)\n fetch(`${CHANGE_PATH}?hash=${hash}`).then(async (r) => { if (r.ok) setChange(await r.json() as Change) }).finally(() => setLoading(false))\n }, [hash])()\n\n const handleRollback = useCallback(async () => {\n setRolling(true)\n try {\n const r = await fetch(ROLLBACK_PATH, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ hash }) })\n if (r.ok) { toast('success', t('rolledBack')); onRollback(); onClose() }\n } finally { setRolling(false) }\n }, [hash, t, toast, onRollback, onClose])\n\n return (\n <Modal title={`${t('change')} ${hash.slice(0, 8)}\u2026`} onClose={onClose} width={760}\n footer={<Button variant=\"danger\" size=\"sm\" disabled={rolling} onClick={() => setConfirmRb(true)}>\u21A9 {t('rollback')}</Button>}>\n {loading ? <div style={{ display: 'flex', justifyContent: 'center', padding: 20 }}><Spinner size={24} /></div>\n : change === null ? <EmptyState message={t('notFound')} />\n : (\n <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>\n <div style={{ fontSize: 13, fontWeight: 600 }}>{change.file}</div>\n <Card title={t('diff')}><CodeBlock>{change.diff}</CodeBlock></Card>\n <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>\n <div style={{ flex: 1, minWidth: 250 }}>\n <Badge color=\"error\">{t('before')}</Badge>\n <CodeBlock style={{ marginTop: 6 }} maxHeight={180}>{`${change.oldContent.slice(0, 2000)}${change.oldContent.length > 2000 ? '\\n\u2026' : ''}`}</CodeBlock>\n </div>\n <div style={{ flex: 1, minWidth: 250 }}>\n <Badge color=\"success\">{t('after')}</Badge>\n <CodeBlock style={{ marginTop: 6 }} maxHeight={180}>{`${change.newContent.slice(0, 2000)}${change.newContent.length > 2000 ? '\\n\u2026' : ''}`}</CodeBlock>\n </div>\n </div>\n </div>\n )}\n {confirmRb && <ConfirmDialog title={t('rollback')} message={t('confirmRollback')} confirmLabel={t('rollback')} danger\n onConfirm={handleRollback} onClose={() => setConfirmRb(false)} />}\n </Modal>\n )\n}\n\nfunction PromptVcsPanelInner({ t }: PanelProps): ReactNode {\n const { payload, error, reload } = usePanel<PanelPayload>(PANEL_PATH)\n const [changeModal, setChangeModal] = useState<string | null>(null)\n\n const header = (\n <header style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 4 }}>\n <strong style={{ fontSize: 15 }}>\uD83D\uDCDD {t('title')}</strong>\n <span style={{ flex: 1 }} />\n <Button variant=\"secondary\" size=\"sm\" onClick={reload}>{t('refresh')}</Button>\n </header>\n )\n\n if (error !== null) return <div style={{ maxWidth: 820 }}>{header}<Card><p role=\"alert\" style={{ margin: 0, fontSize: 13, color: 'var(--error, #e53935)' }}>{t('failed')}: {error}</p></Card></div>\n if (payload === null) return <div style={{ maxWidth: 820 }}>{header}<div style={{ display: 'flex', justifyContent: 'center', padding: 40 }}><Spinner size={28} /></div></div>\n\n return (\n <div style={{ display: 'flex', flexDirection: 'column', gap: 14, maxWidth: 820 }}>\n {header}\n\n <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>\n <StatCard value={payload.totalChanges} label={t('totalChanges')} />\n <StatCard value={payload.files.length} label={t('filesTracked')} />\n <StatCard value={payload.watchedFiles.length} label={t('watched')} />\n </div>\n\n {payload.files.length > 0 && (\n <>\n <SectionTitle icon=\"\uD83D\uDCC1\">{t('files')}</SectionTitle>\n <Card padding={0}>\n <table style={tableStyles.table}>\n <thead><tr><th style={tableStyles.th}>{t('file')}</th><th style={tableStyles.th}>{t('changes')}</th><th style={tableStyles.th}>{t('lastChanged')}</th><th style={tableStyles.th}>{t('size')}</th></tr></thead>\n <tbody>\n {payload.files.map((f) => (\n <tr key={f.file}><td style={tableStyles.td}><code style={{ fontSize: 11 }}>{f.file}</code></td>\n <td style={tableStyles.td}>{f.changes}</td><td style={tableStyles.td}>{new Date(f.lastChanged).toLocaleString()}</td><td style={tableStyles.td}>{f.currentSize}B</td></tr>\n ))}\n </tbody>\n </table>\n </Card>\n </>\n )}\n\n <SectionTitle icon=\"\uD83D\uDD52\">{t('timeline')}</SectionTitle>\n {payload.timeline.length === 0 ? <EmptyState icon=\"\uD83D\uDCED\" message={t('empty')} /> : (\n <Card padding={0}>\n <table style={tableStyles.table}>\n <thead><tr><th style={tableStyles.th}>{t('hash')}</th><th style={tableStyles.th}>{t('date')}</th><th style={tableStyles.th}>{t('by')}</th><th style={tableStyles.th}>{t('file')}</th><th style={tableStyles.th}>{t('added')}</th><th style={tableStyles.th}>{t('removed')}</th></tr></thead>\n <tbody>\n {payload.timeline.map((row) => (\n <tr key={row.hash} style={tableStyles.clickRow} onClick={() => setChangeModal(row.hash)}>\n <td style={tableStyles.td}><code style={{ fontSize: 11 }}>{row.hash.slice(0, 8)}</code></td>\n <td style={tableStyles.td}>{new Date(row.timestamp).toLocaleString()}</td>\n <td style={tableStyles.td}>{row.changedBy}</td><td style={tableStyles.td}>{row.file}</td>\n <td style={{ ...tableStyles.td, color: 'var(--success, #2e7d32)' }}>+{row.addedLines}</td>\n <td style={{ ...tableStyles.td, color: 'var(--error, #e53935)' }}>-{row.removedLines}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </Card>\n )}\n\n {changeModal !== null && <ChangeModal hash={changeModal} t={t} onClose={() => setChangeModal(null)} onRollback={reload} />}\n </div>\n )\n}\n\nexport function PromptVcsPanel({ t }: PanelProps): ReactNode {\n return <ToastProvider><PromptVcsPanelInner t={t} /></ToastProvider>\n}\n", "/**\n * Shared UI component kit for dsh plugin panels.\n *\n * Self-contained: imports nothing but React. All styling is inline + CSS custom\n * properties so the components follow the host theme. A single <style> tag injects\n * keyframes the first time any component mounts.\n *\n * @module client/ui\n */\n\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useState,\n} from 'react'\nimport type {\n ButtonHTMLAttributes,\n CSSProperties,\n InputHTMLAttributes,\n ReactNode,\n SelectHTMLAttributes,\n} from 'react'\n\n// \u2500\u2500\u2500 Theme tokens \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst T = {\n accent: 'var(--accent, #4B8BBE)',\n accentHover: 'var(--accent-hover, #3a6f9e)',\n error: 'var(--error, #e53935)',\n success: 'var(--success, #2e7d32)',\n warning: 'var(--warning, #ed6c02)',\n bg: 'var(--bg-secondary, rgba(128,128,128,0.04))',\n bgHover: 'var(--bg-tertiary, rgba(128,128,128,0.08))',\n border: 'var(--border, rgba(128,128,128,0.2))',\n text: 'var(--text-primary, inherit)',\n muted: 'var(--text-secondary, rgba(128,128,128,0.65))',\n surface: 'var(--bg-primary, #fff)',\n}\n\n// \u2500\u2500\u2500 Style injection (once) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nlet injected = false\nfunction ensureStyles(): void {\n if (injected || typeof document === 'undefined') return\n injected = true\n const el = document.createElement('style')\n el.textContent = `\n@keyframes dsh-fade-in{from{opacity:0}to{opacity:1}}\n@keyframes dsh-slide-up{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}\n@keyframes dsh-spin{to{transform:rotate(360deg)}}\n.dsh-modal-overlay{animation:dsh-fade-in .15s ease}\n.dsh-modal-body{animation:dsh-slide-up .2s ease}\n.dsh-toast{animation:dsh-slide-up .2s ease}\n.dsh-spin{animation:dsh-spin .6s linear infinite}\n `\n document.head.appendChild(el)\n}\n\n// \u2500\u2500\u2500 Modal \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function Modal({\n title,\n onClose,\n children,\n footer,\n width = 520,\n}: {\n title: string\n onClose: () => void\n children: ReactNode\n footer?: ReactNode\n width?: number\n}): ReactNode {\n useEffect(() => {\n ensureStyles()\n const handler = (e: KeyboardEvent): void => {\n if (e.key === 'Escape') onClose()\n }\n document.addEventListener('keydown', handler)\n return () => document.removeEventListener('keydown', handler)\n }, [onClose])\n\n return (\n <div\n className=\"dsh-modal-overlay\"\n style={{\n position: 'fixed',\n inset: 0,\n zIndex: 1000,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n background: 'rgba(0,0,0,0.45)',\n backdropFilter: 'blur(3px)',\n WebkitBackdropFilter: 'blur(3px)',\n }}\n onClick={onClose}\n >\n <div\n className=\"dsh-modal-body\"\n style={{\n background: T.surface,\n borderRadius: 14,\n boxShadow: '0 12px 40px rgba(0,0,0,0.25)',\n width: `min(92vw, ${width}px)`,\n maxHeight: '85vh',\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n }}\n onClick={(e) => e.stopPropagation()}\n >\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '16px 22px',\n borderBottom: `1px solid ${T.border}`,\n }}\n >\n <h3 style={{ margin: 0, fontSize: 15, fontWeight: 600 }}>{title}</h3>\n <button\n type=\"button\"\n onClick={onClose}\n style={{\n background: 'none',\n border: 'none',\n cursor: 'pointer',\n fontSize: 20,\n color: T.muted,\n padding: '0 4px',\n lineHeight: 1,\n borderRadius: 4,\n }}\n aria-label=\"Close\"\n >\n \u00D7\n </button>\n </div>\n <div style={{ padding: 22, overflow: 'auto', flex: 1 }}>{children}</div>\n {footer !== undefined && (\n <div\n style={{\n padding: '14px 22px',\n borderTop: `1px solid ${T.border}`,\n display: 'flex',\n justifyContent: 'flex-end',\n gap: 10,\n background: T.bg,\n }}\n >\n {footer}\n </div>\n )}\n </div>\n </div>\n )\n}\n\n// \u2500\u2500\u2500 ConfirmDialog \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function ConfirmDialog({\n title,\n message,\n confirmLabel,\n cancelLabel = 'Cancel',\n onConfirm,\n onClose,\n danger,\n}: {\n title: string\n message: string\n confirmLabel: string\n cancelLabel?: string\n onConfirm: () => void\n onClose: () => void\n danger?: boolean\n}): ReactNode {\n return (\n <Modal\n title={title}\n onClose={onClose}\n width={420}\n footer={\n <>\n <Button variant=\"secondary\" onClick={onClose}>\n {cancelLabel}\n </Button>\n <Button\n variant={danger === true ? 'danger' : 'primary'}\n onClick={() => {\n onConfirm()\n onClose()\n }}\n >\n {confirmLabel}\n </Button>\n </>\n }\n >\n <p style={{ margin: 0, fontSize: 14, lineHeight: 1.6 }}>{message}</p>\n </Modal>\n )\n}\n\n// \u2500\u2500\u2500 Button \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function Button({\n variant = 'secondary',\n size = 'md',\n children,\n style,\n ...rest\n}: {\n variant?: 'primary' | 'secondary' | 'danger' | 'ghost'\n size?: 'sm' | 'md'\n children: ReactNode\n style?: CSSProperties\n} & ButtonHTMLAttributes<HTMLButtonElement>): ReactNode {\n const base: CSSProperties = {\n cursor: 'pointer',\n borderRadius: 7,\n fontWeight: 500,\n transition: 'all 0.15s ease',\n border: 'none',\n display: 'inline-flex',\n alignItems: 'center',\n gap: 4,\n fontSize: size === 'sm' ? 12 : 13,\n padding: size === 'sm' ? '5px 12px' : '8px 18px',\n fontFamily: 'inherit',\n }\n const variants: Record<string, CSSProperties> = {\n primary: { background: T.accent, color: '#fff' },\n secondary: {\n background: T.bg,\n color: T.text,\n border: `1px solid ${T.border}`,\n },\n danger: {\n background: 'transparent',\n color: T.error,\n border: `1px solid ${T.error}`,\n },\n ghost: { background: 'transparent', color: T.muted, border: 'none' },\n }\n return (\n <button type=\"button\" style={{ ...base, ...variants[variant], ...style }} {...rest}>\n {children}\n </button>\n )\n}\n\n// \u2500\u2500\u2500 Card \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function Card({\n title,\n icon,\n children,\n actions,\n style,\n padding = 14,\n}: {\n title?: string\n icon?: string\n children: ReactNode\n actions?: ReactNode\n style?: CSSProperties\n padding?: number\n}): ReactNode {\n return (\n <div\n style={{\n borderRadius: 10,\n border: `1px solid ${T.border}`,\n background: T.bg,\n overflow: 'hidden',\n ...style,\n }}\n >\n {(title !== undefined || actions !== undefined) && (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '12px 16px',\n borderBottom: title !== undefined ? `1px solid ${T.border}` : 'none',\n }}\n >\n <span style={{ fontSize: 13, fontWeight: 600, display: 'flex', alignItems: 'center', gap: 6 }}>\n {icon !== undefined && <span>{icon}</span>}\n {title}\n </span>\n {actions}\n </div>\n )}\n <div style={{ padding }}>{children}</div>\n </div>\n )\n}\n\n// \u2500\u2500\u2500 StatCard \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function StatCard({\n value,\n label,\n color,\n unit,\n}: {\n value: string | number\n label: string\n color?: string\n unit?: string\n}): ReactNode {\n return (\n <div\n style={{\n flex: 1,\n minWidth: 90,\n textAlign: 'center',\n padding: '14px 10px',\n borderRadius: 10,\n border: `1px solid ${T.border}`,\n background: T.bg,\n }}\n >\n <div style={{ fontSize: 24, fontWeight: 700, color: color ?? T.text, lineHeight: 1.2 }}>\n {value}\n {unit !== undefined && (\n <span style={{ fontSize: 13, fontWeight: 500, opacity: 0.7, marginLeft: 2 }}>{unit}</span>\n )}\n </div>\n <div style={{ fontSize: 11, color: T.muted, marginTop: 4 }}>{label}</div>\n </div>\n )\n}\n\n// \u2500\u2500\u2500 Badge \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function Badge({\n children,\n color = 'default',\n}: {\n children: ReactNode\n color?: 'default' | 'success' | 'error' | 'warning' | 'info'\n}): ReactNode {\n const colors: Record<string, { bg: string; fg: string }> = {\n default: { bg: 'rgba(128,128,128,0.12)', fg: T.muted },\n success: { bg: 'rgba(46,125,50,0.12)', fg: T.success },\n error: { bg: 'rgba(229,57,53,0.12)', fg: T.error },\n warning: { bg: 'rgba(245,124,0,0.12)', fg: T.warning },\n info: { bg: 'rgba(75,139,190,0.12)', fg: T.accent },\n }\n const c = colors[color]\n return (\n <span\n style={{\n display: 'inline-block',\n padding: '2px 9px',\n borderRadius: 10,\n fontSize: 11,\n fontWeight: 600,\n background: c.bg,\n color: c.fg,\n }}\n >\n {children}\n </span>\n )\n}\n\n// \u2500\u2500\u2500 Input \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function Input({\n style,\n ...rest\n}: { style?: CSSProperties } & InputHTMLAttributes<HTMLInputElement>): ReactNode {\n return (\n <input\n {...rest}\n style={{\n fontSize: 13,\n padding: '8px 12px',\n borderRadius: 7,\n border: `1px solid ${T.border}`,\n background: T.surface,\n color: T.text,\n width: '100%',\n boxSizing: 'border-box',\n fontFamily: 'inherit',\n outline: 'none',\n transition: 'border-color 0.15s',\n ...style,\n }}\n />\n )\n}\n\n// \u2500\u2500\u2500 Select \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function Select({\n style,\n children,\n ...rest\n}: {\n style?: CSSProperties\n children: ReactNode\n} & SelectHTMLAttributes<HTMLSelectElement>): ReactNode {\n return (\n <select\n {...rest}\n style={{\n fontSize: 13,\n padding: '8px 12px',\n borderRadius: 7,\n border: `1px solid ${T.border}`,\n background: T.surface,\n color: T.text,\n cursor: 'pointer',\n fontFamily: 'inherit',\n outline: 'none',\n ...style,\n }}\n >\n {children}\n </select>\n )\n}\n\n// \u2500\u2500\u2500 Textarea \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function Textarea({\n style,\n ...rest\n}: { style?: CSSProperties } & React.TextareaHTMLAttributes<HTMLTextAreaElement>): ReactNode {\n return (\n <textarea\n {...rest}\n style={{\n fontSize: 13,\n padding: '8px 12px',\n borderRadius: 7,\n border: `1px solid ${T.border}`,\n background: T.surface,\n color: T.text,\n width: '100%',\n boxSizing: 'border-box',\n fontFamily: 'inherit',\n outline: 'none',\n resize: 'vertical',\n lineHeight: 1.5,\n ...style,\n }}\n />\n )\n}\n\n// \u2500\u2500\u2500 Toast \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\ninterface ToastItem {\n id: number\n type: 'success' | 'error' | 'info'\n message: string\n}\n\nconst ToastContext = createContext<(type: ToastItem['type'], message: string) => void>(\n () => {},\n)\n\nexport function ToastProvider({ children }: { children: ReactNode }): ReactNode {\n const [toasts, setToasts] = useState<ToastItem[]>([])\n const show = useCallback((type: ToastItem['type'], message: string) => {\n const id = Date.now() + Math.random()\n setToasts((t) => [...t, { id, type, message }])\n setTimeout(() => setToasts((t) => t.filter((x) => x.id !== id)), 3500)\n }, [])\n return (\n <ToastContext.Provider value={show}>\n {children}\n {toasts.length > 0 && (\n <div\n style={{\n position: 'fixed',\n bottom: 24,\n right: 24,\n zIndex: 2000,\n display: 'flex',\n flexDirection: 'column',\n gap: 8,\n }}\n >\n {toasts.map((t) => (\n <div\n key={t.id}\n className=\"dsh-toast\"\n style={{\n padding: '12px 18px',\n borderRadius: 8,\n fontSize: 13,\n fontWeight: 500,\n boxShadow: '0 6px 20px rgba(0,0,0,0.2)',\n color: '#fff',\n background:\n t.type === 'success'\n ? T.success\n : t.type === 'error'\n ? T.error\n : T.accent,\n maxWidth: 360,\n }}\n >\n {t.message}\n </div>\n ))}\n </div>\n )}\n </ToastContext.Provider>\n )\n}\n\nexport function useToast(): (type: ToastItem['type'], message: string) => void {\n return useContext(ToastContext)\n}\n\n// \u2500\u2500\u2500 Spinner \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function Spinner({ size = 16 }: { size?: number }): ReactNode {\n ensureStyles()\n return (\n <span\n className=\"dsh-spin\"\n style={{\n display: 'inline-block',\n width: size,\n height: size,\n border: `2px solid ${T.border}`,\n borderTopColor: T.accent,\n borderRadius: '50%',\n }}\n />\n )\n}\n\n// \u2500\u2500\u2500 EmptyState \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function EmptyState({\n icon,\n message,\n}: {\n icon?: string\n message: string\n}): ReactNode {\n return (\n <div\n style={{\n textAlign: 'center',\n padding: '36px 16px',\n color: T.muted,\n }}\n >\n {icon !== undefined && (\n <div style={{ fontSize: 36, marginBottom: 10, opacity: 0.4 }}>{icon}</div>\n )}\n <div style={{ fontSize: 13 }}>{message}</div>\n </div>\n )\n}\n\n// \u2500\u2500\u2500 SectionTitle \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function SectionTitle({\n icon,\n children,\n actions,\n}: {\n icon?: string\n children: ReactNode\n actions?: ReactNode\n}): ReactNode {\n return (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n marginTop: 6,\n marginBottom: 8,\n }}\n >\n <span\n style={{\n fontSize: 14,\n fontWeight: 600,\n display: 'flex',\n alignItems: 'center',\n gap: 6,\n }}\n >\n {icon !== undefined && <span>{icon}</span>}\n {children}\n </span>\n {actions}\n </div>\n )\n}\n\n// \u2500\u2500\u2500 Table helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport const tableStyles = {\n table: {\n borderCollapse: 'collapse' as const,\n width: '100%' as const,\n },\n th: {\n textAlign: 'left' as const,\n padding: '8px 12px 8px 0',\n fontWeight: 600 as const,\n fontSize: 12 as const,\n color: T.muted,\n borderBottom: `1px solid ${T.border}`,\n },\n td: {\n padding: '8px 12px 8px 0',\n fontSize: 13 as const,\n borderBottom: '1px solid rgba(128,128,128,0.08)',\n },\n clickRow: { cursor: 'pointer' as const },\n}\n\n// \u2500\u2500\u2500 CodeBlock \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function CodeBlock({\n children,\n maxHeight = 200,\n style,\n}: {\n children: string\n maxHeight?: number\n style?: CSSProperties\n}): ReactNode {\n return (\n <pre\n style={{\n whiteSpace: 'pre-wrap',\n wordBreak: 'break-word',\n fontSize: 12,\n maxHeight,\n overflow: 'auto',\n padding: 10,\n borderRadius: 7,\n background: 'rgba(128,128,128,0.06)',\n border: `1px solid ${T.border}`,\n margin: 0,\n ...style,\n }}\n >\n {children}\n </pre>\n )\n}\n\n// \u2500\u2500\u2500 ProgressBar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function ProgressBar({\n pct,\n color,\n height = 18,\n}: {\n pct: number\n color?: string\n height?: number\n}): ReactNode {\n return (\n <div\n style={{\n width: '100%',\n height,\n background: 'rgba(128,128,128,0.12)',\n borderRadius: 5,\n overflow: 'hidden',\n position: 'relative',\n }}\n >\n <div\n style={{\n width: `${Math.min(pct, 100)}%`,\n height: '100%',\n background: color ?? T.accent,\n borderRadius: 5,\n transition: 'width 0.3s ease',\n }}\n />\n </div>\n )\n}\n\n// \u2500\u2500\u2500 Field label \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function Field({\n label,\n children,\n hint,\n}: {\n label: string\n children: ReactNode\n hint?: string\n}): ReactNode {\n return (\n <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>\n <label style={{ fontSize: 12, fontWeight: 600, color: T.text }}>{label}</label>\n {children}\n {hint !== undefined && (\n <span style={{ fontSize: 11, color: T.muted }}>{hint}</span>\n )}\n </div>\n )\n}\n\n// \u2500\u2500\u2500 usePanel hook (shared fetch logic) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function usePanel<P>(path: string): {\n payload: P | null\n error: string | null\n reload: () => void\n} {\n const [payload, setPayload] = useState<P | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [tick, setTick] = useState(0)\n const reload = useCallback(() => setTick((v) => v + 1), [])\n\n useEffect(() => {\n const c = new AbortController()\n setError(null)\n fetch(path, { signal: c.signal })\n .then(async (r) => {\n if (!r.ok) throw new Error(String(r.status))\n return r.json() as Promise<P>\n })\n .then((p) => {\n if (!c.signal.aborted) setPayload(p)\n })\n .catch((e: unknown) => {\n if (!c.signal.aborted) setPayload(null), setError(e instanceof Error ? e.message : String(e))\n })\n return () => c.abort()\n }, [tick, path])\n\n return useMemo(() => ({ payload, error, reload }), [payload, error, reload])\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n * Browser half of dsh-prompt-vcs: its page inside Settings.\n *\n * Two services are injected by their runtime identity, not by the packages that provide\n * them: the settings shell is `slots`, the dictionary registry is `locale`. The label and\n * every string inside the component are translated through this plugin's own namespace,\n * which is why the page follows the UI language instead of the plugin author's.\n *\n * @module client\n */\n\nimport type { ComponentType } from 'react'\n\nimport { NS, en, zh } from './locales.js'\nimport { PromptVcsPanel } from './view.js'\n\nexport const inject = ['slots', 'locale']\n\ninterface SlotsService {\n inject(name: string, register: () => void): void\n register(\n options: { name: string; id: string; order: number; label: () => string; locale: string },\n component: ComponentType<{ t: (key: string, params?: Record<string, unknown>) => string }>,\n ): unknown\n}\n\ninterface ClientContext {\n slots: SlotsService\n locale: {\n register(ns: string, dicts: { zh: unknown; en: unknown }): () => void\n bind(ns: string): (key: string, params?: Record<string, unknown>) => string\n }\n effect(callback: () => unknown, label?: string): unknown\n}\n\nexport function apply(ctx: ClientContext): void {\n // `zh` is the key-set source of truth, matching the official client plugins.\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'dsh-prompt-vcs: dictionaries')\n\n ctx.slots.inject('settings.section', () => ctx.slots.register(\n {\n name: 'settings.section',\n id: 'prompt-vcs',\n order: 44,\n label: () => ctx.locale.bind(NS)('nav'),\n locale: NS,\n },\n PromptVcsPanel,\n ))\n}\n", "/**\n * Dictionaries for the Prompt VCS page.\n *\n * `zh` is the key-set source of truth, as in the official client plugins, and `en` is\n * typed against it: a key translated in one language but not the other fails the build\n * instead of silently rendering the raw key.\n *\n * @module client/locales\n */\n\n/** Dictionary namespace owned by this plugin. */\nexport const NS = 'promptVcs'\n\n/** Simplified Chinese dictionary (the key-set source of truth). */\nexport const zh = {\n 'nav': '\u63D0\u793A\u8BCD\u7248\u672C',\n 'title': '\u63D0\u793A\u8BCD\u7248\u672C',\n 'hash': '\u7248\u672C\u53F7',\n 'date': '\u65F6\u95F4',\n 'by': '\u4FEE\u6539\u65B9',\n 'file': '\u6587\u4EF6',\n 'added': '\u65B0\u589E',\n 'removed': '\u5220\u9664',\n 'empty': '\u8FD8\u6CA1\u6709\u8BB0\u5F55\u5230\u6307\u4EE4\u6539\u52A8\u3002',\n 'refresh': '\u5237\u65B0',\n 'loading': '\u6B63\u5728\u52A0\u8F7D\u2026',\n 'failed': '\u52A0\u8F7D\u5931\u8D25',\n 'retry': '\u91CD\u8BD5',\n 'totalChanges': '\u603B\u53D8\u66F4\u6570',\n 'filesTracked': '\u8DDF\u8E2A\u6587\u4EF6\u6570',\n 'watched': '\u76D1\u63A7\u6587\u4EF6',\n 'files': '\u6587\u4EF6',\n 'changes': '\u53D8\u66F4\u6B21\u6570',\n 'lastChanged': '\u6700\u540E\u53D8\u66F4',\n 'size': '\u5927\u5C0F',\n 'timeline': '\u65F6\u95F4\u7EBF',\n 'diff': '\u5DEE\u5F02',\n 'before': '\u53D8\u66F4\u524D',\n 'after': '\u53D8\u66F4\u540E',\n 'rollback': '\u56DE\u6EDA',\n 'confirmRollback': '\u786E\u5B9A\u56DE\u6EDA\u5230\u6B64\u53D8\u66F4\u4E4B\u524D\u7684\u72B6\u6001\uFF1F',\n 'notFound': '\u672A\u627E\u5230\u53D8\u66F4\u8BB0\u5F55',\n}\n\n/** English dictionary, checked complete against the zh key set. */\nexport const en: typeof zh = {\n 'nav': 'Prompt VCS',\n 'title': 'Prompt VCS',\n 'hash': 'Hash',\n 'date': 'Date',\n 'by': 'Changed by',\n 'file': 'File',\n 'added': 'Added',\n 'removed': 'Removed',\n 'empty': 'No instruction changes recorded yet.',\n 'refresh': 'Refresh',\n 'loading': 'Loading\u2026',\n 'failed': 'Failed to load',\n 'retry': 'Retry',\n 'totalChanges': 'Total Changes',\n 'filesTracked': 'Files Tracked',\n 'watched': 'Watched',\n 'files': 'Files',\n 'changes': 'Changes',\n 'lastChanged': 'Last Changed',\n 'size': 'Size',\n 'timeline': 'Timeline',\n 'diff': 'Diff',\n 'before': 'Before',\n 'after': 'After',\n 'rollback': 'Rollback',\n 'confirmRollback': 'Rollback to before this change?',\n 'notFound': 'Change not found',\n}\n", "/**\n * Pure rendering half of the promptVcs page. Uses shared UI kit.\n * @module client/view\n */\n\nimport { useCallback, useState } from 'react'\nimport type { ReactNode } from 'react'\n\nimport type { PanelPayload, Change } from '../types.js'\nimport {\n Badge, Button, Card, CodeBlock, ConfirmDialog, EmptyState, Modal,\n SectionTitle, Spinner, StatCard, ToastProvider, tableStyles, usePanel, useToast,\n} from './ui.js'\n\nexport type Translate = (key: string, params?: Record<string, unknown>) => string\nexport interface PanelProps { t: Translate }\n\nconst PANEL_PATH = '/api/vcs.panel'\nconst CHANGE_PATH = '/api/vcs.change'\nconst ROLLBACK_PATH = '/api/vcs.rollback'\n\nfunction ChangeModal({ hash, t, onClose, onRollback }: {\n hash: string; t: Translate; onClose: () => void; onRollback: () => void\n}): ReactNode {\n const toast = useToast()\n const [change, setChange] = useState<Change | null>(null)\n const [loading, setLoading] = useState(true)\n const [confirmRb, setConfirmRb] = useState(false)\n const [rolling, setRolling] = useState(false)\n\n useCallback(() => {\n setLoading(true)\n fetch(`${CHANGE_PATH}?hash=${hash}`).then(async (r) => { if (r.ok) setChange(await r.json() as Change) }).finally(() => setLoading(false))\n }, [hash])()\n\n const handleRollback = useCallback(async () => {\n setRolling(true)\n try {\n const r = await fetch(ROLLBACK_PATH, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ hash }) })\n if (r.ok) { toast('success', t('rolledBack')); onRollback(); onClose() }\n } finally { setRolling(false) }\n }, [hash, t, toast, onRollback, onClose])\n\n return (\n <Modal title={`${t('change')} ${hash.slice(0, 8)}\u2026`} onClose={onClose} width={760}\n footer={<Button variant=\"danger\" size=\"sm\" disabled={rolling} onClick={() => setConfirmRb(true)}>\u21A9 {t('rollback')}</Button>}>\n {loading ? <div style={{ display: 'flex', justifyContent: 'center', padding: 20 }}><Spinner size={24} /></div>\n : change === null ? <EmptyState message={t('notFound')} />\n : (\n <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>\n <div style={{ fontSize: 13, fontWeight: 600 }}>{change.file}</div>\n <Card title={t('diff')}><CodeBlock>{change.diff}</CodeBlock></Card>\n <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>\n <div style={{ flex: 1, minWidth: 250 }}>\n <Badge color=\"error\">{t('before')}</Badge>\n <CodeBlock style={{ marginTop: 6 }} maxHeight={180}>{`${change.oldContent.slice(0, 2000)}${change.oldContent.length > 2000 ? '\\n\u2026' : ''}`}</CodeBlock>\n </div>\n <div style={{ flex: 1, minWidth: 250 }}>\n <Badge color=\"success\">{t('after')}</Badge>\n <CodeBlock style={{ marginTop: 6 }} maxHeight={180}>{`${change.newContent.slice(0, 2000)}${change.newContent.length > 2000 ? '\\n\u2026' : ''}`}</CodeBlock>\n </div>\n </div>\n </div>\n )}\n {confirmRb && <ConfirmDialog title={t('rollback')} message={t('confirmRollback')} confirmLabel={t('rollback')} danger\n onConfirm={handleRollback} onClose={() => setConfirmRb(false)} />}\n </Modal>\n )\n}\n\nfunction PromptVcsPanelInner({ t }: PanelProps): ReactNode {\n const { payload, error, reload } = usePanel<PanelPayload>(PANEL_PATH)\n const [changeModal, setChangeModal] = useState<string | null>(null)\n\n const header = (\n <header style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 4 }}>\n <strong style={{ fontSize: 15 }}>\uD83D\uDCDD {t('title')}</strong>\n <span style={{ flex: 1 }} />\n <Button variant=\"secondary\" size=\"sm\" onClick={reload}>{t('refresh')}</Button>\n </header>\n )\n\n if (error !== null) return <div style={{ maxWidth: 820 }}>{header}<Card><p role=\"alert\" style={{ margin: 0, fontSize: 13, color: 'var(--error, #e53935)' }}>{t('failed')}: {error}</p></Card></div>\n if (payload === null) return <div style={{ maxWidth: 820 }}>{header}<div style={{ display: 'flex', justifyContent: 'center', padding: 40 }}><Spinner size={28} /></div></div>\n\n return (\n <div style={{ display: 'flex', flexDirection: 'column', gap: 14, maxWidth: 820 }}>\n {header}\n\n <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>\n <StatCard value={payload.totalChanges} label={t('totalChanges')} />\n <StatCard value={payload.files.length} label={t('filesTracked')} />\n <StatCard value={payload.watchedFiles.length} label={t('watched')} />\n </div>\n\n {payload.files.length > 0 && (\n <>\n <SectionTitle icon=\"\uD83D\uDCC1\">{t('files')}</SectionTitle>\n <Card padding={0}>\n <table style={tableStyles.table}>\n <thead><tr><th style={tableStyles.th}>{t('file')}</th><th style={tableStyles.th}>{t('changes')}</th><th style={tableStyles.th}>{t('lastChanged')}</th><th style={tableStyles.th}>{t('size')}</th></tr></thead>\n <tbody>\n {payload.files.map((f) => (\n <tr key={f.file}><td style={tableStyles.td}><code style={{ fontSize: 11 }}>{f.file}</code></td>\n <td style={tableStyles.td}>{f.changes}</td><td style={tableStyles.td}>{new Date(f.lastChanged).toLocaleString()}</td><td style={tableStyles.td}>{f.currentSize}B</td></tr>\n ))}\n </tbody>\n </table>\n </Card>\n </>\n )}\n\n <SectionTitle icon=\"\uD83D\uDD52\">{t('timeline')}</SectionTitle>\n {payload.timeline.length === 0 ? <EmptyState icon=\"\uD83D\uDCED\" message={t('empty')} /> : (\n <Card padding={0}>\n <table style={tableStyles.table}>\n <thead><tr><th style={tableStyles.th}>{t('hash')}</th><th style={tableStyles.th}>{t('date')}</th><th style={tableStyles.th}>{t('by')}</th><th style={tableStyles.th}>{t('file')}</th><th style={tableStyles.th}>{t('added')}</th><th style={tableStyles.th}>{t('removed')}</th></tr></thead>\n <tbody>\n {payload.timeline.map((row) => (\n <tr key={row.hash} style={tableStyles.clickRow} onClick={() => setChangeModal(row.hash)}>\n <td style={tableStyles.td}><code style={{ fontSize: 11 }}>{row.hash.slice(0, 8)}</code></td>\n <td style={tableStyles.td}>{new Date(row.timestamp).toLocaleString()}</td>\n <td style={tableStyles.td}>{row.changedBy}</td><td style={tableStyles.td}>{row.file}</td>\n <td style={{ ...tableStyles.td, color: 'var(--success, #2e7d32)' }}>+{row.addedLines}</td>\n <td style={{ ...tableStyles.td, color: 'var(--error, #e53935)' }}>-{row.removedLines}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </Card>\n )}\n\n {changeModal !== null && <ChangeModal hash={changeModal} t={t} onClose={() => setChangeModal(null)} onRollback={reload} />}\n </div>\n )\n}\n\nexport function PromptVcsPanel({ t }: PanelProps): ReactNode {\n return <ToastProvider><PromptVcsPanelInner t={t} /></ToastProvider>\n}\n", "/**\n * Shared UI component kit for dsh plugin panels.\n *\n * Self-contained: imports nothing but React. All styling is inline + CSS custom\n * properties so the components follow the host theme. A single <style> tag injects\n * keyframes the first time any component mounts.\n *\n * @module client/ui\n */\n\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useState,\n} from 'react'\nimport type {\n ButtonHTMLAttributes,\n CSSProperties,\n InputHTMLAttributes,\n ReactNode,\n SelectHTMLAttributes,\n} from 'react'\n\n// \u2500\u2500\u2500 Theme tokens \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n/**\n * Host theme tokens.\n *\n * Every name here is a real dsh token, read out of the shipped stylesheets\n * (`@deepseek-ai/dsh-client-ui-theme`), which define them on `body`. An earlier\n * version used invented names \u2014 `--accent`, `--border`, `--bg-primary`,\n * `--text-primary` and friends \u2014 that dsh does not define at all. The\n * hardcoded fallbacks then applied in every theme, so each panel rendered its\n * own fixed palette instead of following the host, and the modal and input\n * surfaces stayed white in dark mode.\n *\n * No fallbacks on purpose: if a name were ever missing, the declaration becomes\n * invalid at computed-value time and the property inherits, which degrades\n * gracefully, whereas a hardcoded fallback bakes in a color that is wrong in one\n * of the two themes. `tests/ui-tokens.test.mjs` enforces the name list.\n */\nconst T = {\n accent: 'var(--dsw-alias-brand-primary)',\n accentHover: 'var(--dsw-alias-button-primary-hover)',\n inverted: 'var(--dsw-alias-label-primary-inverted)',\n error: 'var(--dsw-alias-state-error-primary)',\n success: 'var(--dsw-alias-state-success-primary)',\n warning: 'var(--dsw-alias-state-warn-primary)',\n bg: 'var(--dsw-alias-bg-layer-2)',\n bgHover: 'var(--dsw-alias-interactive-bg-hover)',\n border: 'var(--dsw-alias-border-l2)',\n text: 'var(--dsw-alias-label-primary)',\n muted: 'var(--dsw-alias-label-tertiary)',\n surface: 'var(--dsw-alias-bg-layer-1)',\n mask: 'var(--dsw-alias-bg-mask-1)',\n}\n\n// \u2500\u2500\u2500 Style injection (once) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nlet injected = false\nfunction ensureStyles(): void {\n if (injected || typeof document === 'undefined') return\n injected = true\n const el = document.createElement('style')\n el.textContent = `\n@keyframes dsh-fade-in{from{opacity:0}to{opacity:1}}\n@keyframes dsh-slide-up{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}\n@keyframes dsh-spin{to{transform:rotate(360deg)}}\n.dsh-modal-overlay{animation:dsh-fade-in .15s ease}\n.dsh-modal-body{animation:dsh-slide-up .2s ease}\n.dsh-toast{animation:dsh-slide-up .2s ease}\n.dsh-spin{animation:dsh-spin .6s linear infinite}\n `\n document.head.appendChild(el)\n}\n\n// \u2500\u2500\u2500 Modal \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function Modal({\n title,\n onClose,\n children,\n footer,\n width = 520,\n}: {\n title: string\n onClose: () => void\n children: ReactNode\n footer?: ReactNode\n width?: number\n}): ReactNode {\n useEffect(() => {\n ensureStyles()\n const handler = (e: KeyboardEvent): void => {\n if (e.key === 'Escape') onClose()\n }\n document.addEventListener('keydown', handler)\n return () => document.removeEventListener('keydown', handler)\n }, [onClose])\n\n return (\n <div\n className=\"dsh-modal-overlay\"\n style={{\n position: 'fixed',\n inset: 0,\n zIndex: 1000,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n background: T.mask,\n backdropFilter: 'blur(3px)',\n WebkitBackdropFilter: 'blur(3px)',\n }}\n onClick={onClose}\n >\n <div\n className=\"dsh-modal-body\"\n style={{\n background: T.surface,\n borderRadius: 14,\n boxShadow: 'var(--dsw-elevation-prominent)',\n width: `min(92vw, ${width}px)`,\n maxHeight: '85vh',\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n }}\n onClick={(e) => e.stopPropagation()}\n >\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '16px 22px',\n borderBottom: `1px solid ${T.border}`,\n }}\n >\n <h3 style={{ margin: 0, fontSize: 15, fontWeight: 600 }}>{title}</h3>\n <button\n type=\"button\"\n onClick={onClose}\n style={{\n background: 'none',\n border: 'none',\n cursor: 'pointer',\n fontSize: 20,\n color: T.muted,\n padding: '0 4px',\n lineHeight: 1,\n borderRadius: 4,\n }}\n aria-label=\"Close\"\n >\n \u00D7\n </button>\n </div>\n <div style={{ padding: 22, overflow: 'auto', flex: 1 }}>{children}</div>\n {footer !== undefined && (\n <div\n style={{\n padding: '14px 22px',\n borderTop: `1px solid ${T.border}`,\n display: 'flex',\n justifyContent: 'flex-end',\n gap: 10,\n background: T.bg,\n }}\n >\n {footer}\n </div>\n )}\n </div>\n </div>\n )\n}\n\n// \u2500\u2500\u2500 ConfirmDialog \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function ConfirmDialog({\n title,\n message,\n confirmLabel,\n cancelLabel = 'Cancel',\n onConfirm,\n onClose,\n danger,\n}: {\n title: string\n message: string\n confirmLabel: string\n cancelLabel?: string\n onConfirm: () => void\n onClose: () => void\n danger?: boolean\n}): ReactNode {\n return (\n <Modal\n title={title}\n onClose={onClose}\n width={420}\n footer={\n <>\n <Button variant=\"secondary\" onClick={onClose}>\n {cancelLabel}\n </Button>\n <Button\n variant={danger === true ? 'danger' : 'primary'}\n onClick={() => {\n onConfirm()\n onClose()\n }}\n >\n {confirmLabel}\n </Button>\n </>\n }\n >\n <p style={{ margin: 0, fontSize: 14, lineHeight: 1.6 }}>{message}</p>\n </Modal>\n )\n}\n\n// \u2500\u2500\u2500 Button \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function Button({\n variant = 'secondary',\n size = 'md',\n children,\n style,\n ...rest\n}: {\n variant?: 'primary' | 'secondary' | 'danger' | 'ghost'\n size?: 'sm' | 'md'\n children: ReactNode\n style?: CSSProperties\n} & ButtonHTMLAttributes<HTMLButtonElement>): ReactNode {\n const base: CSSProperties = {\n cursor: 'pointer',\n borderRadius: 7,\n fontWeight: 500,\n transition: 'all 0.15s ease',\n border: 'none',\n display: 'inline-flex',\n alignItems: 'center',\n gap: 4,\n fontSize: size === 'sm' ? 12 : 13,\n padding: size === 'sm' ? '5px 12px' : '8px 18px',\n fontFamily: 'inherit',\n }\n const variants: Record<string, CSSProperties> = {\n primary: { background: T.accent, color: T.inverted },\n secondary: {\n background: T.bg,\n color: T.text,\n border: `1px solid ${T.border}`,\n },\n danger: {\n background: 'transparent',\n color: T.error,\n border: `1px solid ${T.error}`,\n },\n ghost: { background: 'transparent', color: T.muted, border: 'none' },\n }\n return (\n <button type=\"button\" style={{ ...base, ...variants[variant], ...style }} {...rest}>\n {children}\n </button>\n )\n}\n\n// \u2500\u2500\u2500 Card \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function Card({\n title,\n icon,\n children,\n actions,\n style,\n padding = 14,\n}: {\n title?: string\n icon?: string\n children: ReactNode\n actions?: ReactNode\n style?: CSSProperties\n padding?: number\n}): ReactNode {\n return (\n <div\n style={{\n borderRadius: 10,\n border: `1px solid ${T.border}`,\n background: T.bg,\n overflow: 'hidden',\n ...style,\n }}\n >\n {(title !== undefined || actions !== undefined) && (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '12px 16px',\n borderBottom: title !== undefined ? `1px solid ${T.border}` : 'none',\n }}\n >\n <span style={{ fontSize: 13, fontWeight: 600, display: 'flex', alignItems: 'center', gap: 6 }}>\n {icon !== undefined && <span>{icon}</span>}\n {title}\n </span>\n {actions}\n </div>\n )}\n <div style={{ padding }}>{children}</div>\n </div>\n )\n}\n\n// \u2500\u2500\u2500 StatCard \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function StatCard({\n value,\n label,\n color,\n unit,\n}: {\n value: string | number\n label: string\n color?: string\n unit?: string\n}): ReactNode {\n return (\n <div\n style={{\n flex: 1,\n minWidth: 90,\n textAlign: 'center',\n padding: '14px 10px',\n borderRadius: 10,\n border: `1px solid ${T.border}`,\n background: T.bg,\n }}\n >\n <div style={{ fontSize: 24, fontWeight: 700, color: color ?? T.text, lineHeight: 1.2 }}>\n {value}\n {unit !== undefined && (\n <span style={{ fontSize: 13, fontWeight: 500, opacity: 0.7, marginLeft: 2 }}>{unit}</span>\n )}\n </div>\n <div style={{ fontSize: 11, color: T.muted, marginTop: 4 }}>{label}</div>\n </div>\n )\n}\n\n// \u2500\u2500\u2500 Badge \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function Badge({\n children,\n color = 'default',\n}: {\n children: ReactNode\n color?: 'default' | 'success' | 'error' | 'warning' | 'info'\n}): ReactNode {\n const colors: Record<string, { bg: string; fg: string }> = {\n default: { bg: T.bgHover, fg: T.muted },\n success: { bg: 'var(--dsw-alias-state-success-tertiary)', fg: T.success },\n error: { bg: 'var(--dsw-alias-state-error-secondary)', fg: T.error },\n warning: { bg: 'var(--dsw-alias-state-warn-tertiary)', fg: T.warning },\n info: { bg: 'var(--dsw-alias-state-business-tertiary)', fg: T.accent },\n }\n const c = colors[color]\n return (\n <span\n style={{\n display: 'inline-block',\n padding: '2px 9px',\n borderRadius: 10,\n fontSize: 11,\n fontWeight: 600,\n background: c.bg,\n color: c.fg,\n }}\n >\n {children}\n </span>\n )\n}\n\n// \u2500\u2500\u2500 Input \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function Input({\n style,\n ...rest\n}: { style?: CSSProperties } & InputHTMLAttributes<HTMLInputElement>): ReactNode {\n return (\n <input\n {...rest}\n style={{\n fontSize: 13,\n padding: '8px 12px',\n borderRadius: 7,\n border: `1px solid ${T.border}`,\n background: T.surface,\n color: T.text,\n width: '100%',\n boxSizing: 'border-box',\n fontFamily: 'inherit',\n outline: 'none',\n transition: 'border-color 0.15s',\n ...style,\n }}\n />\n )\n}\n\n// \u2500\u2500\u2500 Select \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function Select({\n style,\n children,\n ...rest\n}: {\n style?: CSSProperties\n children: ReactNode\n} & SelectHTMLAttributes<HTMLSelectElement>): ReactNode {\n return (\n <select\n {...rest}\n style={{\n fontSize: 13,\n padding: '8px 12px',\n borderRadius: 7,\n border: `1px solid ${T.border}`,\n background: T.surface,\n color: T.text,\n cursor: 'pointer',\n fontFamily: 'inherit',\n outline: 'none',\n ...style,\n }}\n >\n {children}\n </select>\n )\n}\n\n// \u2500\u2500\u2500 Textarea \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function Textarea({\n style,\n ...rest\n}: { style?: CSSProperties } & React.TextareaHTMLAttributes<HTMLTextAreaElement>): ReactNode {\n return (\n <textarea\n {...rest}\n style={{\n fontSize: 13,\n padding: '8px 12px',\n borderRadius: 7,\n border: `1px solid ${T.border}`,\n background: T.surface,\n color: T.text,\n width: '100%',\n boxSizing: 'border-box',\n fontFamily: 'inherit',\n outline: 'none',\n resize: 'vertical',\n lineHeight: 1.5,\n ...style,\n }}\n />\n )\n}\n\n// \u2500\u2500\u2500 Toast \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\ninterface ToastItem {\n id: number\n type: 'success' | 'error' | 'info'\n message: string\n}\n\nconst ToastContext = createContext<(type: ToastItem['type'], message: string) => void>(\n () => {},\n)\n\nexport function ToastProvider({ children }: { children: ReactNode }): ReactNode {\n const [toasts, setToasts] = useState<ToastItem[]>([])\n const show = useCallback((type: ToastItem['type'], message: string) => {\n const id = Date.now() + Math.random()\n setToasts((t) => [...t, { id, type, message }])\n setTimeout(() => setToasts((t) => t.filter((x) => x.id !== id)), 3500)\n }, [])\n return (\n <ToastContext.Provider value={show}>\n {children}\n {toasts.length > 0 && (\n <div\n style={{\n position: 'fixed',\n bottom: 24,\n right: 24,\n zIndex: 2000,\n display: 'flex',\n flexDirection: 'column',\n gap: 8,\n }}\n >\n {toasts.map((t) => (\n <div\n key={t.id}\n className=\"dsh-toast\"\n style={{\n padding: '12px 18px',\n borderRadius: 8,\n fontSize: 13,\n fontWeight: 500,\n boxShadow: 'var(--dsw-elevation-panel)',\n color: T.inverted,\n background:\n t.type === 'success'\n ? T.success\n : t.type === 'error'\n ? T.error\n : T.accent,\n maxWidth: 360,\n }}\n >\n {t.message}\n </div>\n ))}\n </div>\n )}\n </ToastContext.Provider>\n )\n}\n\nexport function useToast(): (type: ToastItem['type'], message: string) => void {\n return useContext(ToastContext)\n}\n\n// \u2500\u2500\u2500 Spinner \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function Spinner({ size = 16 }: { size?: number }): ReactNode {\n ensureStyles()\n return (\n <span\n className=\"dsh-spin\"\n style={{\n display: 'inline-block',\n width: size,\n height: size,\n border: `2px solid ${T.border}`,\n borderTopColor: T.accent,\n borderRadius: '50%',\n }}\n />\n )\n}\n\n// \u2500\u2500\u2500 EmptyState \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function EmptyState({\n icon,\n message,\n}: {\n icon?: string\n message: string\n}): ReactNode {\n return (\n <div\n style={{\n textAlign: 'center',\n padding: '36px 16px',\n color: T.muted,\n }}\n >\n {icon !== undefined && (\n <div style={{ fontSize: 36, marginBottom: 10, opacity: 0.4 }}>{icon}</div>\n )}\n <div style={{ fontSize: 13 }}>{message}</div>\n </div>\n )\n}\n\n// \u2500\u2500\u2500 SectionTitle \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function SectionTitle({\n icon,\n children,\n actions,\n}: {\n icon?: string\n children: ReactNode\n actions?: ReactNode\n}): ReactNode {\n return (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n marginTop: 6,\n marginBottom: 8,\n }}\n >\n <span\n style={{\n fontSize: 14,\n fontWeight: 600,\n display: 'flex',\n alignItems: 'center',\n gap: 6,\n }}\n >\n {icon !== undefined && <span>{icon}</span>}\n {children}\n </span>\n {actions}\n </div>\n )\n}\n\n// \u2500\u2500\u2500 Table helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport const tableStyles = {\n table: {\n borderCollapse: 'collapse' as const,\n width: '100%' as const,\n },\n th: {\n textAlign: 'left' as const,\n padding: '8px 12px 8px 0',\n fontWeight: 600 as const,\n fontSize: 12 as const,\n color: T.muted,\n borderBottom: `1px solid ${T.border}`,\n },\n td: {\n padding: '8px 12px 8px 0',\n fontSize: 13 as const,\n borderBottom: `1px solid ${T.border}`,\n },\n clickRow: { cursor: 'pointer' as const },\n}\n\n// \u2500\u2500\u2500 CodeBlock \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function CodeBlock({\n children,\n maxHeight = 200,\n style,\n}: {\n children: string\n maxHeight?: number\n style?: CSSProperties\n}): ReactNode {\n return (\n <pre\n style={{\n whiteSpace: 'pre-wrap',\n wordBreak: 'break-word',\n fontSize: 12,\n maxHeight,\n overflow: 'auto',\n padding: 10,\n borderRadius: 7,\n background: T.bg,\n border: `1px solid ${T.border}`,\n margin: 0,\n ...style,\n }}\n >\n {children}\n </pre>\n )\n}\n\n// \u2500\u2500\u2500 ProgressBar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function ProgressBar({\n pct,\n color,\n height = 18,\n}: {\n pct: number\n color?: string\n height?: number\n}): ReactNode {\n return (\n <div\n style={{\n width: '100%',\n height,\n background: T.bgHover,\n borderRadius: 5,\n overflow: 'hidden',\n position: 'relative',\n }}\n >\n <div\n style={{\n width: `${Math.min(pct, 100)}%`,\n height: '100%',\n background: color ?? T.accent,\n borderRadius: 5,\n transition: 'width 0.3s ease',\n }}\n />\n </div>\n )\n}\n\n// \u2500\u2500\u2500 Field label \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function Field({\n label,\n children,\n hint,\n}: {\n label: string\n children: ReactNode\n hint?: string\n}): ReactNode {\n return (\n <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>\n <label style={{ fontSize: 12, fontWeight: 600, color: T.text }}>{label}</label>\n {children}\n {hint !== undefined && (\n <span style={{ fontSize: 11, color: T.muted }}>{hint}</span>\n )}\n </div>\n )\n}\n\n// \u2500\u2500\u2500 usePanel hook (shared fetch logic) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport function usePanel<P>(path: string): {\n payload: P | null\n error: string | null\n reload: () => void\n} {\n const [payload, setPayload] = useState<P | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [tick, setTick] = useState(0)\n const reload = useCallback(() => setTick((v) => v + 1), [])\n\n useEffect(() => {\n const c = new AbortController()\n setError(null)\n fetch(path, { signal: c.signal })\n .then(async (r) => {\n if (!r.ok) throw new Error(String(r.status))\n return r.json() as Promise<P>\n })\n .then((p) => {\n if (!c.signal.aborted) setPayload(p)\n })\n .catch((e: unknown) => {\n if (!c.signal.aborted) setPayload(null), setError(e instanceof Error ? e.message : String(e))\n })\n return () => c.abort()\n }, [tick, path])\n\n return useMemo(() => ({ payload, error, reload }), [payload, error, reload])\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["import_react", "import_jsx_runtime"]
|
|
7
7
|
}
|