dsh-plugin-effort-declare 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRIBUTING.en.md +5 -1
- package/CONTRIBUTING.md +5 -1
- package/INSTALL.en.md +1 -1
- package/INSTALL.md +1 -1
- package/README.en.md +8 -7
- package/README.md +8 -7
- package/lib/client.js +389 -65
- package/lib/client.js.map +1 -1
- package/lib/types/client/EffortDeclareSection.d.ts +12 -2
- package/lib/types/client/EffortDeclareSection.d.ts.map +1 -1
- package/lib/types/client/build-info.d.ts +2 -0
- package/lib/types/client/build-info.d.ts.map +1 -0
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/load-drafts.d.ts +36 -6
- package/lib/types/client/load-drafts.d.ts.map +1 -1
- package/lib/types/client/locales.d.ts +1 -1
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/core/attribution.d.ts +13 -0
- package/lib/types/core/attribution.d.ts.map +1 -0
- package/lib/types/core/catalog.d.ts +5 -3
- package/lib/types/core/catalog.d.ts.map +1 -1
- package/lib/types/core/drafts.d.ts +34 -2
- package/lib/types/core/drafts.d.ts.map +1 -1
- package/lib/types/core/efforts.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/README.en.md +1 -1
- package/src/README.md +1 -1
- package/src/client/EffortDeclareSection.tsx +158 -46
- package/src/client/README.en.md +5 -3
- package/src/client/README.md +5 -3
- package/src/client/build-info.ts +12 -0
- package/src/client/effort-declare.module.css +8 -0
- package/src/client/globals.d.ts +5 -0
- package/src/client/index.ts +14 -9
- package/src/client/load-drafts.ts +124 -8
- package/src/client/locales.ts +3 -0
- package/src/core/README.en.md +4 -3
- package/src/core/README.md +4 -3
- package/src/core/attribution.ts +24 -0
- package/src/core/catalog.ts +5 -3
- package/src/core/drafts.ts +151 -4
- package/src/core/efforts.ts +4 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Dictionary namespace for the effort-declare settings section.
|
|
3
3
|
*/
|
|
4
|
-
export type EffortDeclareKey = 'nav' | 'title' | 'intro' | 'empty' | 'emptyHint' | 'loadError' | 'loading' | 'readOnly' | 'save' | 'saving' | 'cancel' | 'saved' | 'conflict' | 'dirtyConflict' | 'presets' | 'presetDeepSeek' | 'presetOpenAI' | 'presetToggle' | 'presetToggleWarn' | 'model' | 'levels' | 'wire' | 'offMode' | 'offAbsent' | 'offEmpty' | 'offValue' | 'offValuePlaceholder' | 'clear' | 'advanced' | 'thinkingFormat' | 'thinkingFormatDefault' | 'supportsDeveloperRole' | 'supportsReasoningEffort' | 'developerTrueHint' | 'compatSummary' | 'errorEmpty' | 'errorOffOnly' | 'errorBadWire' | 'noModels' | 'reload';
|
|
4
|
+
export type EffortDeclareKey = 'nav' | 'title' | 'intro' | 'empty' | 'emptyHint' | 'loadError' | 'loading' | 'readOnly' | 'save' | 'saving' | 'saveBusy' | 'cancel' | 'saved' | 'conflict' | 'dirtyConflict' | 'presets' | 'presetDeepSeek' | 'presetOpenAI' | 'presetToggle' | 'presetToggleWarn' | 'model' | 'levels' | 'wire' | 'offMode' | 'offAbsent' | 'offEmpty' | 'offValue' | 'offValuePlaceholder' | 'clear' | 'advanced' | 'thinkingFormat' | 'thinkingFormatDefault' | 'supportsDeveloperRole' | 'supportsReasoningEffort' | 'developerTrueHint' | 'compatSummary' | 'errorEmpty' | 'errorOffOnly' | 'errorBadWire' | 'noModels' | 'reload';
|
|
5
5
|
export declare const NS = "plugin-effort-declare";
|
|
6
6
|
export declare const zh: Record<EffortDeclareKey, string>;
|
|
7
7
|
export declare const en: Record<EffortDeclareKey, string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,gBAAgB,GACxB,KAAK,GACL,OAAO,GACP,OAAO,GACP,OAAO,GACP,WAAW,GACX,WAAW,GACX,SAAS,GACT,UAAU,GACV,MAAM,GACN,QAAQ,GACR,QAAQ,GACR,OAAO,GACP,UAAU,GACV,eAAe,GACf,SAAS,GACT,gBAAgB,GAChB,cAAc,GACd,cAAc,GACd,kBAAkB,GAClB,OAAO,GACP,QAAQ,GACR,MAAM,GACN,SAAS,GACT,WAAW,GACX,UAAU,GACV,UAAU,GACV,qBAAqB,GACrB,OAAO,GACP,UAAU,GACV,gBAAgB,GAChB,uBAAuB,GACvB,uBAAuB,GACvB,yBAAyB,GACzB,mBAAmB,GACnB,eAAe,GACf,YAAY,GACZ,cAAc,GACd,cAAc,GACd,UAAU,GACV,QAAQ,CAAA;AAEZ,eAAO,MAAM,EAAE,0BAA0B,CAAA;AAEzC,eAAO,MAAM,EAAE,EAAE,MAAM,CAAC,gBAAgB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,gBAAgB,GACxB,KAAK,GACL,OAAO,GACP,OAAO,GACP,OAAO,GACP,WAAW,GACX,WAAW,GACX,SAAS,GACT,UAAU,GACV,MAAM,GACN,QAAQ,GACR,UAAU,GACV,QAAQ,GACR,OAAO,GACP,UAAU,GACV,eAAe,GACf,SAAS,GACT,gBAAgB,GAChB,cAAc,GACd,cAAc,GACd,kBAAkB,GAClB,OAAO,GACP,QAAQ,GACR,MAAM,GACN,SAAS,GACT,WAAW,GACX,UAAU,GACV,UAAU,GACV,qBAAqB,GACrB,OAAO,GACP,UAAU,GACV,gBAAgB,GAChB,uBAAuB,GACvB,uBAAuB,GACvB,yBAAyB,GACzB,mBAAmB,GACnB,eAAe,GACf,YAAY,GACZ,cAAc,GACd,cAAc,GACd,UAAU,GACV,QAAQ,CAAA;AAEZ,eAAO,MAAM,EAAE,0BAA0B,CAAA;AAEzC,eAAO,MAAM,EAAE,EAAE,MAAM,CAAC,gBAAgB,EAAE,MAAM,CA0C/C,CAAA;AAED,eAAO,MAAM,EAAE,EAAE,MAAM,CAAC,gBAAgB,EAAE,MAAM,CA0C/C,CAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin footer attribution. Version and end-year are frozen into the client
|
|
3
|
+
* bundle at pack time; this module only formats the line.
|
|
4
|
+
*/
|
|
5
|
+
/** First publication year (LICENSE). Not the user's wall clock. */
|
|
6
|
+
export declare const COPYRIGHT_FROM = 2026;
|
|
7
|
+
export declare const COPYRIGHT_HOLDER = "Stardust";
|
|
8
|
+
/**
|
|
9
|
+
* `0.1.2 © 2026 Stardust` or `0.1.2 © 2026–2027 Stardust`.
|
|
10
|
+
* Throws if version is empty or `to < from` — a bad stamp must not render.
|
|
11
|
+
*/
|
|
12
|
+
export declare function formatAttribution(version: string, from: number, to: number): string;
|
|
13
|
+
//# sourceMappingURL=attribution.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attribution.d.ts","sourceRoot":"","sources":["../../../src/core/attribution.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,mEAAmE;AACnE,eAAO,MAAM,cAAc,OAAO,CAAA;AAElC,eAAO,MAAM,gBAAgB,aAAa,CAAA;AAE1C;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CASnF"}
|
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Levels match `@deepseek-ai/dsh-llm-pi-ai` catalog.ts `THINKING_LEVELS`.
|
|
5
5
|
* Formats match `SUPPORTED_THINKING_FORMATS` in the same file (rc.8).
|
|
6
|
-
* Tests pin these lists against
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* Tests pin these lists against a checked-in schema fixture
|
|
7
|
+
* (`tests/fixtures/pi-ai-thinking-format-union.ts`) and the local level
|
|
8
|
+
* whitelist. The settings page never offers the handwritten thinkingFormat
|
|
9
|
+
* list as writable choices — only the live schema union, plus a stored
|
|
10
|
+
* value that the union omitted.
|
|
9
11
|
*/
|
|
10
12
|
/** Selectable reasoning levels, in pi-ai escalation order. */
|
|
11
13
|
export declare const THINKING_LEVELS: readonly ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../../src/core/catalog.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../../src/core/catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,8DAA8D;AAC9D,eAAO,MAAM,eAAe,sEAQlB,CAAA;AAEV,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAA;AAE5D,yEAAyE;AACzE,eAAO,MAAM,2BAA2B,6DAEvC,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,yBAAyB,gJAW5B,CAAA;AAEV,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,yBAAyB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEvE,sDAAsD;AACtD,eAAO,MAAM,kBAAkB,uBAAuB,CAAA;AAEtD,6CAA6C;AAC7C,eAAO,MAAM,YAAY,cAAc,CAAA;AAEvC,kDAAkD;AAClD,eAAO,MAAM,eAAe,iBAAiB,CAAA;AAE7C,+CAA+C;AAC/C,eAAO,MAAM,iBAAiB,sBAAsB,CAAA;AAEpD,kEAAkE;AAClE,eAAO,MAAM,kBAAkB,YAAgB,CAAA"}
|
|
@@ -13,6 +13,10 @@ export interface RouteDraft {
|
|
|
13
13
|
/** Whether `compat` is present on the user profile (not the effective view). */
|
|
14
14
|
compatPresent: boolean;
|
|
15
15
|
}
|
|
16
|
+
/** JSON-stable equality matching pathOps (key order included). */
|
|
17
|
+
export declare function sliceEqual(left: unknown, right: unknown): boolean;
|
|
18
|
+
/** Whether two settings slices differ. */
|
|
19
|
+
export declare function sliceChanged(before: unknown, after: unknown): boolean;
|
|
16
20
|
/** Build a draft from the stored user subtree (never from effective `value`). */
|
|
17
21
|
export declare function routeDraftFromUserProfile(args: {
|
|
18
22
|
provider: string;
|
|
@@ -31,8 +35,36 @@ export declare function applySaveSuccess(drafts: readonly RouteDraft[], savedPro
|
|
|
31
35
|
revision: number;
|
|
32
36
|
}): RouteDraft[];
|
|
33
37
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
38
|
+
* Membership follows the latest user-layer models list (Models page add/delete).
|
|
39
|
+
* Local unsaved `reasoningEfforts` (including a cleared key) overlay by id.
|
|
40
|
+
*/
|
|
41
|
+
export declare function mergeModelsById(args: {
|
|
42
|
+
prevModels: readonly Record<string, unknown>[];
|
|
43
|
+
prevOriginal: readonly Record<string, unknown>[];
|
|
44
|
+
incomingModels: readonly Record<string, unknown>[];
|
|
45
|
+
incomingOriginal: readonly Record<string, unknown>[];
|
|
46
|
+
}): {
|
|
47
|
+
models: Record<string, unknown>[];
|
|
48
|
+
conflicted: boolean;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Three-way compat merge: locally changed keys stay local; everything else
|
|
52
|
+
* follows incoming. Conflict only when a locally dirty key also moved in originals.
|
|
53
|
+
*/
|
|
54
|
+
export declare function mergeCompat(args: {
|
|
55
|
+
prev: Record<string, unknown>;
|
|
56
|
+
prevOriginal: Record<string, unknown>;
|
|
57
|
+
incoming: Record<string, unknown>;
|
|
58
|
+
incomingOriginal: Record<string, unknown>;
|
|
59
|
+
}): {
|
|
60
|
+
compat: Record<string, unknown>;
|
|
61
|
+
conflicted: boolean;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Apply a freshly loaded table. Membership and metadata follow incoming;
|
|
65
|
+
* unsaved reasoningEfforts / dirty compat keys overlay by id. Conflict only
|
|
66
|
+
* when a locally dirty field also changed in originals (revision-only bumps
|
|
67
|
+
* and sibling-card saves do not warn).
|
|
36
68
|
*/
|
|
37
69
|
export declare function mergeLoadedDrafts(current: readonly RouteDraft[], incoming: readonly RouteDraft[], options: {
|
|
38
70
|
preserveDirty: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drafts.d.ts","sourceRoot":"","sources":["../../../src/core/drafts.ts"],"names":[],"mappings":"AAQA,iEAAiE;AACjE,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,0EAA0E;IAC1E,YAAY,EAAE,SAAS,MAAM,EAAE,CAAA;IAC/B,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;IACjC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;IACzC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/B,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACvC,gFAAgF;IAChF,aAAa,EAAE,OAAO,CAAA;CACvB;AAED,iFAAiF;AACjF,wBAAgB,yBAAyB,CAAC,IAAI,EAAE;IAC9C,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,SAAS,MAAM,EAAE,CAAA;IAC/B,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,OAAO,CAAA;CACrB,GAAG,UAAU,CAgBb;AAED,wEAAwE;AACxE,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAQrD;AAED,+EAA+E;AAC/E,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,CAOxD;AAED,4FAA4F;AAC5F,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,SAAS,UAAU,EAAE,EAC7B,aAAa,EAAE,MAAM,EACrB,KAAK,EAAE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACzC,UAAU,EAAE,CAcd;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,SAAS,UAAU,EAAE,EAC9B,QAAQ,EAAE,SAAS,UAAU,EAAE,EAC/B,OAAO,EAAE;IAAE,aAAa,EAAE,OAAO,CAAA;CAAE,GAClC;IAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"drafts.d.ts","sourceRoot":"","sources":["../../../src/core/drafts.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"efforts.d.ts","sourceRoot":"","sources":["../../../src/core/efforts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,aAAa,EAAE,MAAM,cAAc,CAAA;AAElE,uEAAuE;AACvE,MAAM,MAAM,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,CAAA;AAE5E,mEAAmE;AACnE,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAA;AAElD,MAAM,MAAM,qBAAqB,GAAG,gBAAgB,GAAG,KAAK,GAAG,SAAS,CAAA;AAExE,kDAAkD;AAClD,wBAAgB,OAAO,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAM/F;AAED,wEAAwE;AACxE,wBAAgB,QAAQ,CACtB,OAAO,EAAE,gBAAgB,EACzB,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,MAAM,GACZ,gBAAgB,
|
|
1
|
+
{"version":3,"file":"efforts.d.ts","sourceRoot":"","sources":["../../../src/core/efforts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,aAAa,EAAE,MAAM,cAAc,CAAA;AAElE,uEAAuE;AACvE,MAAM,MAAM,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC,CAAA;AAE5E,mEAAmE;AACnE,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAA;AAElD,MAAM,MAAM,qBAAqB,GAAG,gBAAgB,GAAG,KAAK,GAAG,SAAS,CAAA;AAExE,kDAAkD;AAClD,wBAAgB,OAAO,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAM/F;AAED,wEAAwE;AACxE,wBAAgB,QAAQ,CACtB,OAAO,EAAE,gBAAgB,EACzB,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,MAAM,GACZ,gBAAgB,CASlB;AAED,uEAAuE;AACvE,wBAAgB,QAAQ,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,EAAE,KAAK,EAAE,OAAO,CAAC,aAAa,EAAE,KAAK,CAAC,GAAG,OAAO,CAE7G;AAED,+EAA+E;AAC/E,wBAAgB,WAAW,CACzB,OAAO,EAAE,gBAAgB,EACzB,KAAK,EAAE,OAAO,CAAC,aAAa,EAAE,KAAK,CAAC,EACpC,OAAO,EAAE,OAAO,EAChB,IAAI,CAAC,EAAE,MAAM,GACZ,gBAAgB,CAKlB;AAED,0DAA0D;AAC1D,wBAAgB,eAAe,CAC7B,OAAO,EAAE,gBAAgB,EACzB,KAAK,EAAE,OAAO,CAAC,aAAa,EAAE,KAAK,CAAC,EACpC,IAAI,EAAE,MAAM,GACX,gBAAgB,CAGlB;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS,CAwBxG;AAED,sEAAsE;AACtE,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAI3F;AAED,2FAA2F;AAC3F,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,gBAAgB,GAAG,SAAS,CAKtF;AAED,wDAAwD;AACxD,wBAAgB,YAAY,CAC1B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,OAAO,EAAE,gBAAgB,GAAG,SAAS,GACpC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQzB"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-effort-declare",
|
|
3
3
|
"description": "Settings page to declare per-model reasoningEfforts and openai-completions compat on hand-declared llm-pi-ai routes. Writes the official namespace; does not intercept llm/stream or replace the Models page.",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.3",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
package/src/README.en.md
CHANGED
|
@@ -16,7 +16,7 @@ Install: [INSTALL.en.md](../INSTALL.en.md). Usage: root [README.en.md](../README
|
|
|
16
16
|
| Path | Role |
|
|
17
17
|
| --- | --- |
|
|
18
18
|
| [`index.ts`](./index.ts) | Host entry. Exports `name` and `apply`. v1 registers no host services or adapters; the bundle still needs this entry so `dsh plugin add` can load the package. |
|
|
19
|
-
| [`core/`](./core/) | UI-free pure functions: efforts, presets, save ops, draft merge, route filtering. Shared by the client and tests; the v1 host entry does not import them (`tsconfig.host` still compiles `core` for types). |
|
|
19
|
+
| [`core/`](./core/) | UI-free pure functions: efforts, presets, save ops, draft merge, route filtering, footer attribution. Shared by the client and tests; the v1 host entry does not import them (`tsconfig.host` still compiles `core` for types). |
|
|
20
20
|
| [`client/`](./client/) | Browser half: locale, settings page, `settings.mutate` against `llm-pi-ai`. |
|
|
21
21
|
| [`css-modules.d.ts`](./css-modules.d.ts) | Types for `*.module.css` (class map plus `cssText` / `cssTagId`). |
|
|
22
22
|
|
package/src/README.md
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
| 路径 | 说明 |
|
|
17
17
|
| --- | --- |
|
|
18
18
|
| [`index.ts`](./index.ts) | Host 入口。导出 `name` 与 `apply`。v1 不注册 host 服务、不挂适配器;组合包仍需要这一入口才能被 `dsh plugin add` 加载。 |
|
|
19
|
-
| [`core/`](./core/) | 与 UI 无关的纯函数:档位、预设、保存 ops
|
|
19
|
+
| [`core/`](./core/) | 与 UI 无关的纯函数:档位、预设、保存 ops、草稿合并、路由过滤、页脚归因格式。client 与单测共用;Host 入口 v1 不引用这些函数(`tsconfig.host` 仍编译 `core` 以产出类型)。 |
|
|
20
20
|
| [`client/`](./client/) | 浏览器半区:locale、设置页、对 `llm-pi-ai` 的 `settings.mutate`。 |
|
|
21
21
|
| [`css-modules.d.ts`](./css-modules.d.ts) | `*.module.css` 模块类型(class map + `cssText` / `cssTagId`)。 |
|
|
22
22
|
|
|
@@ -30,18 +30,32 @@ import { applyPresetCompat, applyPresetEfforts, PRESETS, type PresetId } from '.
|
|
|
30
30
|
import { buildSaveOps } from '../core/path-ops.ts'
|
|
31
31
|
import { cloneModels, cloneObject } from '../core/paths.ts'
|
|
32
32
|
import { modelEffortError } from '../core/validate.ts'
|
|
33
|
-
import {
|
|
33
|
+
import { PLUGIN_FOOTER_TEXT } from './build-info.ts'
|
|
34
|
+
import {
|
|
35
|
+
foldReloadNotices,
|
|
36
|
+
isOwnDocumentEcho,
|
|
37
|
+
loadDrafts,
|
|
38
|
+
waitForNamespaceRevision,
|
|
39
|
+
waitForNamespaceRevisionChange,
|
|
40
|
+
type LoadDraftsMode,
|
|
41
|
+
} from './load-drafts.ts'
|
|
34
42
|
import { validateSaveDraft, type SchemaOps } from './schema-ops.ts'
|
|
35
43
|
import type { EffortDeclareKey } from './locales.ts'
|
|
36
44
|
import css from './effort-declare.module.css'
|
|
37
45
|
|
|
38
|
-
export type InvalidationSource = 'settings' | 'directory'
|
|
46
|
+
export type InvalidationSource = 'settings' | 'directory' | 'reset' | 'writable'
|
|
47
|
+
|
|
48
|
+
export type Invalidation =
|
|
49
|
+
| { source: 'writable' }
|
|
50
|
+
| { source: 'directory' }
|
|
51
|
+
| { source: 'reset' }
|
|
52
|
+
| { source: 'settings'; revision: number }
|
|
39
53
|
|
|
40
54
|
export interface EffortDeclareSectionInjected {
|
|
41
55
|
api: Pick<IApiClient, 'settings' | 'llm'>
|
|
42
56
|
describe: SettingsDescribeFace
|
|
43
57
|
schema: SchemaOps
|
|
44
|
-
subscribeInvalidate: (listener: (
|
|
58
|
+
subscribeInvalidate: (listener: (event: Invalidation) => void) => () => void
|
|
45
59
|
}
|
|
46
60
|
|
|
47
61
|
export interface EffortDeclareSectionProps extends Partial<EffortDeclareSectionInjected> {
|
|
@@ -165,18 +179,20 @@ function RouteCard(props: {
|
|
|
165
179
|
draft: RouteDraft
|
|
166
180
|
formats: readonly string[]
|
|
167
181
|
writable: boolean
|
|
182
|
+
/** This card is the in-flight mutate — show “Saving…”. */
|
|
168
183
|
busy: boolean
|
|
169
|
-
|
|
184
|
+
/** Namespace-level lock: any mutate, read-only, or first load. */
|
|
185
|
+
saveLocked: boolean
|
|
170
186
|
notice: CardNotice | undefined
|
|
171
187
|
t: (key: EffortDeclareKey) => string
|
|
172
188
|
onChange: (draft: RouteDraft) => void
|
|
173
189
|
onSave: (draft: RouteDraft) => void
|
|
174
190
|
onCancel: (draft: RouteDraft) => void
|
|
175
191
|
}): ReactNode {
|
|
176
|
-
const { draft, writable, busy,
|
|
192
|
+
const { draft, writable, busy, saveLocked, t, onChange } = props
|
|
177
193
|
const noModels = draft.models.length === 0
|
|
178
194
|
const editDisabled = !writable || busy || noModels
|
|
179
|
-
const
|
|
195
|
+
const saveDisabled = saveLocked || noModels
|
|
180
196
|
const formats = thinkingFormatChoices(props.formats, draft.compat.thinkingFormat)
|
|
181
197
|
const summary = compatSummary(draft.compat)
|
|
182
198
|
const sameWire = draft.compat.supportsReasoningEffort === false
|
|
@@ -302,7 +318,7 @@ function RouteCard(props: {
|
|
|
302
318
|
<button
|
|
303
319
|
type="button"
|
|
304
320
|
className={css.primaryButton}
|
|
305
|
-
disabled={
|
|
321
|
+
disabled={saveDisabled || !dirty || clientError !== undefined}
|
|
306
322
|
onClick={() => { props.onSave(draft) }}
|
|
307
323
|
>
|
|
308
324
|
{busy ? t('saving') : t('save')}
|
|
@@ -326,53 +342,133 @@ export function EffortDeclareSection(props: EffortDeclareSectionProps): ReactNod
|
|
|
326
342
|
const [notices, setNotices] = useState<Record<string, CardNotice>>({})
|
|
327
343
|
const generationRef = useRef(0)
|
|
328
344
|
const draftsRef = useRef(drafts)
|
|
345
|
+
const echoedRevisionRef = useRef<number | undefined>(undefined)
|
|
346
|
+
const pendingRevisionRef = useRef<number | undefined>(undefined)
|
|
347
|
+
const busyRouteRef = useRef<string | null>(null)
|
|
348
|
+
const abortRef = useRef<AbortController | null>(null)
|
|
329
349
|
draftsRef.current = drafts
|
|
330
350
|
|
|
331
|
-
const
|
|
351
|
+
const applyDrafts = (next: RouteDraft[] | ((current: RouteDraft[]) => RouteDraft[])): void => {
|
|
352
|
+
const resolved = typeof next === 'function' ? next(draftsRef.current) : next
|
|
353
|
+
draftsRef.current = resolved
|
|
354
|
+
setDrafts(resolved)
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const snapshotMode = (): LoadDraftsMode => (
|
|
358
|
+
describe === undefined || describe.getSnapshot().status === 'idle' ? 'ensure' : 'snapshot'
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
const beginGeneration = (): { generation: number; signal: AbortSignal } => {
|
|
362
|
+
abortRef.current?.abort()
|
|
363
|
+
const abort = new AbortController()
|
|
364
|
+
abortRef.current = abort
|
|
365
|
+
return { generation: nextGeneration(generationRef), signal: abort.signal }
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const failGeneration = (generation: number, failure: unknown): void => {
|
|
369
|
+
if (!generationIsCurrent(generationRef, generation)) return
|
|
370
|
+
setStatus('error')
|
|
371
|
+
setError(failure instanceof Error ? failure.message : t('loadError'))
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const settleReload = (
|
|
375
|
+
generation: number,
|
|
376
|
+
preserveDirty: boolean,
|
|
377
|
+
result: Awaited<ReturnType<typeof loadDrafts>>,
|
|
378
|
+
): void => {
|
|
379
|
+
if (!generationIsCurrent(generationRef, generation)) return
|
|
380
|
+
setWritable(result.writable)
|
|
381
|
+
setFormats(result.formats)
|
|
382
|
+
if (result.error !== undefined) {
|
|
383
|
+
setStatus('error')
|
|
384
|
+
setError(result.error)
|
|
385
|
+
return
|
|
386
|
+
}
|
|
387
|
+
const merged = mergeLoadedDrafts(draftsRef.current, result.drafts, { preserveDirty })
|
|
388
|
+
applyDrafts(merged.drafts)
|
|
389
|
+
setNotices(current => foldReloadNotices(current, {
|
|
390
|
+
conflicted: merged.conflicted,
|
|
391
|
+
conflictNotice: { kind: 'conflict', text: t('dirtyConflict') },
|
|
392
|
+
liveProviders: merged.drafts.map(draft => draft.provider),
|
|
393
|
+
}))
|
|
394
|
+
setStatus('ready')
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const loadSnapshotThenSettle = async (generation: number, preserveDirty: boolean): Promise<void> => {
|
|
398
|
+
if (api === undefined || describe === undefined || schema === undefined) return
|
|
399
|
+
if (!generationIsCurrent(generationRef, generation)) return
|
|
400
|
+
try {
|
|
401
|
+
const result = await loadDrafts(api, describe, schema, 'snapshot')
|
|
402
|
+
settleReload(generation, preserveDirty, result)
|
|
403
|
+
} catch (failure) {
|
|
404
|
+
failGeneration(generation, failure)
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const reload = useCallback((preserveDirty: boolean, mode: LoadDraftsMode = 'ensure'): void => {
|
|
332
409
|
if (api === undefined || describe === undefined || schema === undefined) {
|
|
333
410
|
setStatus('error')
|
|
334
411
|
setError(t('loadError'))
|
|
335
412
|
return
|
|
336
413
|
}
|
|
337
|
-
const generation =
|
|
338
|
-
setStatus('loading')
|
|
414
|
+
const { generation } = beginGeneration()
|
|
415
|
+
if (draftsRef.current.length === 0) setStatus('loading')
|
|
339
416
|
setError('')
|
|
340
|
-
void loadDrafts(api, describe, schema).then((result) => {
|
|
341
|
-
|
|
342
|
-
setWritable(result.writable)
|
|
343
|
-
setFormats(result.formats)
|
|
344
|
-
if (result.error !== undefined) {
|
|
345
|
-
setStatus('error')
|
|
346
|
-
setError(result.error)
|
|
347
|
-
return
|
|
348
|
-
}
|
|
349
|
-
const merged = mergeLoadedDrafts(draftsRef.current, result.drafts, { preserveDirty })
|
|
350
|
-
setDrafts(merged.drafts)
|
|
351
|
-
if (merged.conflicted.length > 0) {
|
|
352
|
-
setNotices(current => {
|
|
353
|
-
const next = { ...current }
|
|
354
|
-
for (const provider of merged.conflicted) {
|
|
355
|
-
next[provider] = { kind: 'conflict', text: t('dirtyConflict') }
|
|
356
|
-
}
|
|
357
|
-
return next
|
|
358
|
-
})
|
|
359
|
-
}
|
|
360
|
-
setStatus('ready')
|
|
417
|
+
void loadDrafts(api, describe, schema, mode).then((result) => {
|
|
418
|
+
settleReload(generation, preserveDirty, result)
|
|
361
419
|
}, (failure: unknown) => {
|
|
362
|
-
|
|
363
|
-
setStatus('error')
|
|
364
|
-
setError(failure instanceof Error ? failure.message : t('loadError'))
|
|
420
|
+
failGeneration(generation, failure)
|
|
365
421
|
})
|
|
366
422
|
}, [api, describe, schema, t])
|
|
367
423
|
|
|
368
|
-
|
|
424
|
+
const refreshAtRevision = useCallback((revision: number, preserveDirty: boolean): void => {
|
|
425
|
+
if (api === undefined || describe === undefined || schema === undefined) return
|
|
426
|
+
const { generation, signal } = beginGeneration()
|
|
427
|
+
if (draftsRef.current.length === 0) setStatus('loading')
|
|
428
|
+
setError('')
|
|
429
|
+
void waitForNamespaceRevision(describe, LLM_PI_AI_NS, revision, signal).then((outcome) => {
|
|
430
|
+
if (outcome === 'aborted' || !generationIsCurrent(generationRef, generation)) return
|
|
431
|
+
return loadSnapshotThenSettle(generation, preserveDirty)
|
|
432
|
+
}, (failure: unknown) => {
|
|
433
|
+
failGeneration(generation, failure)
|
|
434
|
+
})
|
|
435
|
+
}, [api, describe, schema, t])
|
|
436
|
+
|
|
437
|
+
const flushPendingSettings = (refresh: (revision: number, preserveDirty: boolean) => void): void => {
|
|
438
|
+
const pending = pendingRevisionRef.current
|
|
439
|
+
pendingRevisionRef.current = undefined
|
|
440
|
+
if (pending === undefined) return
|
|
441
|
+
if (isOwnDocumentEcho(echoedRevisionRef.current, pending)) return
|
|
442
|
+
refresh(pending, true)
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
useEffect(() => { reload(false, 'ensure') }, [reload])
|
|
446
|
+
useEffect(() => () => {
|
|
447
|
+
abortRef.current?.abort()
|
|
448
|
+
nextGeneration(generationRef)
|
|
449
|
+
}, [])
|
|
369
450
|
|
|
370
451
|
useEffect(() => {
|
|
371
452
|
if (props.subscribeInvalidate === undefined) return undefined
|
|
372
|
-
return props.subscribeInvalidate((
|
|
373
|
-
if (source === '
|
|
453
|
+
return props.subscribeInvalidate((event) => {
|
|
454
|
+
if (event.source === 'writable') {
|
|
455
|
+
const view = describe?.getSnapshot().view
|
|
456
|
+
if (view !== undefined) setWritable(view.writable)
|
|
457
|
+
return
|
|
458
|
+
}
|
|
459
|
+
if (event.source === 'settings') {
|
|
460
|
+
if (busyRouteRef.current !== null) {
|
|
461
|
+
pendingRevisionRef.current = event.revision
|
|
462
|
+
return
|
|
463
|
+
}
|
|
464
|
+
if (isOwnDocumentEcho(echoedRevisionRef.current, event.revision)) return
|
|
465
|
+
refreshAtRevision(event.revision, true)
|
|
466
|
+
return
|
|
467
|
+
}
|
|
468
|
+
if (event.source === 'directory') reload(true, snapshotMode())
|
|
469
|
+
if (event.source === 'reset') reload(true, 'ensure')
|
|
374
470
|
})
|
|
375
|
-
}, [props.subscribeInvalidate, reload])
|
|
471
|
+
}, [describe, props.subscribeInvalidate, refreshAtRevision, reload])
|
|
376
472
|
|
|
377
473
|
const patchNotice = (provider: string, notice: CardNotice | undefined): void => {
|
|
378
474
|
setNotices(current => {
|
|
@@ -385,7 +481,10 @@ export function EffortDeclareSection(props: EffortDeclareSectionProps): ReactNod
|
|
|
385
481
|
|
|
386
482
|
const save = async (draft: RouteDraft): Promise<void> => {
|
|
387
483
|
if (api === undefined || describe === undefined || schema === undefined) return
|
|
388
|
-
if (status === 'loading' ||
|
|
484
|
+
if (status === 'loading' || busyRouteRef.current !== null) {
|
|
485
|
+
patchNotice(draft.provider, { kind: 'error', text: t('saveBusy') })
|
|
486
|
+
return
|
|
487
|
+
}
|
|
389
488
|
const blocking = draft.models
|
|
390
489
|
.map(row => errorText(modelEffortError(row), t))
|
|
391
490
|
.find(text => text !== undefined)
|
|
@@ -393,6 +492,7 @@ export function EffortDeclareSection(props: EffortDeclareSectionProps): ReactNod
|
|
|
393
492
|
patchNotice(draft.provider, { kind: 'error', text: blocking })
|
|
394
493
|
return
|
|
395
494
|
}
|
|
495
|
+
busyRouteRef.current = draft.provider
|
|
396
496
|
setBusyRoute(draft.provider)
|
|
397
497
|
patchNotice(draft.provider, undefined)
|
|
398
498
|
try {
|
|
@@ -404,7 +504,7 @@ export function EffortDeclareSection(props: EffortDeclareSectionProps): ReactNod
|
|
|
404
504
|
afterCompat: draft.compat,
|
|
405
505
|
})
|
|
406
506
|
if (ops.length === 0) {
|
|
407
|
-
|
|
507
|
+
applyDrafts(current => current.map(row => row.provider === draft.provider ? alignDraft(row) : row))
|
|
408
508
|
return
|
|
409
509
|
}
|
|
410
510
|
const willWriteCompat = ops.some(op => (
|
|
@@ -444,12 +544,21 @@ export function EffortDeclareSection(props: EffortDeclareSectionProps): ReactNod
|
|
|
444
544
|
kind: conflict ? 'conflict' : 'error',
|
|
445
545
|
text: conflict ? t('conflict') : response.result.error.message,
|
|
446
546
|
})
|
|
447
|
-
if (conflict)
|
|
547
|
+
if (conflict) {
|
|
548
|
+
const { generation, signal } = beginGeneration()
|
|
549
|
+
void waitForNamespaceRevisionChange(describe, LLM_PI_AI_NS, draft.revision, signal).then((outcome) => {
|
|
550
|
+
if (outcome === 'aborted' || !generationIsCurrent(generationRef, generation)) return
|
|
551
|
+
return loadSnapshotThenSettle(generation, true)
|
|
552
|
+
}, (failure: unknown) => {
|
|
553
|
+
failGeneration(generation, failure)
|
|
554
|
+
})
|
|
555
|
+
}
|
|
448
556
|
return
|
|
449
557
|
}
|
|
450
558
|
const view = response.result.value
|
|
559
|
+
echoedRevisionRef.current = view.revision
|
|
451
560
|
describe.acceptView(view)
|
|
452
|
-
|
|
561
|
+
applyDrafts(applySaveSuccess(draftsRef.current, draft.provider, {
|
|
453
562
|
user: view.user ?? {},
|
|
454
563
|
revision: view.revision,
|
|
455
564
|
}))
|
|
@@ -460,7 +569,9 @@ export function EffortDeclareSection(props: EffortDeclareSectionProps): ReactNod
|
|
|
460
569
|
text: failure instanceof Error ? failure.message : t('loadError'),
|
|
461
570
|
})
|
|
462
571
|
} finally {
|
|
572
|
+
busyRouteRef.current = null
|
|
463
573
|
setBusyRoute(null)
|
|
574
|
+
flushPendingSettings(refreshAtRevision)
|
|
464
575
|
}
|
|
465
576
|
}
|
|
466
577
|
|
|
@@ -481,7 +592,7 @@ export function EffortDeclareSection(props: EffortDeclareSectionProps): ReactNod
|
|
|
481
592
|
{status === 'error' ? <p className={css.error}>{error}</p> : null}
|
|
482
593
|
{showReload
|
|
483
594
|
? (
|
|
484
|
-
<button type="button" className={css.secondaryButton} onClick={() => { reload(true) }}>{t('reload')}</button>
|
|
595
|
+
<button type="button" className={css.secondaryButton} onClick={() => { reload(true, snapshotMode()) }}>{t('reload')}</button>
|
|
485
596
|
)
|
|
486
597
|
: null}
|
|
487
598
|
{showEmpty
|
|
@@ -502,17 +613,17 @@ export function EffortDeclareSection(props: EffortDeclareSectionProps): ReactNod
|
|
|
502
613
|
formats={formats.length > 0 ? formats : []}
|
|
503
614
|
writable={writable}
|
|
504
615
|
busy={busyRoute === draft.provider}
|
|
505
|
-
|
|
616
|
+
saveLocked={!writable || busyRoute !== null || status === 'loading'}
|
|
506
617
|
notice={notices[draft.provider]}
|
|
507
618
|
t={t}
|
|
508
619
|
onChange={(next) => {
|
|
509
620
|
patchNotice(next.provider, undefined)
|
|
510
|
-
|
|
621
|
+
applyDrafts(current => current.map(row => row.provider === next.provider ? next : row))
|
|
511
622
|
}}
|
|
512
623
|
onSave={(next) => { void save(next) }}
|
|
513
624
|
onCancel={(next) => {
|
|
514
625
|
patchNotice(next.provider, undefined)
|
|
515
|
-
|
|
626
|
+
applyDrafts(current => current.map(row => row.provider === next.provider
|
|
516
627
|
? {
|
|
517
628
|
...row,
|
|
518
629
|
models: cloneModels(row.originalModels),
|
|
@@ -525,6 +636,7 @@ export function EffortDeclareSection(props: EffortDeclareSectionProps): ReactNod
|
|
|
525
636
|
</ul>
|
|
526
637
|
)
|
|
527
638
|
: null}
|
|
639
|
+
<p className={css.footer}>{PLUGIN_FOOTER_TEXT}</p>
|
|
528
640
|
</div>
|
|
529
641
|
)
|
|
530
642
|
}
|
package/src/client/README.en.md
CHANGED
|
@@ -17,9 +17,11 @@ Cross-plugin work uses Cordis services only (`connection`, `settingsScope`, `set
|
|
|
17
17
|
|
|
18
18
|
| File | Role |
|
|
19
19
|
| --- | --- |
|
|
20
|
-
| [`index.ts`](./index.ts) | Registers zh/en copy, CSS (`ctx.effect` insert/remove), and `settings.section` (id `effort-declare`, order 12).
|
|
21
|
-
| [`EffortDeclareSection.tsx`](./EffortDeclareSection.tsx) | Route cards, presets, Off tri-state, advanced protocol switches, save and cancel. |
|
|
22
|
-
| [`
|
|
20
|
+
| [`index.ts`](./index.ts) | Registers zh/en copy, CSS (`ctx.effect` insert/remove), and `settings.section` (id `effort-declare`, order 12). `describe.subscribe` only syncs `writable` (it fires for every namespace, including this page’s own `acceptView`). `settings/document-updated` (`llm-pi-ai` only) carries the Host revision; the section skips its own mutate echo, waits until the mirror revision has **caught up**, then `getSnapshot()` — it does not use `ensure()` as refresh. Provider directory changes come from `llm/adapters-updated`; `connection/reset` uses `ensure()` so an in-flight official `load()` is awaited. `locale: NS` lets the framework inject `t`; `inject` only returns `api` / `describe` / `schema` / `subscribeInvalidate`. |
|
|
21
|
+
| [`EffortDeclareSection.tsx`](./EffortDeclareSection.tsx) | Route cards, presets, Off tri-state, advanced protocol switches, save and cancel. Save is exclusive for the whole `llm-pi-ai` namespace (ref-guarded against double-click); `busy` is “Saving…” on the in-flight card, `saveLocked` disables the others. A successful write syncs `draftsRef` immediately and folds `acceptView` + `applySaveSuccess`. External `document-updated` events that arrive during save are replayed afterwards. A reload with no conflict clears that card’s conflict/error notice. Footer shows the version and copyright years frozen at pack time. |
|
|
22
|
+
| [`build-info.ts`](./build-info.ts) | Reads `package.json` version and UTC pack year injected by tsdown `define`, and formats the footer line. |
|
|
23
|
+
| [`globals.d.ts`](./globals.d.ts) | Types for `__PLUGIN_VERSION__` and `__COPYRIGHT_TO__`. |
|
|
24
|
+
| [`load-drafts.ts`](./load-drafts.ts) | Builds drafts from `llm.providers` + the settings mirror; drafts come from `user`, protocol classification may use `value`. `thinkingFormat` choices come only from the live schema union. First paint uses `ensure()`; refresh reads `getSnapshot()` only, with helpers that wait for the namespace revision. |
|
|
23
25
|
| [`locales.ts`](./locales.ts) | Copy namespace `plugin-effort-declare`. |
|
|
24
26
|
| [`effort-declare.module.css`](./effort-declare.module.css) | `--dsw-alias-*` tokens only, so dark theme stays correct. |
|
|
25
27
|
| [`schema-ops.ts`](./schema-ops.ts) | Binds `settingsSchema` as plain callbacks (including `validate`) so the service identity is not passed into React. |
|
package/src/client/README.md
CHANGED
|
@@ -17,9 +17,11 @@
|
|
|
17
17
|
|
|
18
18
|
| 文件 | 说明 |
|
|
19
19
|
| --- | --- |
|
|
20
|
-
| [`index.ts`](./index.ts) | 注册 zh/en 文案、CSS(`ctx.effect` 插入/移除)与 `settings.section`(id `effort-declare`,order 12
|
|
21
|
-
| [`EffortDeclareSection.tsx`](./EffortDeclareSection.tsx) | 路由卡片、预设、Off
|
|
22
|
-
| [`
|
|
20
|
+
| [`index.ts`](./index.ts) | 注册 zh/en 文案、CSS(`ctx.effect` 插入/移除)与 `settings.section`(id `effort-declare`,order 12)。`describe.subscribe` 只同步 `writable`(任意 namespace 都会触发,含自己的 `acceptView`)。`settings/document-updated`(仅 `llm-pi-ai`)带上 Host 的 revision;设置页跳过自己这次 mutate 的回声,等镜像 revision **追上**后再 `getSnapshot()`,不把 `ensure()` 当刷新。提供方目录来自 `llm/adapters-updated`;`connection/reset` 走 `ensure()` 以便等待官方镜像的 in-flight `load()`。`locale: NS` 由框架注入 `t`,inject 只传 `api` / `describe` / `schema` / `subscribeInvalidate`。 |
|
|
21
|
+
| [`EffortDeclareSection.tsx`](./EffortDeclareSection.tsx) | 路由卡片、预设、Off 三态、高级协议开关、保存与取消。保存互斥到整个 `llm-pi-ai` namespace(以 ref 防连点);`busy` 只给正在 mutate 的卡显示「保存中」,其它卡 `saveLocked`。自己写入成功立刻同步 `draftsRef` 并走 `acceptView` + `applySaveSuccess`。保存过程中到达的外部 `document-updated` 会在结束后补刷。无冲突的 reload 清掉该卡 conflict/error notice。页脚显示打包时冻结的版本与版权年。 |
|
|
22
|
+
| [`build-info.ts`](./build-info.ts) | 读取 tsdown `define` 注入的 `package.json` version 与 UTC 打包年,拼出页脚文案。 |
|
|
23
|
+
| [`globals.d.ts`](./globals.d.ts) | `__PLUGIN_VERSION__` / `__COPYRIGHT_TO__` 的类型声明。 |
|
|
24
|
+
| [`load-drafts.ts`](./load-drafts.ts) | 从 `llm.providers` + 镜像组装草稿;草稿取自 `user`,协议分类可用 `value`。`thinkingFormat` 可选项只来自现场 schema union。首屏 `ensure()`;刷新只读 `getSnapshot()`,并提供等 namespace revision 的 helper。 |
|
|
23
25
|
| [`locales.ts`](./locales.ts) | 文案 namespace `plugin-effort-declare`。 |
|
|
24
26
|
| [`effort-declare.module.css`](./effort-declare.module.css) | 仅使用 `--dsw-alias-*` 设计令牌,保证暗色主题正确。 |
|
|
25
27
|
| [`schema-ops.ts`](./schema-ops.ts) | 将 `settingsSchema` 收成普通回调(含 `validate`),避免把服务身份带进 React。 |
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Footer line frozen into the client bundle. Do not read the clock or
|
|
3
|
+
* package.json at DSH startup — host apply is empty and the settings page
|
|
4
|
+
* runs in the browser.
|
|
5
|
+
*/
|
|
6
|
+
import { COPYRIGHT_FROM, formatAttribution } from '../core/attribution.ts'
|
|
7
|
+
|
|
8
|
+
export const PLUGIN_FOOTER_TEXT = formatAttribution(
|
|
9
|
+
__PLUGIN_VERSION__,
|
|
10
|
+
COPYRIGHT_FROM,
|
|
11
|
+
__COPYRIGHT_TO__,
|
|
12
|
+
)
|
package/src/client/index.ts
CHANGED
|
@@ -8,7 +8,7 @@ import type {} from '@deepseek-ai/dsh-client-ui-settings/client'
|
|
|
8
8
|
import type {} from '@deepseek-ai/dsh-client-locale/client'
|
|
9
9
|
import type {} from '@deepseek-ai/dsh-api-remotes/client'
|
|
10
10
|
import { EffortDeclareSection } from './EffortDeclareSection.tsx'
|
|
11
|
-
import type { EffortDeclareSectionInjected,
|
|
11
|
+
import type { EffortDeclareSectionInjected, Invalidation } from './EffortDeclareSection.tsx'
|
|
12
12
|
import { bindSchema } from './schema-ops.ts'
|
|
13
13
|
import { LLM_PI_AI_NS } from '../core/catalog.ts'
|
|
14
14
|
import { NS, en, zh, type EffortDeclareKey } from './locales.ts'
|
|
@@ -60,20 +60,25 @@ export function apply(ctx: ClientContext): void {
|
|
|
60
60
|
})
|
|
61
61
|
const t = ctx.locale.bind(NS)
|
|
62
62
|
const describe = ctx.settingsScope.describe()
|
|
63
|
-
const invalidation = new Set<(
|
|
63
|
+
const invalidation = new Set<(event: Invalidation) => void>()
|
|
64
64
|
|
|
65
65
|
ctx.effect(() => {
|
|
66
|
-
const emit = (
|
|
67
|
-
for (const listener of invalidation) listener(
|
|
66
|
+
const emit = (event: Invalidation) => {
|
|
67
|
+
for (const listener of invalidation) listener(event)
|
|
68
68
|
}
|
|
69
|
+
// Mirror subscribe fires on every namespace (theme, locale, own acceptView).
|
|
70
|
+
// Only fold writable from that. llm-pi-ai document changes come from
|
|
71
|
+
// settings/document-updated; the section waits until the mirror revision
|
|
72
|
+
// has caught up instead of calling ensure(), and skips the echo of its
|
|
73
|
+
// own mutate. connection/reset uses ensure() so an in-flight load is awaited.
|
|
69
74
|
const disposers = [
|
|
70
|
-
describe.subscribe(() => { emit('
|
|
71
|
-
ctx.remote.$on('settings/document-updated', (ns: string) => {
|
|
75
|
+
describe.subscribe(() => { emit({ source: 'writable' }) }),
|
|
76
|
+
ctx.remote.$on('settings/document-updated', (ns: string, revision: number) => {
|
|
72
77
|
if (ns !== LLM_PI_AI_NS) return
|
|
73
|
-
emit('settings')
|
|
78
|
+
emit({ source: 'settings', revision })
|
|
74
79
|
}),
|
|
75
|
-
ctx.remote.$on('llm/adapters-updated', () => { emit('directory') }),
|
|
76
|
-
ctx.on('connection/reset', () => { emit('
|
|
80
|
+
ctx.remote.$on('llm/adapters-updated', () => { emit({ source: 'directory' }) }),
|
|
81
|
+
ctx.on('connection/reset', () => { emit({ source: 'reset' }) }),
|
|
77
82
|
]
|
|
78
83
|
return () => {
|
|
79
84
|
for (const dispose of disposers) dispose()
|