@lmzhen/dsh-evolution-client-kit 0.0.0 → 0.30.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -1
- package/lib/generated/contracts.generated.js +1 -0
- package/lib/generated/fallbacks.generated.js +18 -0
- package/lib/generated/route-client.generated.js +96 -0
- package/lib/generated/routes.generated.js +35 -0
- package/lib/generated/states.generated.js +42 -0
- package/lib/index.js +84 -0
- package/lib/tokens.js +57 -0
- package/lib/types/generated/contracts.generated.d.ts +37 -0
- package/lib/types/generated/contracts.generated.js +3 -0
- package/lib/types/generated/fallbacks.generated.d.ts +8 -0
- package/lib/types/generated/fallbacks.generated.js +25 -0
- package/lib/types/generated/route-client.generated.d.ts +45 -0
- package/lib/types/generated/route-client.generated.js +62 -0
- package/lib/types/generated/routes.generated.d.ts +39 -0
- package/lib/types/generated/routes.generated.js +38 -0
- package/lib/types/generated/states.generated.d.ts +43 -0
- package/lib/types/generated/states.generated.js +31 -0
- package/lib/types/index.d.ts +10 -0
- package/lib/types/index.js +8 -0
- package/lib/types/panel.d.ts +44 -0
- package/lib/types/panel.js +56 -0
- package/lib/types/seam.d.ts +63 -0
- package/lib/types/seam.js +2 -0
- package/lib/types/tokens.d.ts +23 -0
- package/lib/types/tokens.js +55 -0
- package/lib/types/workspace.d.ts +12 -0
- package/lib/types/workspace.js +21 -0
- package/package.json +57 -8
package/README.md
CHANGED
|
@@ -1 +1,9 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @lmzhen/dsh-evolution-client-kit
|
|
2
|
+
|
|
3
|
+
Browser-side seams and lifecycle shared by the evolution family's sidebar panels.
|
|
4
|
+
It owns the client contract types, generated route/state/contract tables, design tokens, and
|
|
5
|
+
one `registerSidebarPanel` entry point that installs the locale, stylesheet, row, and keyed
|
|
6
|
+
main body. It is a library, not a plugin row: it declares no `dsh.client` entry.
|
|
7
|
+
|
|
8
|
+
Feature panels keep their own Panel/Store/api and icon; only the platform-facing lifecycle
|
|
9
|
+
lives here. settings-ui keeps its settings-section lifecycle, but shares the generated tables.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//#region lib/types/generated/fallbacks.generated.js
|
|
2
|
+
/**
|
|
3
|
+
* The command-face fallback for every Host surface, generated from evolution-commands'
|
|
4
|
+
* COMMAND_FALLBACKS (the ONE home). When the platform's connection carrier is absent (E-325) the
|
|
5
|
+
* panels render this hint instead of a bare HTTP failure — same use case, command face.
|
|
6
|
+
*/
|
|
7
|
+
const FALLBACKS = {
|
|
8
|
+
approval: { command: "/evolution pending … /evolution approve <id> … /evolution reject <id> … /evolution release <id>" },
|
|
9
|
+
"skill-history": { command: "/evolution restore <name> [v](查看:/evolution skills history <name>)" },
|
|
10
|
+
"skill-health": { command: "/evolution skills refresh(健康行:/evolution doctor)" },
|
|
11
|
+
merge: { command: "/evolution merge status / pending / approve <id> / report" },
|
|
12
|
+
diagnostics: {
|
|
13
|
+
command: "/evolution doctor",
|
|
14
|
+
platformCli: "dsh --json /evolution doctor"
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
//#endregion
|
|
18
|
+
export { FALLBACKS };
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
//#region lib/types/generated/route-client.generated.js
|
|
2
|
+
/** Classify a non-2xx status: 401/403/404 keep their own kind, everything else is a server answer. */
|
|
3
|
+
function classify(status) {
|
|
4
|
+
if (status === 401) return {
|
|
5
|
+
kind: "unauthenticated",
|
|
6
|
+
status: 401
|
|
7
|
+
};
|
|
8
|
+
if (status === 403) return {
|
|
9
|
+
kind: "forbidden",
|
|
10
|
+
status: 403
|
|
11
|
+
};
|
|
12
|
+
if (status === 404) return {
|
|
13
|
+
kind: "unmounted",
|
|
14
|
+
status: 404
|
|
15
|
+
};
|
|
16
|
+
return {
|
|
17
|
+
kind: "server",
|
|
18
|
+
status
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* One request, no retries and no body parsing for the platform's admission answers.
|
|
23
|
+
* @param doFetch - the fetch in use (the browser's, or a spec's stub).
|
|
24
|
+
* @param path - the exact route path from the generated ROUTES table.
|
|
25
|
+
* @param init - standard RequestInit.
|
|
26
|
+
* @returns the data, or the classified failure (network/parse failures read as 'server').
|
|
27
|
+
*/
|
|
28
|
+
async function requestJson(doFetch, path, init) {
|
|
29
|
+
let response;
|
|
30
|
+
try {
|
|
31
|
+
response = await doFetch(path, init);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
return {
|
|
34
|
+
ok: false,
|
|
35
|
+
failure: {
|
|
36
|
+
kind: "server",
|
|
37
|
+
status: 0,
|
|
38
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if (!response.ok) return {
|
|
43
|
+
ok: false,
|
|
44
|
+
failure: classify(response.status)
|
|
45
|
+
};
|
|
46
|
+
let body;
|
|
47
|
+
try {
|
|
48
|
+
body = await response.json();
|
|
49
|
+
} catch {
|
|
50
|
+
return {
|
|
51
|
+
ok: false,
|
|
52
|
+
failure: {
|
|
53
|
+
kind: "server",
|
|
54
|
+
status: response.status
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
if (typeof body === "object" && body !== null) {
|
|
59
|
+
const record = body;
|
|
60
|
+
if (record.ok === true) return {
|
|
61
|
+
ok: true,
|
|
62
|
+
data: record.data
|
|
63
|
+
};
|
|
64
|
+
const nested = record.error;
|
|
65
|
+
const errorRecord = typeof nested === "object" && nested !== null ? nested : void 0;
|
|
66
|
+
const code = typeof record.code === "string" && record.code !== "" ? record.code : typeof errorRecord?.code === "string" ? errorRecord.code : void 0;
|
|
67
|
+
const message = typeof record.message === "string" && record.message !== "" ? record.message : typeof errorRecord?.message === "string" ? errorRecord.message : void 0;
|
|
68
|
+
if (record.ok === false && code !== void 0 && message !== void 0 && message !== "") return {
|
|
69
|
+
ok: false,
|
|
70
|
+
failure: {
|
|
71
|
+
kind: "refused",
|
|
72
|
+
code,
|
|
73
|
+
message
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
ok: false,
|
|
79
|
+
failure: {
|
|
80
|
+
kind: "server",
|
|
81
|
+
status: response.status
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/** The one-sentence rendering legacy callers attach to a classified failure. */
|
|
86
|
+
function failureMessage(failure) {
|
|
87
|
+
switch (failure.kind) {
|
|
88
|
+
case "refused": return failure.message;
|
|
89
|
+
case "server": return failure.status === 0 ? failure.detail === void 0 || failure.detail === "" ? "网络错误:无法连接宿主" : failure.detail : "HTTP " + String(failure.status);
|
|
90
|
+
case "unauthenticated": return "HTTP 401";
|
|
91
|
+
case "forbidden": return "HTTP 403";
|
|
92
|
+
case "unmounted": return "HTTP 404";
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
//#endregion
|
|
96
|
+
export { classify, failureMessage, requestJson };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
//#region lib/types/generated/routes.generated.js
|
|
2
|
+
/**
|
|
3
|
+
* Every family host path, generated from evolution-core's HOST_ROUTES (the ONE declaration).
|
|
4
|
+
* A browser half cannot import the host package at runtime (client bundles keep @deepseek-ai/*
|
|
5
|
+
* external), so the table is mirrored here mechanically; a rename in core regenerates all three
|
|
6
|
+
* client copies, and the generator's --check makes a stale copy red.
|
|
7
|
+
*/
|
|
8
|
+
const ROUTES = {
|
|
9
|
+
approval: {
|
|
10
|
+
pending: "/api/dsh-evolution/approval/pending",
|
|
11
|
+
preview: "/api/dsh-evolution/approval/preview",
|
|
12
|
+
approve: "/api/dsh-evolution/approval/approve",
|
|
13
|
+
reject: "/api/dsh-evolution/approval/reject"
|
|
14
|
+
},
|
|
15
|
+
"skill-history": {
|
|
16
|
+
skills: "/api/dsh-evolution/skill-history/skills",
|
|
17
|
+
versions: "/api/dsh-evolution/skill-history/versions",
|
|
18
|
+
diff: "/api/dsh-evolution/skill-history/versions/diff",
|
|
19
|
+
body: "/api/dsh-evolution/skill-history/versions/body",
|
|
20
|
+
undo: "/api/dsh-evolution/skill-history/undo",
|
|
21
|
+
health: "/api/dsh-evolution/skill-history/health"
|
|
22
|
+
},
|
|
23
|
+
"skill-health": { rows: "/api/dsh-evolution/skill-health/rows" },
|
|
24
|
+
merge: {
|
|
25
|
+
view: "/api/dsh-evolution/merge/view",
|
|
26
|
+
rows: "/api/dsh-evolution/merge/rows",
|
|
27
|
+
run: "/api/dsh-evolution/merge/run",
|
|
28
|
+
runs: "/api/dsh-evolution/merge/runs",
|
|
29
|
+
body: "/api/dsh-evolution/merge/body",
|
|
30
|
+
history: "/api/dsh-evolution/merge/history"
|
|
31
|
+
},
|
|
32
|
+
diagnostics: { view: "/api/dsh-evolution/diagnostics" }
|
|
33
|
+
};
|
|
34
|
+
//#endregion
|
|
35
|
+
export { ROUTES };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { createElement } from "react";
|
|
2
|
+
//#region lib/types/generated/states.generated.js
|
|
3
|
+
/** Map one classified answer onto the card's four states. */
|
|
4
|
+
function loadStateOf(answer, isEmpty) {
|
|
5
|
+
if (!answer.ok) return {
|
|
6
|
+
phase: "failed",
|
|
7
|
+
failure: answer.failure
|
|
8
|
+
};
|
|
9
|
+
return isEmpty(answer.data) ? { phase: "empty" } : {
|
|
10
|
+
phase: "ready",
|
|
11
|
+
data: answer.data
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
/** A neutral/ warn/ fail block; a fail block is role=alert so assistive tech announces it. */
|
|
15
|
+
function Block(props) {
|
|
16
|
+
const tone = props.tone ?? "neutral";
|
|
17
|
+
return createElement("div", {
|
|
18
|
+
"data-tone": tone,
|
|
19
|
+
...tone === "fail" ? { role: "alert" } : {}
|
|
20
|
+
}, props.title === void 0 ? null : createElement("div", { "data-tone-title": tone }, props.title), props.children);
|
|
21
|
+
}
|
|
22
|
+
/** One classified failure as a reader-facing sentence. */
|
|
23
|
+
function failureText(failure, t) {
|
|
24
|
+
switch (failure.kind) {
|
|
25
|
+
case "unauthenticated": return t("failureUnauthorized");
|
|
26
|
+
case "forbidden": return t("failureForbidden");
|
|
27
|
+
case "unmounted": return t("failureUnmounted");
|
|
28
|
+
case "server": return failure.status === 0 ? t("failureServer") : t("failureServer") + " (HTTP " + String(failure.status) + ")";
|
|
29
|
+
case "refused": return failure.message;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** The failed-state block: sentence + (when the surface exists but is unreachable) the command fallback. */
|
|
33
|
+
function FailureBlock(props) {
|
|
34
|
+
const hint = props.fallback === void 0 ? null : createElement("div", { "data-tone-hint": "true" }, props.t("fallbackHint", { command: props.fallback.command }));
|
|
35
|
+
return Block({
|
|
36
|
+
tone: "fail",
|
|
37
|
+
title: failureText(props.failure, props.t),
|
|
38
|
+
children: hint
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
export { Block, FailureBlock, failureText, loadStateOf };
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { ROUTES } from "./generated/routes.generated.js";
|
|
2
|
+
import { FALLBACKS } from "./generated/fallbacks.generated.js";
|
|
3
|
+
import "./generated/contracts.generated.js";
|
|
4
|
+
import { Block, FailureBlock, failureText, loadStateOf } from "./generated/states.generated.js";
|
|
5
|
+
import { TOKENS, TOKEN_CSS, tokenVars } from "./tokens.js";
|
|
6
|
+
//#region lib/types/panel.js
|
|
7
|
+
function interpolate(text, values) {
|
|
8
|
+
if (values === void 0) return text;
|
|
9
|
+
return text.replace(/\{(\w+)\}/g, (_match, key) => String(values[key] ?? ""));
|
|
10
|
+
}
|
|
11
|
+
function injectStyles(doc, css, tagId) {
|
|
12
|
+
if (doc === void 0) return () => {};
|
|
13
|
+
if (doc.querySelector("style[data-plugin-css=\"" + tagId + "\"]") !== null) return () => {};
|
|
14
|
+
const tag = doc.createElement("style");
|
|
15
|
+
tag.setAttribute("data-plugin-css", tagId);
|
|
16
|
+
tag.textContent = css;
|
|
17
|
+
doc.head.append(tag);
|
|
18
|
+
return () => {
|
|
19
|
+
tag.remove();
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Register one sidebar row and its main body under one lifecycle.
|
|
24
|
+
* @param ctx - the client context carrying the slots and locale seats.
|
|
25
|
+
* @param spec - the feature-specific row/body descriptor.
|
|
26
|
+
*/
|
|
27
|
+
function registerSidebarPanel(ctx, spec) {
|
|
28
|
+
const seam = ctx;
|
|
29
|
+
let registered = false;
|
|
30
|
+
ctx.effect(() => {
|
|
31
|
+
try {
|
|
32
|
+
const dispose = seam.locale.register(spec.namespace, spec.dictionaries);
|
|
33
|
+
registered = true;
|
|
34
|
+
return dispose;
|
|
35
|
+
} catch (error) {
|
|
36
|
+
ctx.logger.warn(spec.namespace + ": the locale seat refused the dictionary (" + String(error) + "); the panel falls back to the bundle copy");
|
|
37
|
+
return () => {};
|
|
38
|
+
}
|
|
39
|
+
}, spec.namespace + ": locale");
|
|
40
|
+
ctx.effect(() => injectStyles(typeof document === "undefined" ? void 0 : document, spec.styles.css, spec.styles.tagId), spec.namespace + ": styles");
|
|
41
|
+
const fallback = spec.dictionaries[spec.fallback ?? "zh"];
|
|
42
|
+
const lookup = (key) => {
|
|
43
|
+
const own = fallback[key] ?? key;
|
|
44
|
+
if (!registered) return own;
|
|
45
|
+
try {
|
|
46
|
+
return seam.locale.bind(spec.namespace)(key);
|
|
47
|
+
} catch {
|
|
48
|
+
return own;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
const t = (key, values) => interpolate(lookup(key), values);
|
|
52
|
+
const label = () => lookup("entry.label");
|
|
53
|
+
seam.slots.inject("sidebar.panellist", function* () {
|
|
54
|
+
yield seam.slots.register({
|
|
55
|
+
name: "sidebar.panellist",
|
|
56
|
+
id: spec.id,
|
|
57
|
+
order: spec.order,
|
|
58
|
+
label
|
|
59
|
+
}, spec.icon);
|
|
60
|
+
yield seam.slots.register({
|
|
61
|
+
name: "main",
|
|
62
|
+
key: spec.id,
|
|
63
|
+
inject: () => spec.body(t)
|
|
64
|
+
}, spec.component);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
//#endregion
|
|
68
|
+
//#region lib/types/workspace.js
|
|
69
|
+
/**
|
|
70
|
+
* Best-effort workspace id; a seat that is absent or refuses leaves the panel in local scope.
|
|
71
|
+
* @param ctx - the client context.
|
|
72
|
+
* @returns the current workspace id, or undefined.
|
|
73
|
+
*/
|
|
74
|
+
function panelWorkspaceId(ctx) {
|
|
75
|
+
const get = ctx.get;
|
|
76
|
+
if (typeof get !== "function") return void 0;
|
|
77
|
+
for (const name of ["workspace", "uiWorkspace"]) try {
|
|
78
|
+
const seat = get.call(ctx, name);
|
|
79
|
+
const id = seat?.workspaceId ?? seat?.currentWorkspaceId;
|
|
80
|
+
if (typeof id === "string" && id.trim() !== "") return id;
|
|
81
|
+
} catch {}
|
|
82
|
+
}
|
|
83
|
+
//#endregion
|
|
84
|
+
export { Block, FALLBACKS, FailureBlock, ROUTES, TOKENS, TOKEN_CSS, failureText, loadStateOf, panelWorkspaceId, registerSidebarPanel, tokenVars };
|
package/lib/tokens.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
//#region lib/types/tokens.js
|
|
2
|
+
/**
|
|
3
|
+
* The family client scale - GENERATED from packages/scripts/client-tokens.json. Do not edit.
|
|
4
|
+
*
|
|
5
|
+
* The shared client kit carries this file, because the scale is one fact: `verify-client-tokens.mjs`
|
|
6
|
+
* (a gate step) fails when the kit copy drifts from the source. The kit bundles into each browser
|
|
7
|
+
* half at build time, so no runtime module-table row is needed.
|
|
8
|
+
*
|
|
9
|
+
* Usage: put `tokenVars(...)` for your root class in the package stylesheet, then read
|
|
10
|
+
* `var(--evo-group-name)` in its rules.
|
|
11
|
+
* Regenerate with: node packages/scripts/gen-client-tokens.mjs packages
|
|
12
|
+
* @module dsh-evolution-client-scale
|
|
13
|
+
*/
|
|
14
|
+
/** Every custom property of the scale, in the source order. */
|
|
15
|
+
const TOKENS = {
|
|
16
|
+
"--evo-type-lg": "calc(1em + 2px)",
|
|
17
|
+
"--evo-type-body": "1em",
|
|
18
|
+
"--evo-type-sm": "calc(1em - 1px)",
|
|
19
|
+
"--evo-type-xs": "calc(1em - 2px)",
|
|
20
|
+
"--evo-space-2": "2px",
|
|
21
|
+
"--evo-space-4": "4px",
|
|
22
|
+
"--evo-space-6": "6px",
|
|
23
|
+
"--evo-space-8": "8px",
|
|
24
|
+
"--evo-space-10": "10px",
|
|
25
|
+
"--evo-space-12": "12px",
|
|
26
|
+
"--evo-space-14": "14px",
|
|
27
|
+
"--evo-space-16": "16px",
|
|
28
|
+
"--evo-space-20": "20px",
|
|
29
|
+
"--evo-radius-chip": "4px",
|
|
30
|
+
"--evo-radius-control": "8px",
|
|
31
|
+
"--evo-radius-surface": "12px",
|
|
32
|
+
"--evo-radius-pill": "999px",
|
|
33
|
+
"--evo-hairline-width": ".5px",
|
|
34
|
+
"--evo-hairline-marker": "2px",
|
|
35
|
+
"--evo-measure-read": "80ch",
|
|
36
|
+
"--evo-leading-base": "1.5",
|
|
37
|
+
"--evo-tone-quiet": "var(--dsw-alias-label-secondary)",
|
|
38
|
+
"--evo-tone-primary": "var(--dsw-alias-brand-primary)",
|
|
39
|
+
"--evo-tone-danger": "var(--dsw-alias-state-error-primary)",
|
|
40
|
+
"--evo-tone-success": "var(--dsw-alias-state-success-primary)",
|
|
41
|
+
"--evo-focus-ring": "2px solid var(--dsw-alias-label-primary)",
|
|
42
|
+
"--evo-focus-offset": "-2px",
|
|
43
|
+
"--evo-cap-aside": "280px",
|
|
44
|
+
"--evo-cap-block": "260px"
|
|
45
|
+
};
|
|
46
|
+
/** The declarations, without a selector. */
|
|
47
|
+
const TOKEN_CSS = "--evo-type-lg:calc(1em + 2px);--evo-type-body:1em;--evo-type-sm:calc(1em - 1px);--evo-type-xs:calc(1em - 2px);--evo-space-2:2px;--evo-space-4:4px;--evo-space-6:6px;--evo-space-8:8px;--evo-space-10:10px;--evo-space-12:12px;--evo-space-14:14px;--evo-space-16:16px;--evo-space-20:20px;--evo-radius-chip:4px;--evo-radius-control:8px;--evo-radius-surface:12px;--evo-radius-pill:999px;--evo-hairline-width:.5px;--evo-hairline-marker:2px;--evo-measure-read:80ch;--evo-leading-base:1.5;--evo-tone-quiet:var(--dsw-alias-label-secondary);--evo-tone-primary:var(--dsw-alias-brand-primary);--evo-tone-danger:var(--dsw-alias-state-error-primary);--evo-tone-success:var(--dsw-alias-state-success-primary);--evo-focus-ring:2px solid var(--dsw-alias-label-primary);--evo-focus-offset:-2px;--evo-cap-aside:280px;--evo-cap-block:260px";
|
|
48
|
+
/**
|
|
49
|
+
* The scale, declared on one element so it never leaks into the host shell.
|
|
50
|
+
* @param scope - the selector the custom properties are declared on.
|
|
51
|
+
* @returns the declaration block to put in the stylesheet.
|
|
52
|
+
*/
|
|
53
|
+
function tokenVars(scope) {
|
|
54
|
+
return scope + "{--evo-type-lg:calc(1em + 2px);--evo-type-body:1em;--evo-type-sm:calc(1em - 1px);--evo-type-xs:calc(1em - 2px);--evo-space-2:2px;--evo-space-4:4px;--evo-space-6:6px;--evo-space-8:8px;--evo-space-10:10px;--evo-space-12:12px;--evo-space-14:14px;--evo-space-16:16px;--evo-space-20:20px;--evo-radius-chip:4px;--evo-radius-control:8px;--evo-radius-surface:12px;--evo-radius-pill:999px;--evo-hairline-width:.5px;--evo-hairline-marker:2px;--evo-measure-read:80ch;--evo-leading-base:1.5;--evo-tone-quiet:var(--dsw-alias-label-secondary);--evo-tone-primary:var(--dsw-alias-brand-primary);--evo-tone-danger:var(--dsw-alias-state-error-primary);--evo-tone-success:var(--dsw-alias-state-success-primary);--evo-focus-ring:2px solid var(--dsw-alias-label-primary);--evo-focus-offset:-2px;--evo-cap-aside:280px;--evo-cap-block:260px}";
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
57
|
+
export { TOKENS, TOKEN_CSS, tokenVars };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Literal mirror of the frozen Host diagnostics contract (W1 §2.8 / core diagnostics.ts). The browser
|
|
3
|
+
* build keeps every @deepseek-ai/* specifier external, so the client CANNOT import the host type — the
|
|
4
|
+
* shape is mirrored mechanically here instead. The key unions are rendered from core diagnostics.ts by
|
|
5
|
+
* the generator, so a key change regenerates all three copies (a stale copy fails --check).
|
|
6
|
+
*/
|
|
7
|
+
export type DiagnosticsSchemaVersion = 2;
|
|
8
|
+
export type DiagnosticsFactKey = 'platform-line' | 'family-version' | 'install-form' | 'preset-ids' | 'readonly-contract' | 'upgrade-order' | 'fallback-map';
|
|
9
|
+
export type DiagnosticsRuntimeKey = 'subagent-channel' | 'restricted-preset' | 'host-surface' | 'session-fold' | 'platform-services' | 'runs' | 'platform-pairing' | 'install-form-detected';
|
|
10
|
+
export type DiagnosticStatus = 'ok' | 'warn' | 'fail' | 'unknown';
|
|
11
|
+
export interface DocFactRow {
|
|
12
|
+
readonly key: DiagnosticsFactKey;
|
|
13
|
+
readonly label: string;
|
|
14
|
+
readonly value: string;
|
|
15
|
+
readonly home: string;
|
|
16
|
+
}
|
|
17
|
+
export interface DiagnosticRow {
|
|
18
|
+
readonly key: DiagnosticsRuntimeKey;
|
|
19
|
+
readonly label: string;
|
|
20
|
+
readonly status: DiagnosticStatus;
|
|
21
|
+
readonly detail?: string;
|
|
22
|
+
readonly remedy?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface DiagnosticsView {
|
|
25
|
+
readonly schemaVersion: DiagnosticsSchemaVersion;
|
|
26
|
+
readonly generatedAt: string;
|
|
27
|
+
readonly facts: readonly DocFactRow[];
|
|
28
|
+
readonly runtime: readonly DiagnosticRow[];
|
|
29
|
+
}
|
|
30
|
+
/** The installation form the doctor reports (W8 install-form-detected row). */
|
|
31
|
+
export type InstallFormId = 'M1' | 'M2' | 'M3' | 'M4';
|
|
32
|
+
/** One surface's command-face path(s) — the client `fallbacks.generated.ts` table's row type. */
|
|
33
|
+
export interface FallbackHint {
|
|
34
|
+
readonly command: string;
|
|
35
|
+
readonly platformCli?: string;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=contracts.generated.d.ts.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FallbackHint } from './contracts.generated.ts';
|
|
2
|
+
/**
|
|
3
|
+
* The command-face fallback for every Host surface, generated from evolution-commands'
|
|
4
|
+
* COMMAND_FALLBACKS (the ONE home). When the platform's connection carrier is absent (E-325) the
|
|
5
|
+
* panels render this hint instead of a bare HTTP failure — same use case, command face.
|
|
6
|
+
*/
|
|
7
|
+
export declare const FALLBACKS: Readonly<Record<'approval' | 'skill-history' | 'skill-health' | 'merge' | 'diagnostics', FallbackHint>>;
|
|
8
|
+
//# sourceMappingURL=fallbacks.generated.d.ts.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// GENERATED by packages/scripts/gen-client-seam.mjs from evolution-commands COMMAND_FALLBACKS — do not edit.
|
|
2
|
+
/**
|
|
3
|
+
* The command-face fallback for every Host surface, generated from evolution-commands'
|
|
4
|
+
* COMMAND_FALLBACKS (the ONE home). When the platform's connection carrier is absent (E-325) the
|
|
5
|
+
* panels render this hint instead of a bare HTTP failure — same use case, command face.
|
|
6
|
+
*/
|
|
7
|
+
export const FALLBACKS = {
|
|
8
|
+
approval: {
|
|
9
|
+
command: '/evolution pending … /evolution approve <id> … /evolution reject <id> … /evolution release <id>',
|
|
10
|
+
},
|
|
11
|
+
'skill-history': {
|
|
12
|
+
command: '/evolution restore <name> [v](查看:/evolution skills history <name>)',
|
|
13
|
+
},
|
|
14
|
+
'skill-health': {
|
|
15
|
+
command: '/evolution skills refresh(健康行:/evolution doctor)',
|
|
16
|
+
},
|
|
17
|
+
merge: {
|
|
18
|
+
command: '/evolution merge status / pending / approve <id> / report',
|
|
19
|
+
},
|
|
20
|
+
diagnostics: {
|
|
21
|
+
command: '/evolution doctor',
|
|
22
|
+
platformCli: 'dsh --json /evolution doctor',
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=fallbacks.generated.js.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The four-state transport classifier every family client shares (W7 §2). The platform owns
|
|
3
|
+
* admission (401/403/404 are its answers, not ours), so the client reads the STATUS only for those
|
|
4
|
+
* three and never tries to interpret a platform body. Everything the family route itself answers
|
|
5
|
+
* keeps its business envelope: 2xx + {ok:true,data} or 2xx + {ok:false,code,message}.
|
|
6
|
+
*/
|
|
7
|
+
export type RouteFailure = {
|
|
8
|
+
readonly kind: 'unauthenticated';
|
|
9
|
+
readonly status: 401;
|
|
10
|
+
} | {
|
|
11
|
+
readonly kind: 'forbidden';
|
|
12
|
+
readonly status: 403;
|
|
13
|
+
} | {
|
|
14
|
+
readonly kind: 'unmounted';
|
|
15
|
+
readonly status: 404;
|
|
16
|
+
} | {
|
|
17
|
+
readonly kind: 'server';
|
|
18
|
+
readonly status: number;
|
|
19
|
+
readonly detail?: string;
|
|
20
|
+
} | {
|
|
21
|
+
readonly kind: 'refused';
|
|
22
|
+
readonly code: string;
|
|
23
|
+
readonly message: string;
|
|
24
|
+
};
|
|
25
|
+
/** One route answer: the data, or the classified failure. */
|
|
26
|
+
export type RouteAnswer<T> = {
|
|
27
|
+
readonly ok: true;
|
|
28
|
+
readonly data: T;
|
|
29
|
+
} | {
|
|
30
|
+
readonly ok: false;
|
|
31
|
+
readonly failure: RouteFailure;
|
|
32
|
+
};
|
|
33
|
+
/** Classify a non-2xx status: 401/403/404 keep their own kind, everything else is a server answer. */
|
|
34
|
+
export declare function classify(status: number): RouteFailure;
|
|
35
|
+
/**
|
|
36
|
+
* One request, no retries and no body parsing for the platform's admission answers.
|
|
37
|
+
* @param doFetch - the fetch in use (the browser's, or a spec's stub).
|
|
38
|
+
* @param path - the exact route path from the generated ROUTES table.
|
|
39
|
+
* @param init - standard RequestInit.
|
|
40
|
+
* @returns the data, or the classified failure (network/parse failures read as 'server').
|
|
41
|
+
*/
|
|
42
|
+
export declare function requestJson<T>(doFetch: typeof fetch, path: string, init?: RequestInit): Promise<RouteAnswer<T>>;
|
|
43
|
+
/** The one-sentence rendering legacy callers attach to a classified failure. */
|
|
44
|
+
export declare function failureMessage(failure: RouteFailure): string;
|
|
45
|
+
//# sourceMappingURL=route-client.generated.d.ts.map
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// GENERATED by packages/scripts/gen-client-seam.mjs from packages/scripts/templates/client/route-client.ts.tmpl — do not edit.
|
|
2
|
+
/** Classify a non-2xx status: 401/403/404 keep their own kind, everything else is a server answer. */
|
|
3
|
+
export function classify(status) {
|
|
4
|
+
if (status === 401)
|
|
5
|
+
return { kind: 'unauthenticated', status: 401 };
|
|
6
|
+
if (status === 403)
|
|
7
|
+
return { kind: 'forbidden', status: 403 };
|
|
8
|
+
if (status === 404)
|
|
9
|
+
return { kind: 'unmounted', status: 404 };
|
|
10
|
+
return { kind: 'server', status };
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* One request, no retries and no body parsing for the platform's admission answers.
|
|
14
|
+
* @param doFetch - the fetch in use (the browser's, or a spec's stub).
|
|
15
|
+
* @param path - the exact route path from the generated ROUTES table.
|
|
16
|
+
* @param init - standard RequestInit.
|
|
17
|
+
* @returns the data, or the classified failure (network/parse failures read as 'server').
|
|
18
|
+
*/
|
|
19
|
+
export async function requestJson(doFetch, path, init) {
|
|
20
|
+
let response;
|
|
21
|
+
try {
|
|
22
|
+
response = await doFetch(path, init);
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
return { ok: false, failure: { kind: 'server', status: 0, detail: error instanceof Error ? error.message : String(error) } };
|
|
26
|
+
}
|
|
27
|
+
if (!response.ok)
|
|
28
|
+
return { ok: false, failure: classify(response.status) };
|
|
29
|
+
let body;
|
|
30
|
+
try {
|
|
31
|
+
body = await response.json();
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return { ok: false, failure: { kind: 'server', status: response.status } };
|
|
35
|
+
}
|
|
36
|
+
if (typeof body === 'object' && body !== null) {
|
|
37
|
+
const record = body;
|
|
38
|
+
if (record.ok === true)
|
|
39
|
+
return { ok: true, data: record.data };
|
|
40
|
+
const nested = record.error;
|
|
41
|
+
const errorRecord = typeof nested === 'object' && nested !== null ? nested : undefined;
|
|
42
|
+
const code = typeof record.code === 'string' && record.code !== '' ? record.code : typeof errorRecord?.code === 'string' ? errorRecord.code : undefined;
|
|
43
|
+
const message = typeof record.message === 'string' && record.message !== '' ? record.message : typeof errorRecord?.message === 'string' ? errorRecord.message : undefined;
|
|
44
|
+
if (record.ok === false && code !== undefined && message !== undefined && message !== '') {
|
|
45
|
+
return { ok: false, failure: { kind: 'refused', code, message } };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return { ok: false, failure: { kind: 'server', status: response.status } };
|
|
49
|
+
}
|
|
50
|
+
/** The one-sentence rendering legacy callers attach to a classified failure. */
|
|
51
|
+
export function failureMessage(failure) {
|
|
52
|
+
switch (failure.kind) {
|
|
53
|
+
case 'refused': return failure.message;
|
|
54
|
+
case 'server': return failure.status === 0
|
|
55
|
+
? (failure.detail === undefined || failure.detail === '' ? '网络错误:无法连接宿主' : failure.detail)
|
|
56
|
+
: 'HTTP ' + String(failure.status);
|
|
57
|
+
case 'unauthenticated': return 'HTTP 401';
|
|
58
|
+
case 'forbidden': return 'HTTP 403';
|
|
59
|
+
case 'unmounted': return 'HTTP 404';
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=route-client.generated.js.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/** One host data-plane surface the browser half can call. */
|
|
2
|
+
export type SurfaceId = 'approval' | 'skill-history' | 'skill-health' | 'merge' | 'diagnostics';
|
|
3
|
+
/**
|
|
4
|
+
* Every family host path, generated from evolution-core's HOST_ROUTES (the ONE declaration).
|
|
5
|
+
* A browser half cannot import the host package at runtime (client bundles keep @deepseek-ai/*
|
|
6
|
+
* external), so the table is mirrored here mechanically; a rename in core regenerates all three
|
|
7
|
+
* client copies, and the generator's --check makes a stale copy red.
|
|
8
|
+
*/
|
|
9
|
+
export declare const ROUTES: {
|
|
10
|
+
readonly approval: {
|
|
11
|
+
readonly pending: "/api/dsh-evolution/approval/pending";
|
|
12
|
+
readonly preview: "/api/dsh-evolution/approval/preview";
|
|
13
|
+
readonly approve: "/api/dsh-evolution/approval/approve";
|
|
14
|
+
readonly reject: "/api/dsh-evolution/approval/reject";
|
|
15
|
+
};
|
|
16
|
+
readonly 'skill-history': {
|
|
17
|
+
readonly skills: "/api/dsh-evolution/skill-history/skills";
|
|
18
|
+
readonly versions: "/api/dsh-evolution/skill-history/versions";
|
|
19
|
+
readonly diff: "/api/dsh-evolution/skill-history/versions/diff";
|
|
20
|
+
readonly body: "/api/dsh-evolution/skill-history/versions/body";
|
|
21
|
+
readonly undo: "/api/dsh-evolution/skill-history/undo";
|
|
22
|
+
readonly health: "/api/dsh-evolution/skill-history/health";
|
|
23
|
+
};
|
|
24
|
+
readonly 'skill-health': {
|
|
25
|
+
readonly rows: "/api/dsh-evolution/skill-health/rows";
|
|
26
|
+
};
|
|
27
|
+
readonly merge: {
|
|
28
|
+
readonly view: "/api/dsh-evolution/merge/view";
|
|
29
|
+
readonly rows: "/api/dsh-evolution/merge/rows";
|
|
30
|
+
readonly run: "/api/dsh-evolution/merge/run";
|
|
31
|
+
readonly runs: "/api/dsh-evolution/merge/runs";
|
|
32
|
+
readonly body: "/api/dsh-evolution/merge/body";
|
|
33
|
+
readonly history: "/api/dsh-evolution/merge/history";
|
|
34
|
+
};
|
|
35
|
+
readonly diagnostics: {
|
|
36
|
+
readonly view: "/api/dsh-evolution/diagnostics";
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=routes.generated.d.ts.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// GENERATED by packages/scripts/gen-client-seam.mjs from evolution-core HOST_ROUTES — do not edit.
|
|
2
|
+
/**
|
|
3
|
+
* Every family host path, generated from evolution-core's HOST_ROUTES (the ONE declaration).
|
|
4
|
+
* A browser half cannot import the host package at runtime (client bundles keep @deepseek-ai/*
|
|
5
|
+
* external), so the table is mirrored here mechanically; a rename in core regenerates all three
|
|
6
|
+
* client copies, and the generator's --check makes a stale copy red.
|
|
7
|
+
*/
|
|
8
|
+
export const ROUTES = {
|
|
9
|
+
approval: {
|
|
10
|
+
pending: '/api/dsh-evolution/approval/pending',
|
|
11
|
+
preview: '/api/dsh-evolution/approval/preview',
|
|
12
|
+
approve: '/api/dsh-evolution/approval/approve',
|
|
13
|
+
reject: '/api/dsh-evolution/approval/reject',
|
|
14
|
+
},
|
|
15
|
+
'skill-history': {
|
|
16
|
+
skills: '/api/dsh-evolution/skill-history/skills',
|
|
17
|
+
versions: '/api/dsh-evolution/skill-history/versions',
|
|
18
|
+
diff: '/api/dsh-evolution/skill-history/versions/diff',
|
|
19
|
+
body: '/api/dsh-evolution/skill-history/versions/body',
|
|
20
|
+
undo: '/api/dsh-evolution/skill-history/undo',
|
|
21
|
+
health: '/api/dsh-evolution/skill-history/health',
|
|
22
|
+
},
|
|
23
|
+
'skill-health': {
|
|
24
|
+
rows: '/api/dsh-evolution/skill-health/rows',
|
|
25
|
+
},
|
|
26
|
+
merge: {
|
|
27
|
+
view: '/api/dsh-evolution/merge/view',
|
|
28
|
+
rows: '/api/dsh-evolution/merge/rows',
|
|
29
|
+
run: '/api/dsh-evolution/merge/run',
|
|
30
|
+
runs: '/api/dsh-evolution/merge/runs',
|
|
31
|
+
body: '/api/dsh-evolution/merge/body',
|
|
32
|
+
history: '/api/dsh-evolution/merge/history',
|
|
33
|
+
},
|
|
34
|
+
diagnostics: {
|
|
35
|
+
view: '/api/dsh-evolution/diagnostics',
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=routes.generated.js.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import type { RouteAnswer, RouteFailure } from './route-client.generated.ts';
|
|
3
|
+
import type { FallbackHint } from './contracts.generated.ts';
|
|
4
|
+
/**
|
|
5
|
+
* The four-state base of every family card (W7 §2). Load states are read from the generated
|
|
6
|
+
* route-client's answer — a card owns no HTTP vocabulary and no second state machine.
|
|
7
|
+
*/
|
|
8
|
+
export type LoadState<T> = {
|
|
9
|
+
readonly phase: 'loading';
|
|
10
|
+
} | {
|
|
11
|
+
readonly phase: 'ready';
|
|
12
|
+
readonly data: T;
|
|
13
|
+
} | {
|
|
14
|
+
readonly phase: 'empty';
|
|
15
|
+
} | {
|
|
16
|
+
readonly phase: 'failed';
|
|
17
|
+
readonly failure: RouteFailure;
|
|
18
|
+
};
|
|
19
|
+
/** Map one classified answer onto the card's four states. */
|
|
20
|
+
export declare function loadStateOf<T>(answer: RouteAnswer<T>, isEmpty: (data: T) => boolean): LoadState<T>;
|
|
21
|
+
/** The locale lookup every block calls (the platform's own `t`). */
|
|
22
|
+
export interface Translate {
|
|
23
|
+
(key: string, values?: Readonly<Record<string, string | number>>): string;
|
|
24
|
+
}
|
|
25
|
+
export type BlockTone = 'neutral' | 'warn' | 'fail';
|
|
26
|
+
export interface BlockProps {
|
|
27
|
+
readonly tone?: BlockTone;
|
|
28
|
+
readonly title?: string;
|
|
29
|
+
readonly children?: ReactNode;
|
|
30
|
+
}
|
|
31
|
+
/** A neutral/ warn/ fail block; a fail block is role=alert so assistive tech announces it. */
|
|
32
|
+
export declare function Block(props: BlockProps): ReactNode;
|
|
33
|
+
export interface FailureBlockProps {
|
|
34
|
+
readonly failure: RouteFailure;
|
|
35
|
+
readonly t: Translate;
|
|
36
|
+
/** The command-face fallback for this surface (rendered on unmounted/failed answers). */
|
|
37
|
+
readonly fallback?: FallbackHint | undefined;
|
|
38
|
+
}
|
|
39
|
+
/** One classified failure as a reader-facing sentence. */
|
|
40
|
+
export declare function failureText(failure: RouteFailure, t: Translate): string;
|
|
41
|
+
/** The failed-state block: sentence + (when the surface exists but is unreachable) the command fallback. */
|
|
42
|
+
export declare function FailureBlock(props: FailureBlockProps): ReactNode;
|
|
43
|
+
//# sourceMappingURL=states.generated.d.ts.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// GENERATED by packages/scripts/gen-client-seam.mjs from packages/scripts/templates/client/states.ts.tmpl — do not edit.
|
|
2
|
+
import { createElement } from 'react';
|
|
3
|
+
/** Map one classified answer onto the card's four states. */
|
|
4
|
+
export function loadStateOf(answer, isEmpty) {
|
|
5
|
+
if (!answer.ok)
|
|
6
|
+
return { phase: 'failed', failure: answer.failure };
|
|
7
|
+
return isEmpty(answer.data) ? { phase: 'empty' } : { phase: 'ready', data: answer.data };
|
|
8
|
+
}
|
|
9
|
+
/** A neutral/ warn/ fail block; a fail block is role=alert so assistive tech announces it. */
|
|
10
|
+
export function Block(props) {
|
|
11
|
+
const tone = props.tone ?? 'neutral';
|
|
12
|
+
return createElement('div', { 'data-tone': tone, ...tone === 'fail' ? { role: 'alert' } : {} }, props.title === undefined ? null : createElement('div', { 'data-tone-title': tone }, props.title), props.children);
|
|
13
|
+
}
|
|
14
|
+
/** One classified failure as a reader-facing sentence. */
|
|
15
|
+
export function failureText(failure, t) {
|
|
16
|
+
switch (failure.kind) {
|
|
17
|
+
case 'unauthenticated': return t('failureUnauthorized');
|
|
18
|
+
case 'forbidden': return t('failureForbidden');
|
|
19
|
+
case 'unmounted': return t('failureUnmounted');
|
|
20
|
+
case 'server': return failure.status === 0 ? t('failureServer') : t('failureServer') + ' (HTTP ' + String(failure.status) + ')';
|
|
21
|
+
case 'refused': return failure.message;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/** The failed-state block: sentence + (when the surface exists but is unreachable) the command fallback. */
|
|
25
|
+
export function FailureBlock(props) {
|
|
26
|
+
const hint = props.fallback === undefined
|
|
27
|
+
? null
|
|
28
|
+
: createElement('div', { 'data-tone-hint': 'true' }, props.t('fallbackHint', { command: props.fallback.command }));
|
|
29
|
+
return Block({ tone: 'fail', title: failureText(props.failure, props.t), children: hint });
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=states.generated.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Shared browser seams and panel lifecycle for the evolution family. */
|
|
2
|
+
export type { ClientSeam, ConfigFormLike, ConfigFormSnapshotLike, ConfigFormsSeat, LocaleSeat, PanelBodyOptions, PanelRowOptions, SettingsFace, SlotsSeat } from './seam.ts';
|
|
3
|
+
export { registerSidebarPanel, type SidebarPanelSpec, type Translate } from './panel.ts';
|
|
4
|
+
export { panelWorkspaceId } from './workspace.ts';
|
|
5
|
+
export { ROUTES } from './generated/routes.generated.ts';
|
|
6
|
+
export { FALLBACKS } from './generated/fallbacks.generated.ts';
|
|
7
|
+
export * from './generated/contracts.generated.ts';
|
|
8
|
+
export * from './generated/states.generated.ts';
|
|
9
|
+
export { TOKEN_CSS, TOKENS, tokenVars } from './tokens.ts';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { registerSidebarPanel } from "./panel.js";
|
|
2
|
+
export { panelWorkspaceId } from "./workspace.js";
|
|
3
|
+
export { ROUTES } from "./generated/routes.generated.js";
|
|
4
|
+
export { FALLBACKS } from "./generated/fallbacks.generated.js";
|
|
5
|
+
export * from "./generated/contracts.generated.js";
|
|
6
|
+
export * from "./generated/states.generated.js";
|
|
7
|
+
export { TOKEN_CSS, TOKENS, tokenVars } from "./tokens.js";
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One registration path for a sidebar panel: locale, stylesheet, row and body.
|
|
3
|
+
* @module @lmzhen/dsh-evolution-client-kit
|
|
4
|
+
*/
|
|
5
|
+
import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
6
|
+
import type { ReactNode } from 'react';
|
|
7
|
+
/** Dictionary lookup used by the panel body and row label. */
|
|
8
|
+
export type Translate = (key: string, values?: Readonly<Record<string, string | number>>) => string;
|
|
9
|
+
/** The feature-specific part of one sidebar panel. */
|
|
10
|
+
export interface SidebarPanelSpec<Face> {
|
|
11
|
+
/** The row id; the keyed main body uses the same value. */
|
|
12
|
+
id: string;
|
|
13
|
+
/** Row order in the global panel list. */
|
|
14
|
+
order: number;
|
|
15
|
+
/** Locale namespace owned by this panel. */
|
|
16
|
+
namespace: string;
|
|
17
|
+
dictionaries: {
|
|
18
|
+
zh: Record<string, string>;
|
|
19
|
+
en: Record<string, string>;
|
|
20
|
+
};
|
|
21
|
+
/** Fallback dictionary when the locale seat refuses or is absent. */
|
|
22
|
+
fallback?: 'zh' | 'en';
|
|
23
|
+
/** Panel stylesheet and the tag id that makes injection idempotent. */
|
|
24
|
+
styles: {
|
|
25
|
+
css: string;
|
|
26
|
+
tagId: string;
|
|
27
|
+
};
|
|
28
|
+
/** The row glyph. */
|
|
29
|
+
icon: (props: {
|
|
30
|
+
size?: number;
|
|
31
|
+
active?: boolean;
|
|
32
|
+
}) => ReactNode;
|
|
33
|
+
/** The body component; the seat spreads the face as its props. */
|
|
34
|
+
component: (props: never) => ReactNode;
|
|
35
|
+
/** The body face factory; receives the bound translator. */
|
|
36
|
+
body: (t: Translate) => Face;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Register one sidebar row and its main body under one lifecycle.
|
|
40
|
+
* @param ctx - the client context carrying the slots and locale seats.
|
|
41
|
+
* @param spec - the feature-specific row/body descriptor.
|
|
42
|
+
*/
|
|
43
|
+
export declare function registerSidebarPanel<Face>(ctx: ClientContext, spec: SidebarPanelSpec<Face>): void;
|
|
44
|
+
//# sourceMappingURL=panel.d.ts.map
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
function interpolate(text, values) {
|
|
2
|
+
if (values === undefined)
|
|
3
|
+
return text;
|
|
4
|
+
return text.replace(/\{(\w+)\}/g, (_match, key) => String(values[key] ?? ''));
|
|
5
|
+
}
|
|
6
|
+
function injectStyles(doc, css, tagId) {
|
|
7
|
+
if (doc === undefined)
|
|
8
|
+
return () => { };
|
|
9
|
+
if (doc.querySelector('style[data-plugin-css="' + tagId + '"]') !== null)
|
|
10
|
+
return () => { };
|
|
11
|
+
const tag = doc.createElement('style');
|
|
12
|
+
tag.setAttribute('data-plugin-css', tagId);
|
|
13
|
+
tag.textContent = css;
|
|
14
|
+
doc.head.append(tag);
|
|
15
|
+
return () => { tag.remove(); };
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Register one sidebar row and its main body under one lifecycle.
|
|
19
|
+
* @param ctx - the client context carrying the slots and locale seats.
|
|
20
|
+
* @param spec - the feature-specific row/body descriptor.
|
|
21
|
+
*/
|
|
22
|
+
export function registerSidebarPanel(ctx, spec) {
|
|
23
|
+
const seam = ctx;
|
|
24
|
+
let registered = false;
|
|
25
|
+
ctx.effect(() => {
|
|
26
|
+
try {
|
|
27
|
+
const dispose = seam.locale.register(spec.namespace, spec.dictionaries);
|
|
28
|
+
registered = true;
|
|
29
|
+
return dispose;
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
ctx.logger.warn(spec.namespace + ': the locale seat refused the dictionary (' + String(error) + '); the panel falls back to the bundle copy');
|
|
33
|
+
return () => { };
|
|
34
|
+
}
|
|
35
|
+
}, spec.namespace + ': locale');
|
|
36
|
+
ctx.effect(() => injectStyles(typeof document === 'undefined' ? undefined : document, spec.styles.css, spec.styles.tagId), spec.namespace + ': styles');
|
|
37
|
+
const fallback = spec.dictionaries[spec.fallback ?? 'zh'];
|
|
38
|
+
const lookup = (key) => {
|
|
39
|
+
const own = fallback[key] ?? key;
|
|
40
|
+
if (!registered)
|
|
41
|
+
return own;
|
|
42
|
+
try {
|
|
43
|
+
return seam.locale.bind(spec.namespace)(key);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return own;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const t = (key, values) => interpolate(lookup(key), values);
|
|
50
|
+
const label = () => lookup('entry.label');
|
|
51
|
+
seam.slots.inject('sidebar.panellist', function* () {
|
|
52
|
+
yield seam.slots.register({ name: 'sidebar.panellist', id: spec.id, order: spec.order, label }, spec.icon);
|
|
53
|
+
yield seam.slots.register({ name: 'main', key: spec.id, inject: () => spec.body(t) }, spec.component);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=panel.js.map
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The client seams shared by the family's sidebar panels.
|
|
3
|
+
*
|
|
4
|
+
* The browser bundle keeps platform specifiers external, so these seat shapes are the
|
|
5
|
+
* smallest contract the panels consume; keeping one copy prevents the three panels from
|
|
6
|
+
* drifting when the platform adds a field.
|
|
7
|
+
* @module @lmzhen/dsh-evolution-client-kit
|
|
8
|
+
*/
|
|
9
|
+
import type { ReactNode } from 'react';
|
|
10
|
+
/** Registration options for the global sidebar row. */
|
|
11
|
+
export interface PanelRowOptions {
|
|
12
|
+
name: 'sidebar.panellist';
|
|
13
|
+
id: string;
|
|
14
|
+
order: number;
|
|
15
|
+
/** The sidebar re-reads this on every projection, so a locale switch follows. */
|
|
16
|
+
label: () => string;
|
|
17
|
+
}
|
|
18
|
+
/** Registration options for the main-column body dispatched by the panel id. */
|
|
19
|
+
export interface PanelBodyOptions {
|
|
20
|
+
name: 'main';
|
|
21
|
+
key: string;
|
|
22
|
+
/** The face handed to the component: data and callbacks only, never a service. */
|
|
23
|
+
inject: () => unknown;
|
|
24
|
+
}
|
|
25
|
+
/** The locale seat: one namespace registration and a bound translator. */
|
|
26
|
+
export interface LocaleSeat {
|
|
27
|
+
register(namespace: string, dictionaries: {
|
|
28
|
+
zh: Record<string, string>;
|
|
29
|
+
en: Record<string, string>;
|
|
30
|
+
}): () => void;
|
|
31
|
+
bind(namespace: string): (key: string) => string;
|
|
32
|
+
}
|
|
33
|
+
/** The slot registry: an inject body may register and is disposed when it unmounts. */
|
|
34
|
+
export interface SlotsSeat {
|
|
35
|
+
inject(name: string, body: () => Generator<() => void, void, void>): void;
|
|
36
|
+
register(options: PanelRowOptions | PanelBodyOptions, component: (props: never) => ReactNode): () => void;
|
|
37
|
+
}
|
|
38
|
+
/** Everything a sidebar panel asks the shell for. */
|
|
39
|
+
export interface ClientSeam {
|
|
40
|
+
slots: SlotsSeat;
|
|
41
|
+
locale: LocaleSeat;
|
|
42
|
+
}
|
|
43
|
+
/** One client settings row's readable/writable face (platform configForms). */
|
|
44
|
+
export interface ConfigFormSnapshotLike {
|
|
45
|
+
status: 'loading' | 'ready' | 'unavailable';
|
|
46
|
+
value: Record<string, unknown> | undefined;
|
|
47
|
+
writable: boolean;
|
|
48
|
+
revision?: number;
|
|
49
|
+
}
|
|
50
|
+
export interface ConfigFormLike {
|
|
51
|
+
getSnapshot(): ConfigFormSnapshotLike;
|
|
52
|
+
subscribe(listener: () => void): () => void;
|
|
53
|
+
set(field: string, value: unknown): Promise<boolean>;
|
|
54
|
+
unset(field: string): Promise<boolean>;
|
|
55
|
+
}
|
|
56
|
+
export interface ConfigFormsSeat {
|
|
57
|
+
get(entryId: string): ConfigFormLike;
|
|
58
|
+
}
|
|
59
|
+
export interface SettingsFace {
|
|
60
|
+
merge?: ConfigFormLike;
|
|
61
|
+
platform?: ConfigFormLike;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=seam.d.ts.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The family client scale - GENERATED from packages/scripts/client-tokens.json. Do not edit.
|
|
3
|
+
*
|
|
4
|
+
* The shared client kit carries this file, because the scale is one fact: `verify-client-tokens.mjs`
|
|
5
|
+
* (a gate step) fails when the kit copy drifts from the source. The kit bundles into each browser
|
|
6
|
+
* half at build time, so no runtime module-table row is needed.
|
|
7
|
+
*
|
|
8
|
+
* Usage: put `tokenVars(...)` for your root class in the package stylesheet, then read
|
|
9
|
+
* `var(--evo-group-name)` in its rules.
|
|
10
|
+
* Regenerate with: node packages/scripts/gen-client-tokens.mjs packages
|
|
11
|
+
* @module dsh-evolution-client-scale
|
|
12
|
+
*/
|
|
13
|
+
/** Every custom property of the scale, in the source order. */
|
|
14
|
+
export declare const TOKENS: Readonly<Record<string, string>>;
|
|
15
|
+
/** The declarations, without a selector. */
|
|
16
|
+
export declare const TOKEN_CSS: string;
|
|
17
|
+
/**
|
|
18
|
+
* The scale, declared on one element so it never leaks into the host shell.
|
|
19
|
+
* @param scope - the selector the custom properties are declared on.
|
|
20
|
+
* @returns the declaration block to put in the stylesheet.
|
|
21
|
+
*/
|
|
22
|
+
export declare function tokenVars(scope: string): string;
|
|
23
|
+
//# sourceMappingURL=tokens.d.ts.map
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The family client scale - GENERATED from packages/scripts/client-tokens.json. Do not edit.
|
|
3
|
+
*
|
|
4
|
+
* The shared client kit carries this file, because the scale is one fact: `verify-client-tokens.mjs`
|
|
5
|
+
* (a gate step) fails when the kit copy drifts from the source. The kit bundles into each browser
|
|
6
|
+
* half at build time, so no runtime module-table row is needed.
|
|
7
|
+
*
|
|
8
|
+
* Usage: put `tokenVars(...)` for your root class in the package stylesheet, then read
|
|
9
|
+
* `var(--evo-group-name)` in its rules.
|
|
10
|
+
* Regenerate with: node packages/scripts/gen-client-tokens.mjs packages
|
|
11
|
+
* @module dsh-evolution-client-scale
|
|
12
|
+
*/
|
|
13
|
+
/** Every custom property of the scale, in the source order. */
|
|
14
|
+
export const TOKENS = {
|
|
15
|
+
'--evo-type-lg': 'calc(1em + 2px)',
|
|
16
|
+
'--evo-type-body': '1em',
|
|
17
|
+
'--evo-type-sm': 'calc(1em - 1px)',
|
|
18
|
+
'--evo-type-xs': 'calc(1em - 2px)',
|
|
19
|
+
'--evo-space-2': '2px',
|
|
20
|
+
'--evo-space-4': '4px',
|
|
21
|
+
'--evo-space-6': '6px',
|
|
22
|
+
'--evo-space-8': '8px',
|
|
23
|
+
'--evo-space-10': '10px',
|
|
24
|
+
'--evo-space-12': '12px',
|
|
25
|
+
'--evo-space-14': '14px',
|
|
26
|
+
'--evo-space-16': '16px',
|
|
27
|
+
'--evo-space-20': '20px',
|
|
28
|
+
'--evo-radius-chip': '4px',
|
|
29
|
+
'--evo-radius-control': '8px',
|
|
30
|
+
'--evo-radius-surface': '12px',
|
|
31
|
+
'--evo-radius-pill': '999px',
|
|
32
|
+
'--evo-hairline-width': '.5px',
|
|
33
|
+
'--evo-hairline-marker': '2px',
|
|
34
|
+
'--evo-measure-read': '80ch',
|
|
35
|
+
'--evo-leading-base': '1.5',
|
|
36
|
+
'--evo-tone-quiet': 'var(--dsw-alias-label-secondary)',
|
|
37
|
+
'--evo-tone-primary': 'var(--dsw-alias-brand-primary)',
|
|
38
|
+
'--evo-tone-danger': 'var(--dsw-alias-state-error-primary)',
|
|
39
|
+
'--evo-tone-success': 'var(--dsw-alias-state-success-primary)',
|
|
40
|
+
'--evo-focus-ring': '2px solid var(--dsw-alias-label-primary)',
|
|
41
|
+
'--evo-focus-offset': '-2px',
|
|
42
|
+
'--evo-cap-aside': '280px',
|
|
43
|
+
'--evo-cap-block': '260px',
|
|
44
|
+
};
|
|
45
|
+
/** The declarations, without a selector. */
|
|
46
|
+
export const TOKEN_CSS = '--evo-type-lg:calc(1em + 2px);--evo-type-body:1em;--evo-type-sm:calc(1em - 1px);--evo-type-xs:calc(1em - 2px);--evo-space-2:2px;--evo-space-4:4px;--evo-space-6:6px;--evo-space-8:8px;--evo-space-10:10px;--evo-space-12:12px;--evo-space-14:14px;--evo-space-16:16px;--evo-space-20:20px;--evo-radius-chip:4px;--evo-radius-control:8px;--evo-radius-surface:12px;--evo-radius-pill:999px;--evo-hairline-width:.5px;--evo-hairline-marker:2px;--evo-measure-read:80ch;--evo-leading-base:1.5;--evo-tone-quiet:var(--dsw-alias-label-secondary);--evo-tone-primary:var(--dsw-alias-brand-primary);--evo-tone-danger:var(--dsw-alias-state-error-primary);--evo-tone-success:var(--dsw-alias-state-success-primary);--evo-focus-ring:2px solid var(--dsw-alias-label-primary);--evo-focus-offset:-2px;--evo-cap-aside:280px;--evo-cap-block:260px';
|
|
47
|
+
/**
|
|
48
|
+
* The scale, declared on one element so it never leaks into the host shell.
|
|
49
|
+
* @param scope - the selector the custom properties are declared on.
|
|
50
|
+
* @returns the declaration block to put in the stylesheet.
|
|
51
|
+
*/
|
|
52
|
+
export function tokenVars(scope) {
|
|
53
|
+
return scope + '{' + TOKEN_CSS + '}';
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=tokens.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read the panel workspace id from either workspace service shape.
|
|
3
|
+
* @module @lmzhen/dsh-evolution-client-kit
|
|
4
|
+
*/
|
|
5
|
+
import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
6
|
+
/**
|
|
7
|
+
* Best-effort workspace id; a seat that is absent or refuses leaves the panel in local scope.
|
|
8
|
+
* @param ctx - the client context.
|
|
9
|
+
* @returns the current workspace id, or undefined.
|
|
10
|
+
*/
|
|
11
|
+
export declare function panelWorkspaceId(ctx: ClientContext): string | undefined;
|
|
12
|
+
//# sourceMappingURL=workspace.d.ts.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Best-effort workspace id; a seat that is absent or refuses leaves the panel in local scope.
|
|
3
|
+
* @param ctx - the client context.
|
|
4
|
+
* @returns the current workspace id, or undefined.
|
|
5
|
+
*/
|
|
6
|
+
export function panelWorkspaceId(ctx) {
|
|
7
|
+
const get = ctx.get;
|
|
8
|
+
if (typeof get !== 'function')
|
|
9
|
+
return undefined;
|
|
10
|
+
for (const name of ['workspace', 'uiWorkspace']) {
|
|
11
|
+
try {
|
|
12
|
+
const seat = get.call(ctx, name);
|
|
13
|
+
const id = seat?.workspaceId ?? seat?.currentWorkspaceId;
|
|
14
|
+
if (typeof id === 'string' && id.trim() !== '')
|
|
15
|
+
return id;
|
|
16
|
+
}
|
|
17
|
+
catch { /* optional service: absence is the local scope */ }
|
|
18
|
+
}
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=workspace.js.map
|
package/package.json
CHANGED
|
@@ -1,17 +1,66 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lmzhen/dsh-evolution-client-kit",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
3
|
+
"description": "Shared browser seams and lifecycle for the evolution family sidebar panels (community build)",
|
|
4
|
+
"version": "0.30.28",
|
|
5
5
|
"private": false,
|
|
6
|
-
"
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
7
9
|
"repository": {
|
|
8
10
|
"type": "git",
|
|
9
|
-
"url": "git+https://github.com/lmzhen/dsh-evolution.git"
|
|
11
|
+
"url": "git+https://github.com/lmzhen/dsh-evolution.git",
|
|
12
|
+
"directory": "packages/evolution-client-kit"
|
|
10
13
|
},
|
|
11
|
-
"
|
|
12
|
-
|
|
14
|
+
"type": "module",
|
|
15
|
+
"main": "lib/index.js",
|
|
16
|
+
"types": "lib/types/index.d.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./lib/types/index.d.ts",
|
|
20
|
+
"default": "./lib/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./generated/contracts": {
|
|
23
|
+
"types": "./lib/types/generated/contracts.generated.d.ts",
|
|
24
|
+
"default": "./lib/generated/contracts.generated.js"
|
|
25
|
+
},
|
|
26
|
+
"./generated/fallbacks": {
|
|
27
|
+
"types": "./lib/types/generated/fallbacks.generated.d.ts",
|
|
28
|
+
"default": "./lib/generated/fallbacks.generated.js"
|
|
29
|
+
},
|
|
30
|
+
"./generated/route-client": {
|
|
31
|
+
"types": "./lib/types/generated/route-client.generated.d.ts",
|
|
32
|
+
"default": "./lib/generated/route-client.generated.js"
|
|
33
|
+
},
|
|
34
|
+
"./generated/routes": {
|
|
35
|
+
"types": "./lib/types/generated/routes.generated.d.ts",
|
|
36
|
+
"default": "./lib/generated/routes.generated.js"
|
|
37
|
+
},
|
|
38
|
+
"./generated/states": {
|
|
39
|
+
"types": "./lib/types/generated/states.generated.d.ts",
|
|
40
|
+
"default": "./lib/generated/states.generated.js"
|
|
41
|
+
},
|
|
42
|
+
"./tokens": {
|
|
43
|
+
"types": "./lib/types/tokens.d.ts",
|
|
44
|
+
"default": "./lib/tokens.js"
|
|
45
|
+
},
|
|
46
|
+
"./package.json": "./package.json"
|
|
13
47
|
},
|
|
14
48
|
"files": [
|
|
15
|
-
"
|
|
16
|
-
|
|
49
|
+
"lib/**/*.js",
|
|
50
|
+
"lib/types/**/*.d.ts",
|
|
51
|
+
"package.json"
|
|
52
|
+
],
|
|
53
|
+
"license": "MIT",
|
|
54
|
+
"peerDependencies": {
|
|
55
|
+
"@deepseek-ai/cordis": "^4.0.5",
|
|
56
|
+
"react": "^18.2.0"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@deepseek-ai/cordis": "^4.0.5",
|
|
60
|
+
"@types/react": "~18.3.1",
|
|
61
|
+
"react": "^18.2.0"
|
|
62
|
+
},
|
|
63
|
+
"engines": {
|
|
64
|
+
"dsh": ">=0.2.1-rc.2 <0.2.2-0"
|
|
65
|
+
}
|
|
17
66
|
}
|