dsh-claude-desktop-theme 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -51
- package/lib/client.js +2342 -270
- package/lib/index.js +387 -20
- package/lib/types/client/compat/adapter.d.ts +22 -1
- package/lib/types/client/compat/composer-menus.d.ts +6 -3
- package/lib/types/client/compat/context-panel.d.ts +47 -0
- package/lib/types/client/compat/empty-groups.d.ts +46 -0
- package/lib/types/client/compat/host-dom.d.ts +64 -0
- package/lib/types/client/compat/permission-menu.d.ts +88 -0
- package/lib/types/client/compat/sidebar-navigation.d.ts +11 -0
- package/lib/types/client/compat/stats-values.d.ts +18 -1
- package/lib/types/client/compat/view-options.d.ts +58 -0
- package/lib/types/client/contracts/ports.d.ts +147 -0
- package/lib/types/client/features/context-panel/ContextPanel.d.ts +12 -0
- package/lib/types/client/features/context-panel/mount.d.ts +20 -0
- package/lib/types/client/features/settings/locales.d.ts +7 -0
- package/lib/types/client/features/sidebar/navigation-icons.d.ts +6 -0
- package/lib/types/client/features/view-options/ViewOptionsCard.d.ts +30 -0
- package/lib/types/client/features/view-options/mount.d.ts +25 -0
- package/lib/types/host/context/compaction.d.ts +26 -0
- package/lib/types/host/context/pricing.d.ts +99 -0
- package/lib/types/host/context/service.d.ts +24 -0
- package/lib/types/host/context/unit.d.ts +136 -0
- package/lib/types/host/index.d.ts +18 -0
- package/lib/types/shared/config.d.ts +26 -1
- package/lib/types/shared/context.d.ts +243 -0
- package/package.json +6 -3
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/** One content block, as far as pricing reads it. */
|
|
2
|
+
export interface PricedBlock {
|
|
3
|
+
readonly type?: string;
|
|
4
|
+
readonly text?: string;
|
|
5
|
+
readonly name?: string;
|
|
6
|
+
readonly arguments?: string;
|
|
7
|
+
/** Every other field the block carries; pricing only ever serializes them. */
|
|
8
|
+
readonly [field: string]: unknown;
|
|
9
|
+
}
|
|
10
|
+
/** One model-visible message, as far as pricing reads it. */
|
|
11
|
+
export interface PricedMessage {
|
|
12
|
+
/** Absent for a `user/message` payload, which *is* the message. */
|
|
13
|
+
readonly role?: string;
|
|
14
|
+
readonly content: readonly PricedBlock[];
|
|
15
|
+
}
|
|
16
|
+
/** One tool schema, as far as pricing reads it. */
|
|
17
|
+
export interface PricedTool {
|
|
18
|
+
readonly name: string;
|
|
19
|
+
}
|
|
20
|
+
/** The structural slice of one session event this fold reads. */
|
|
21
|
+
export interface PricedEvent {
|
|
22
|
+
readonly type: string;
|
|
23
|
+
readonly seq: number;
|
|
24
|
+
readonly surfaceOp?: 'append' | {
|
|
25
|
+
readonly startSeq: number;
|
|
26
|
+
readonly endSeq: number;
|
|
27
|
+
};
|
|
28
|
+
readonly data?: {
|
|
29
|
+
readonly content?: readonly PricedBlock[];
|
|
30
|
+
readonly message?: PricedMessage;
|
|
31
|
+
readonly source?: {
|
|
32
|
+
readonly kind?: string;
|
|
33
|
+
readonly changes?: readonly {
|
|
34
|
+
readonly action?: string;
|
|
35
|
+
readonly path?: string;
|
|
36
|
+
}[];
|
|
37
|
+
readonly entries?: readonly {
|
|
38
|
+
readonly name?: string;
|
|
39
|
+
readonly description?: string;
|
|
40
|
+
}[];
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Price one content block list.
|
|
46
|
+
*
|
|
47
|
+
* @param blocks - the message's content.
|
|
48
|
+
* @returns heuristic tokens including per-block structural overhead.
|
|
49
|
+
*/
|
|
50
|
+
export declare function priceContent(blocks: readonly PricedBlock[]): number;
|
|
51
|
+
/**
|
|
52
|
+
* Price one model-visible message.
|
|
53
|
+
*
|
|
54
|
+
* A system prompt is serialized as a plain string by every adapter, so the host
|
|
55
|
+
* prices it as text density plus role framing with no per-block overhead; every
|
|
56
|
+
* other role takes the per-block arm.
|
|
57
|
+
*
|
|
58
|
+
* @param message - the derived message, or null when the event produces none.
|
|
59
|
+
* @returns heuristic tokens.
|
|
60
|
+
*/
|
|
61
|
+
export declare function priceMessage(message: PricedMessage | null): number;
|
|
62
|
+
/**
|
|
63
|
+
* The message one surface event projects to, or null when it produces none.
|
|
64
|
+
*
|
|
65
|
+
* @param event - one committed session event.
|
|
66
|
+
* @returns the message to price.
|
|
67
|
+
*/
|
|
68
|
+
export declare function derivedMessage(event: PricedEvent): PricedMessage | null;
|
|
69
|
+
/** Whether an event carries a surface placement and therefore joins the fold. */
|
|
70
|
+
export declare function isSurfaceEvent(event: PricedEvent): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Price one tool schema on its own.
|
|
73
|
+
*
|
|
74
|
+
* The host prices the assembled array whole, so these per-tool numbers never
|
|
75
|
+
* add back up to it; {@link splitTools} is what keeps the two group rows
|
|
76
|
+
* summing to the host's own total.
|
|
77
|
+
*
|
|
78
|
+
* @param tool - one entry of `EpochHeader.tools`.
|
|
79
|
+
* @returns heuristic tokens for that entry alone.
|
|
80
|
+
*/
|
|
81
|
+
export declare function priceTool(tool: unknown): number;
|
|
82
|
+
/**
|
|
83
|
+
* Price the assembled tool array exactly as the host does.
|
|
84
|
+
*
|
|
85
|
+
* @param tools - `EpochHeader.tools`.
|
|
86
|
+
* @returns the array's heuristic price; 0 when there are no tools.
|
|
87
|
+
*/
|
|
88
|
+
export declare function priceTools(tools: readonly PricedTool[] | undefined): number;
|
|
89
|
+
/**
|
|
90
|
+
* Split one token total across weighted entries, preserving the total exactly.
|
|
91
|
+
*
|
|
92
|
+
* The last entry takes whatever rounding left over, so the rows a group draws
|
|
93
|
+
* always add back up to the group's own number.
|
|
94
|
+
*
|
|
95
|
+
* @param total - tokens to distribute.
|
|
96
|
+
* @param weights - one weight per entry; non-positive weights fall back to 1.
|
|
97
|
+
* @returns one token count per entry, summing to `total`.
|
|
98
|
+
*/
|
|
99
|
+
export declare function allocate(total: number, weights: readonly number[]): readonly number[];
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mount the context panel's projection unit.
|
|
3
|
+
*
|
|
4
|
+
* The unit is the only thing the host half adds: the browser half reads the
|
|
5
|
+
* host's own `contextPressure` / `contextBreakdown` straight off the session
|
|
6
|
+
* list, so there is no route, no polling and no second state store — one
|
|
7
|
+
* registration on the session-projection seam, riding this plugin's fiber like
|
|
8
|
+
* `stats/unit.ts` does.
|
|
9
|
+
*
|
|
10
|
+
* A plugin's `apply` runs as soon as its own fiber starts, which can be before
|
|
11
|
+
* the projection registry exists: the optional injection below waits for it,
|
|
12
|
+
* and a deployment that mounts no registry simply never gets the panel's
|
|
13
|
+
* per-tool rows — the card falls back to the host's three numbers alone.
|
|
14
|
+
*
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
18
|
+
/**
|
|
19
|
+
* Register the `ccdContext` unit, once the projection registry exists.
|
|
20
|
+
*
|
|
21
|
+
* @param ctx - host plugin context.
|
|
22
|
+
* @param isEnabled - current plugin switch, read at mount time.
|
|
23
|
+
*/
|
|
24
|
+
export declare function mountContextBreakdown(ctx: Context, isEnabled: () => boolean): void;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure `ccdContext` fold over committed session events.
|
|
3
|
+
*
|
|
4
|
+
* The host already publishes `contextBreakdown` — system prompt, tools,
|
|
5
|
+
* conversation — and `contextPressure`. Neither says *which* tool, *which*
|
|
6
|
+
* instruction file or *which* skill holds the tokens, and nothing outside the
|
|
7
|
+
* token meter can answer that from a wire value. So this unit mirrors the
|
|
8
|
+
* meter's own surface fold: it prices every message-producing event with the
|
|
9
|
+
* same heuristic, honours `surfaceOp` replacements exactly as
|
|
10
|
+
* `contextBreakdown` does, and keeps the priced surface so the two classified
|
|
11
|
+
* kinds can be summed at read time.
|
|
12
|
+
*
|
|
13
|
+
* The mirror is exact rather than approximate — against a real 19 MB session
|
|
14
|
+
* log it reproduces the host's persisted `{systemTokens: 1953, toolsTokens:
|
|
15
|
+
* 7492, messageTokens: 93461}` to the token — and `tests/context-pricing.test.ts`
|
|
16
|
+
* pins that. What it deliberately does *not* do is restate the whole
|
|
17
|
+
* breakdown: the system prompt and the conversation are the host's numbers, and
|
|
18
|
+
* this unit only re-derives the two shares that come out of `messageTokens`.
|
|
19
|
+
*
|
|
20
|
+
* @module
|
|
21
|
+
*/
|
|
22
|
+
import type { ProjectionDefinition } from '@deepseek-ai/dsh-session-projection';
|
|
23
|
+
import { CONTEXT_KEY, type ContextBreakdownView, type ContextEntry } from '../../shared/context.ts';
|
|
24
|
+
import { type PricedEvent } from './pricing.ts';
|
|
25
|
+
declare module '@deepseek-ai/dsh-session-projection/types' {
|
|
26
|
+
interface SessionProjectionStateMap {
|
|
27
|
+
ccdContext: ContextState;
|
|
28
|
+
}
|
|
29
|
+
interface SessionProjectionMap {
|
|
30
|
+
ccdContext: ContextBreakdownView;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** The autocompaction policy this deployment runs, as the loader declares it. */
|
|
34
|
+
export interface CompactionPolicy {
|
|
35
|
+
/** `thresholdRatio`; the window share a compaction may fill. */
|
|
36
|
+
readonly ratio: number;
|
|
37
|
+
/** `headroomTokens`; room kept above the summary budget. */
|
|
38
|
+
readonly headroom: number;
|
|
39
|
+
}
|
|
40
|
+
/** Node kinds the fold classifies; everything else is `OTHER`. */
|
|
41
|
+
export declare const KIND_OTHER = 0;
|
|
42
|
+
/** A retained `agent-instructions` message. */
|
|
43
|
+
export declare const KIND_INSTRUCTIONS = 1;
|
|
44
|
+
/** A retained `skill-catalog` message. */
|
|
45
|
+
export declare const KIND_SKILLS = 2;
|
|
46
|
+
/**
|
|
47
|
+
* One retained surface node: `[seq, tokens, kind, entries]`.
|
|
48
|
+
*
|
|
49
|
+
* `entries` is empty for anything but the two classified kinds; for those it is
|
|
50
|
+
* the node's own price already split across the files or skills it names, so
|
|
51
|
+
* summing a kind's entries is summing its nodes.
|
|
52
|
+
*/
|
|
53
|
+
export type ContextNode = readonly [
|
|
54
|
+
seq: number,
|
|
55
|
+
tokens: number,
|
|
56
|
+
kind: number,
|
|
57
|
+
entries: readonly ContextEntry[]
|
|
58
|
+
];
|
|
59
|
+
/** Fold state: plain JSON, one value per session. */
|
|
60
|
+
export interface ContextState {
|
|
61
|
+
/** Every retained surface node, in model-visible order. */
|
|
62
|
+
readonly nodes: readonly ContextNode[];
|
|
63
|
+
/** Per-tool prices from the latest request envelope, largest first. */
|
|
64
|
+
readonly tools: readonly ContextEntry[];
|
|
65
|
+
/** `request/context` capacity, 0 until one is logged. */
|
|
66
|
+
readonly window: number;
|
|
67
|
+
/** The envelope's own output cap, or null when it declares none. */
|
|
68
|
+
readonly reserved: number | null;
|
|
69
|
+
}
|
|
70
|
+
/** Empty state for one session. */
|
|
71
|
+
export declare function initContextState(): ContextState;
|
|
72
|
+
/**
|
|
73
|
+
* One instruction message's price, split across the files it names.
|
|
74
|
+
*
|
|
75
|
+
* The paths come from `source.changes` — the structured record of what that
|
|
76
|
+
* message carries — and the split weights come from each file's own section, so
|
|
77
|
+
* a message carrying two files of very different size does not report them as
|
|
78
|
+
* equal. A path whose section cannot be located takes an even weight.
|
|
79
|
+
*
|
|
80
|
+
* @param event - the retained `agent-instructions` message.
|
|
81
|
+
* @param tokens - the message's own heuristic price.
|
|
82
|
+
* @returns one row per named file, summing to `tokens`.
|
|
83
|
+
*/
|
|
84
|
+
export declare function instructionEntries(event: PricedEvent, tokens: number): readonly ContextEntry[];
|
|
85
|
+
/**
|
|
86
|
+
* One skill catalog's price, split across the entries it lists.
|
|
87
|
+
*
|
|
88
|
+
* The catalog's structured `entries` are the names; their descriptions are the
|
|
89
|
+
* only per-entry size the message carries, so they are what the split weighs.
|
|
90
|
+
*
|
|
91
|
+
* @param event - the retained `skill-catalog` message.
|
|
92
|
+
* @param tokens - the message's own heuristic price.
|
|
93
|
+
* @returns one row per listed skill, summing to `tokens`.
|
|
94
|
+
*/
|
|
95
|
+
export declare function skillEntries(event: PricedEvent, tokens: number): readonly ContextEntry[];
|
|
96
|
+
/**
|
|
97
|
+
* Advance the fold by one committed event.
|
|
98
|
+
*
|
|
99
|
+
* @param state - state covering every earlier event.
|
|
100
|
+
* @param event - the next committed session event.
|
|
101
|
+
* @returns the next state, or the same reference when the event is not ours.
|
|
102
|
+
*/
|
|
103
|
+
export declare function applyContextEvent(state: ContextState, event: PricedEvent): ContextState;
|
|
104
|
+
/**
|
|
105
|
+
* Project one state onto the client-visible value.
|
|
106
|
+
*
|
|
107
|
+
* @param state - current fold state.
|
|
108
|
+
* @param policy - the deployment's autocompaction policy, or null when none runs.
|
|
109
|
+
* @returns the whole current value.
|
|
110
|
+
*/
|
|
111
|
+
export declare function viewContext(state: ContextState, policy: CompactionPolicy | null): ContextBreakdownView;
|
|
112
|
+
/** Read one persisted state row; lenient, because a read must never throw. */
|
|
113
|
+
export declare function readContextState(value: unknown): ContextState;
|
|
114
|
+
/** Read one wire view row, leniently — the schema slot never throws. */
|
|
115
|
+
export declare function readContextView(value: unknown): ContextBreakdownView;
|
|
116
|
+
/**
|
|
117
|
+
* The registry's wire-unit shape: the definition with its optional `wire`
|
|
118
|
+
* field made required, which is the overload a client-visible key registers
|
|
119
|
+
* through.
|
|
120
|
+
*/
|
|
121
|
+
type WireUnit = Omit<ProjectionDefinition<typeof CONTEXT_KEY>, 'wire'> & {
|
|
122
|
+
wire: NonNullable<ProjectionDefinition<typeof CONTEXT_KEY>['wire']>;
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* Build the unit this plugin registers on `ctx.sessionProjections`.
|
|
126
|
+
*
|
|
127
|
+
* The policy is read through a callback rather than captured once: the unit is
|
|
128
|
+
* registered while the plugin is on, and a settings write that enables or
|
|
129
|
+
* reconfigures compaction lands in the loader's entry tree without remounting
|
|
130
|
+
* this plugin.
|
|
131
|
+
*
|
|
132
|
+
* @param policy - reads the deployment's autocompaction policy, or null.
|
|
133
|
+
* @returns the projection unit.
|
|
134
|
+
*/
|
|
135
|
+
export declare function createContextProjection(policy: () => CompactionPolicy | null): WireUnit;
|
|
136
|
+
export {};
|
|
@@ -24,6 +24,8 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
|
24
24
|
'tool-calls': z<boolean, boolean, "defined">;
|
|
25
25
|
statistics: z<boolean, boolean, "defined">;
|
|
26
26
|
'composer-pet': z<boolean, boolean, "defined">;
|
|
27
|
+
'composer-stats': z<boolean, boolean, "defined">;
|
|
28
|
+
'context-panel': z<boolean, boolean, "defined">;
|
|
27
29
|
}>>>, NoInfer<Schemastery.ObjectT<NoInfer<{
|
|
28
30
|
shell: z<boolean, boolean, "defined">;
|
|
29
31
|
sidebar: z<boolean, boolean, "defined">;
|
|
@@ -32,6 +34,8 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
|
32
34
|
'tool-calls': z<boolean, boolean, "defined">;
|
|
33
35
|
statistics: z<boolean, boolean, "defined">;
|
|
34
36
|
'composer-pet': z<boolean, boolean, "defined">;
|
|
37
|
+
'composer-stats': z<boolean, boolean, "defined">;
|
|
38
|
+
'context-panel': z<boolean, boolean, "defined">;
|
|
35
39
|
}>>>, "volatile-defined">;
|
|
36
40
|
appearance: z<NoInfer<Schemastery.ObjectS<NoInfer<{
|
|
37
41
|
canvas: z<string, string, "defined">;
|
|
@@ -49,6 +53,11 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
|
49
53
|
uiCjk: z<string, string, "defined">;
|
|
50
54
|
code: z<string, string, "defined">;
|
|
51
55
|
}>>>, "volatile-defined">;
|
|
56
|
+
view: z<NoInfer<Schemastery.ObjectS<NoInfer<{
|
|
57
|
+
showEmptyGroups: z<boolean, boolean, "defined">;
|
|
58
|
+
}>>>, NoInfer<Schemastery.ObjectT<NoInfer<{
|
|
59
|
+
showEmptyGroups: z<boolean, boolean, "defined">;
|
|
60
|
+
}>>>, "volatile-defined">;
|
|
52
61
|
}>>, Schemastery.ObjectT<NoInfer<{
|
|
53
62
|
enabled: z<boolean, boolean, "volatile-defined">;
|
|
54
63
|
debug: z<boolean, boolean, "volatile-defined">;
|
|
@@ -60,6 +69,8 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
|
60
69
|
'tool-calls': z<boolean, boolean, "defined">;
|
|
61
70
|
statistics: z<boolean, boolean, "defined">;
|
|
62
71
|
'composer-pet': z<boolean, boolean, "defined">;
|
|
72
|
+
'composer-stats': z<boolean, boolean, "defined">;
|
|
73
|
+
'context-panel': z<boolean, boolean, "defined">;
|
|
63
74
|
}>>>, NoInfer<Schemastery.ObjectT<NoInfer<{
|
|
64
75
|
shell: z<boolean, boolean, "defined">;
|
|
65
76
|
sidebar: z<boolean, boolean, "defined">;
|
|
@@ -68,6 +79,8 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
|
68
79
|
'tool-calls': z<boolean, boolean, "defined">;
|
|
69
80
|
statistics: z<boolean, boolean, "defined">;
|
|
70
81
|
'composer-pet': z<boolean, boolean, "defined">;
|
|
82
|
+
'composer-stats': z<boolean, boolean, "defined">;
|
|
83
|
+
'context-panel': z<boolean, boolean, "defined">;
|
|
71
84
|
}>>>, "volatile-defined">;
|
|
72
85
|
appearance: z<NoInfer<Schemastery.ObjectS<NoInfer<{
|
|
73
86
|
canvas: z<string, string, "defined">;
|
|
@@ -85,6 +98,11 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
|
85
98
|
uiCjk: z<string, string, "defined">;
|
|
86
99
|
code: z<string, string, "defined">;
|
|
87
100
|
}>>>, "volatile-defined">;
|
|
101
|
+
view: z<NoInfer<Schemastery.ObjectS<NoInfer<{
|
|
102
|
+
showEmptyGroups: z<boolean, boolean, "defined">;
|
|
103
|
+
}>>>, NoInfer<Schemastery.ObjectT<NoInfer<{
|
|
104
|
+
showEmptyGroups: z<boolean, boolean, "defined">;
|
|
105
|
+
}>>>, "volatile-defined">;
|
|
88
106
|
}>>, "plain">;
|
|
89
107
|
/**
|
|
90
108
|
* Host half: the configuration surface the interface reads, plus the read-only
|
|
@@ -1,20 +1,35 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* The interface modules a configuration switch turns on.
|
|
3
|
+
*
|
|
4
|
+
* `composer-stats` is the one entry that mounts nothing of its own: it is the
|
|
5
|
+
* Composer status bar's statistics readouts — `compat/stats-values.ts` plus the
|
|
6
|
+
* Composer sheet — and it is off by default, so a fresh install shows the row
|
|
7
|
+
* without the host's readout cluster. Turning it on mirrors one short value per
|
|
8
|
+
* pill (the output rate, or the cache-hit share) in place of the host's own
|
|
9
|
+
* label.
|
|
10
|
+
*/
|
|
11
|
+
export declare const FEATURE_IDS: readonly ['shell', 'sidebar', 'new-session', 'conversation', 'tool-calls', 'statistics', 'composer-pet', 'composer-stats', 'context-panel'];
|
|
2
12
|
export type FeatureId = typeof FEATURE_IDS[number];
|
|
3
13
|
export type FeatureFlags = Readonly<Record<FeatureId, boolean>>;
|
|
4
14
|
/** Fields of the background-colour section; an empty string means "built-in palette". */
|
|
5
15
|
export declare const APPEARANCE_FIELDS: readonly ['canvas', 'sidebar'];
|
|
6
16
|
/** Fields of the typeface section; an empty string means "system stack". */
|
|
7
17
|
export declare const FONT_FIELDS: readonly ['uiLatin', 'uiCjk', 'code'];
|
|
18
|
+
/** Fields of the view-options section. */
|
|
19
|
+
export declare const VIEW_FIELDS: readonly ['showEmptyGroups'];
|
|
8
20
|
export type AppearanceField = typeof APPEARANCE_FIELDS[number];
|
|
9
21
|
export type FontField = typeof FONT_FIELDS[number];
|
|
22
|
+
export type ViewField = typeof VIEW_FIELDS[number];
|
|
10
23
|
export type AppearanceConfig = Readonly<Record<AppearanceField, string>>;
|
|
11
24
|
export type FontsConfig = Readonly<Record<FontField, string>>;
|
|
25
|
+
export type ViewConfig = Readonly<Record<ViewField, boolean>>;
|
|
12
26
|
export interface StyleConfig {
|
|
13
27
|
readonly enabled: boolean;
|
|
14
28
|
readonly debug: boolean;
|
|
15
29
|
readonly features: FeatureFlags;
|
|
16
30
|
readonly appearance: AppearanceConfig;
|
|
17
31
|
readonly fonts: FontsConfig;
|
|
32
|
+
readonly view: ViewConfig;
|
|
18
33
|
}
|
|
19
34
|
export interface StyleOptions {
|
|
20
35
|
readonly enabled?: boolean;
|
|
@@ -22,11 +37,21 @@ export interface StyleOptions {
|
|
|
22
37
|
readonly features?: Partial<FeatureFlags>;
|
|
23
38
|
readonly appearance?: Partial<Record<AppearanceField, unknown>>;
|
|
24
39
|
readonly fonts?: Partial<Record<FontField, unknown>>;
|
|
40
|
+
readonly view?: Partial<Record<ViewField, unknown>>;
|
|
25
41
|
}
|
|
26
42
|
export declare const DEFAULT_FEATURES: FeatureFlags;
|
|
27
43
|
/** Empty strings keep the built-in palette and the system font stack in charge. */
|
|
28
44
|
export declare const DEFAULT_APPEARANCE: AppearanceConfig;
|
|
29
45
|
export declare const DEFAULT_FONTS: FontsConfig;
|
|
46
|
+
/**
|
|
47
|
+
* The view-options section's defaults.
|
|
48
|
+
*
|
|
49
|
+
* `showEmptyGroups` is off, which means the sidebar hides a Workspace that has
|
|
50
|
+
* no Session under the current filter — the reference's own default for that
|
|
51
|
+
* switch, and the reason the row in the view-options card reads as "off" on a
|
|
52
|
+
* fresh install.
|
|
53
|
+
*/
|
|
54
|
+
export declare const DEFAULT_VIEW: ViewConfig;
|
|
30
55
|
/** Font names longer than this are a pasted paragraph, not a family. */
|
|
31
56
|
export declare const FONT_NAME_MAX_LENGTH = 64;
|
|
32
57
|
/**
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The context-breakdown contract both halves share.
|
|
3
|
+
*
|
|
4
|
+
* The host half re-derives what the host's own three-number breakdown cannot
|
|
5
|
+
* say — per-tool rows, per-file rows, per-skill rows, the autocompaction
|
|
6
|
+
* threshold — and ships it as the `ccdContext` projection; the browser half
|
|
7
|
+
* reads the host's authoritative `contextPressure` / `contextBreakdown` beside
|
|
8
|
+
* it and turns all of it into the panel's rows and bar with the pure functions
|
|
9
|
+
* below. Keeping the arithmetic here is what lets the card be a pure function
|
|
10
|
+
* of its inputs and lets the tests price a real session log without a browser.
|
|
11
|
+
*
|
|
12
|
+
* The pricing constants are the host's own (`@deepseek-ai/dsh-token-meter`
|
|
13
|
+
* `estimate.ts`): four characters per token, four tokens of structural overhead
|
|
14
|
+
* per content block and per message. They are restated rather than imported —
|
|
15
|
+
* that package is not part of this plugin's runtime baseline — and every number
|
|
16
|
+
* they produce is a re-derivation of a value the host also computed, never a
|
|
17
|
+
* second authority beside it.
|
|
18
|
+
*/
|
|
19
|
+
/** Projection key this plugin owns; it must not collide with a host key. */
|
|
20
|
+
export declare const CONTEXT_KEY = "ccdContext";
|
|
21
|
+
/** Persisted-cache invalidation version for the fold state. */
|
|
22
|
+
export declare const CONTEXT_STATE_VERSION = 1;
|
|
23
|
+
/** Characters per token in the host's density heuristic. */
|
|
24
|
+
export declare const CHARS_PER_TOKEN = 4;
|
|
25
|
+
/** Structural overhead the host adds per content block and per message. */
|
|
26
|
+
export declare const BLOCK_OVERHEAD = 4;
|
|
27
|
+
/** Default autocompaction threshold ratio (`compaction-basic`). */
|
|
28
|
+
export declare const DEFAULT_THRESHOLD_RATIO = 0.8;
|
|
29
|
+
/** Default autocompaction headroom, in tokens (`compaction-basic`). */
|
|
30
|
+
export declare const DEFAULT_HEADROOM_TOKENS = 65536;
|
|
31
|
+
/** The public-name prefix DSH gives every MCP tool. */
|
|
32
|
+
export declare const MCP_PREFIX = "mcp__";
|
|
33
|
+
/** Distinct entries one fold group keeps before it drops the smallest. */
|
|
34
|
+
export declare const CONTEXT_ENTRY_LIMIT = 200;
|
|
35
|
+
/** Detail rows a group renders before its list scrolls. */
|
|
36
|
+
export declare const DETAIL_ROW_LIMIT = 200;
|
|
37
|
+
/** The narrowest segment the bar draws; anything under this is left out. */
|
|
38
|
+
export declare const MIN_SEGMENT_PX = 2;
|
|
39
|
+
/** One priced row, as it travels on the wire: `[name, tokens]`. */
|
|
40
|
+
export type ContextEntry = readonly [name: string, tokens: number];
|
|
41
|
+
/** The `ccdContext` projection's client-visible value. */
|
|
42
|
+
export interface ContextBreakdownView {
|
|
43
|
+
/** Every assembled tool schema, largest first. */
|
|
44
|
+
readonly tools: readonly ContextEntry[];
|
|
45
|
+
/** Every retained instruction file, largest first. */
|
|
46
|
+
readonly files: readonly ContextEntry[];
|
|
47
|
+
/** Every retained skill-catalog entry, largest first. */
|
|
48
|
+
readonly skills: readonly ContextEntry[];
|
|
49
|
+
/** Tokens every retained skill catalog contributes to the surface. */
|
|
50
|
+
readonly skillTokens: number;
|
|
51
|
+
/** Tokens every retained instruction message contributes to the surface. */
|
|
52
|
+
readonly fileTokens: number;
|
|
53
|
+
/** Effective autocompaction threshold, or null when no policy is readable. */
|
|
54
|
+
readonly compaction: number | null;
|
|
55
|
+
}
|
|
56
|
+
/** One row of the panel, and one segment of the bar, in the reference's order. */
|
|
57
|
+
export type ContextSliceKey = 'mcp' | 'tools' | 'system' | 'skills' | 'memory' | 'messages' | 'autocompact' | 'free';
|
|
58
|
+
/** One priced slice of the context window. */
|
|
59
|
+
export interface ContextSlice {
|
|
60
|
+
readonly key: ContextSliceKey;
|
|
61
|
+
/** Tokens this slice holds; never negative. */
|
|
62
|
+
readonly tokens: number;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Everything the panel draws from: the host's two authoritative projections,
|
|
66
|
+
* this plugin's re-derived rows, and the session's capacity.
|
|
67
|
+
*/
|
|
68
|
+
export interface ContextSource {
|
|
69
|
+
/** `contextPressure.contextWindow`. */
|
|
70
|
+
readonly window: number;
|
|
71
|
+
/** Occupancy: `projectedTokens ?? pressureTokens` — the ring's own number. */
|
|
72
|
+
readonly used: number;
|
|
73
|
+
/** `contextBreakdown.systemTokens`. */
|
|
74
|
+
readonly systemTokens: number;
|
|
75
|
+
/** `contextBreakdown.toolsTokens`. */
|
|
76
|
+
readonly toolsTokens: number;
|
|
77
|
+
/** Per-tool rows, used only to split the authoritative total in two. */
|
|
78
|
+
readonly tools?: readonly ContextEntry[];
|
|
79
|
+
/** `contextBreakdown.messageTokens`. */
|
|
80
|
+
readonly messageTokens: number;
|
|
81
|
+
/** Tokens in every retained skill catalog, re-derived. */
|
|
82
|
+
readonly skillTokens: number;
|
|
83
|
+
/** Tokens in every retained instruction message, re-derived. */
|
|
84
|
+
readonly fileTokens: number;
|
|
85
|
+
/** Effective autocompaction threshold, or null when it is not readable. */
|
|
86
|
+
readonly compaction: number | null;
|
|
87
|
+
}
|
|
88
|
+
/** The panel's whole reading, ready to draw. */
|
|
89
|
+
export interface ContextBreakdown {
|
|
90
|
+
/** The session's capacity. */
|
|
91
|
+
readonly window: number;
|
|
92
|
+
/** Occupancy, exactly as the ring reads it. */
|
|
93
|
+
readonly used: number;
|
|
94
|
+
/** The ring's own integer percentage. */
|
|
95
|
+
readonly percent: number;
|
|
96
|
+
/** Every row, in the bar's own order; `free` is last. */
|
|
97
|
+
readonly slices: readonly ContextSlice[];
|
|
98
|
+
/** The slices the bar draws, without `free` — the track is the remainder. */
|
|
99
|
+
readonly segments: readonly ContextSlice[];
|
|
100
|
+
/** `used − Σ rows`: the sampled reading's distance from the heuristic tree. */
|
|
101
|
+
readonly residual: number;
|
|
102
|
+
}
|
|
103
|
+
/** Read a non-negative integer, or null when the value is not one. */
|
|
104
|
+
export declare function readCount(value: unknown): number | null;
|
|
105
|
+
/** Read one `[name, tokens]` list, dropping everything else. */
|
|
106
|
+
export declare function readEntries(value: unknown): readonly ContextEntry[];
|
|
107
|
+
/**
|
|
108
|
+
* Read one `ccdContext` value, from the wire or from a hand-written fixture.
|
|
109
|
+
*
|
|
110
|
+
* Deliberately lenient — a build that carries no such key, or a value from a
|
|
111
|
+
* later state version, must read as "this plugin's rows are not here" rather
|
|
112
|
+
* than throw into a render — and deliberately strict about *that*: a value that
|
|
113
|
+
* is not an object is null, so the panel can tell "no rows" from "zero rows".
|
|
114
|
+
*
|
|
115
|
+
* @param value - the raw projection value.
|
|
116
|
+
* @returns a usable view, or null when there is nothing to read.
|
|
117
|
+
*/
|
|
118
|
+
export declare function readContextView(value: unknown): ContextBreakdownView | null;
|
|
119
|
+
/** The empty view, for a build or a state version that carries no rows. */
|
|
120
|
+
export declare function emptyContextView(): ContextBreakdownView;
|
|
121
|
+
/**
|
|
122
|
+
* Split the authoritative tool total across the MCP and built-in groups.
|
|
123
|
+
*
|
|
124
|
+
* The host prices the assembled tool array whole (`ceil(JSON.length / 4) + 4`),
|
|
125
|
+
* so per-tool prices cannot be summed back to it — 37 real tools came to 7657
|
|
126
|
+
* against the array's 7492. The authoritative total is therefore split in the
|
|
127
|
+
* ratio the per-tool prices report, which keeps the two rows summing to the
|
|
128
|
+
* host's own number with the rounding difference absorbed here.
|
|
129
|
+
*
|
|
130
|
+
* @param tools - per-tool rows, `[name, tokens]`.
|
|
131
|
+
* @param total - the host's authoritative `toolsTokens`.
|
|
132
|
+
* @returns the MCP share and the built-in share, summing to `total`.
|
|
133
|
+
*/
|
|
134
|
+
export declare function splitTools(tools: readonly ContextEntry[], total: number): {
|
|
135
|
+
readonly mcp: number;
|
|
136
|
+
readonly tools: number;
|
|
137
|
+
};
|
|
138
|
+
/**
|
|
139
|
+
* The panel's rows, bar segments and residual.
|
|
140
|
+
*
|
|
141
|
+
* @param source - the host's projections plus this plugin's re-derived rows.
|
|
142
|
+
* @returns the whole reading; rows that price at zero are left out.
|
|
143
|
+
*/
|
|
144
|
+
export declare function contextBreakdown(source: ContextSource): ContextBreakdown;
|
|
145
|
+
/**
|
|
146
|
+
* The autocompaction segment's own height: what the policy holds back.
|
|
147
|
+
*
|
|
148
|
+
* `compaction-basic` resolves `floor(min(window × ratio, window − reserved −
|
|
149
|
+
* headroom))`; the segment is the distance from that threshold to the window,
|
|
150
|
+
* which is the room the window keeps for the summary it will write. A session
|
|
151
|
+
* with no readable policy, or one already past its threshold, draws nothing.
|
|
152
|
+
*
|
|
153
|
+
* @param window - the session's capacity.
|
|
154
|
+
* @param compaction - the resolved threshold, or null when unreadable.
|
|
155
|
+
* @param used - current occupancy, which the segment never overstates.
|
|
156
|
+
* @returns the segment's token height; 0 when it is not drawn.
|
|
157
|
+
*/
|
|
158
|
+
export declare function autocompactTokens(window: number, compaction: number | null, used: number): number;
|
|
159
|
+
/**
|
|
160
|
+
* The autocompaction threshold a request envelope implies.
|
|
161
|
+
*
|
|
162
|
+
* `reservedCompletionTokens` is the request's own output cap; a route that
|
|
163
|
+
* advertises none leaves the reservation unknown, and the row is then left out
|
|
164
|
+
* rather than guessed at.
|
|
165
|
+
*
|
|
166
|
+
* @param window - the session's capacity.
|
|
167
|
+
* @param reserved - the request's `maxTokens`, or null when it declares none.
|
|
168
|
+
* @param ratio - the policy's threshold ratio.
|
|
169
|
+
* @param headroom - the policy's headroom, in tokens.
|
|
170
|
+
* @returns the threshold, or null when it cannot be resolved.
|
|
171
|
+
*/
|
|
172
|
+
export declare function compactionThreshold(window: number, reserved: number | null, ratio?: number, headroom?: number): number | null;
|
|
173
|
+
/**
|
|
174
|
+
* The bar's segments and the free weight beside them, all in tokens.
|
|
175
|
+
*
|
|
176
|
+
* The reference drops a category whose share is under 2px instead of drawing a
|
|
177
|
+
* hairline. The bar's weights have to add up to the window for a segment's
|
|
178
|
+
* share to be its share of the window — and the rows deliberately do not, since
|
|
179
|
+
* the sampled reading and the heuristic tree differ — so the remainder is
|
|
180
|
+
* measured against the window rather than summed from the rows.
|
|
181
|
+
*
|
|
182
|
+
* @param breakdown - the panel's reading.
|
|
183
|
+
* @param barWidth - the bar's measured width in CSS pixels.
|
|
184
|
+
* @returns the drawn segments and the free weight, all in tokens.
|
|
185
|
+
*/
|
|
186
|
+
export declare function barSegments(breakdown: ContextBreakdown, barWidth: number): {
|
|
187
|
+
readonly drawn: readonly ContextSlice[];
|
|
188
|
+
readonly free: number;
|
|
189
|
+
};
|
|
190
|
+
/**
|
|
191
|
+
* The reference's compact token count: `342`, `4.6k`, `216.7k`, `1M`.
|
|
192
|
+
*
|
|
193
|
+
* One decimal place, with a trailing `.0` dropped — which is what the
|
|
194
|
+
* reference's own readings show (`48k`, `32.5k`, `216.7k`, `878k`, `1M`). The
|
|
195
|
+
* host's own formatter rounds to a whole number above 100 and would print
|
|
196
|
+
* `217k` where the reference prints `216.7k`; the panel follows the reference,
|
|
197
|
+
* and the header still agrees with the ring because both read the same
|
|
198
|
+
* `contextPressure` value.
|
|
199
|
+
*
|
|
200
|
+
* @param value - token count.
|
|
201
|
+
* @returns the compact reading.
|
|
202
|
+
*/
|
|
203
|
+
export declare function formatTokens(value: number): string;
|
|
204
|
+
/**
|
|
205
|
+
* One row's share of the window, at the reference's one decimal.
|
|
206
|
+
*
|
|
207
|
+
* @param tokens - the row's tokens.
|
|
208
|
+
* @param window - the session's capacity.
|
|
209
|
+
* @returns e.g. `4.8%`.
|
|
210
|
+
*/
|
|
211
|
+
export declare function formatPercent(tokens: number, window: number): string;
|
|
212
|
+
/**
|
|
213
|
+
* An MCP tool's two names, as the reference writes them.
|
|
214
|
+
*
|
|
215
|
+
* DSH gives every MCP tool the public name `mcp__<server>__<tool>`; the panel
|
|
216
|
+
* shows `<server> · <tool>`. A name that does not follow the convention is
|
|
217
|
+
* returned unchanged, so a build that renames the prefix loses the split and
|
|
218
|
+
* nothing else.
|
|
219
|
+
*
|
|
220
|
+
* @param name - the tool's public name.
|
|
221
|
+
* @returns the display label.
|
|
222
|
+
*/
|
|
223
|
+
export declare function toolLabel(name: string): string;
|
|
224
|
+
/** The MCP server one tool name belongs to, or null for a built-in tool. */
|
|
225
|
+
export declare function mcpServer(name: string): string | null;
|
|
226
|
+
/**
|
|
227
|
+
* Shorten a path from the middle, keeping its own file name.
|
|
228
|
+
*
|
|
229
|
+
* The reference writes `/Users/zoukai/code/dsh-ccd-…AGENTS.md`: the tail is what
|
|
230
|
+
* identifies the file, so the head gives way first. A path that already fits is
|
|
231
|
+
* returned unchanged.
|
|
232
|
+
*
|
|
233
|
+
* The budget is the panel's own label column: a fixed 360px card leaves about
|
|
234
|
+
* 208px for a detail row's name, which is this many characters of a path at
|
|
235
|
+
* 13px with room to spare. It is deliberately conservative — the row carries
|
|
236
|
+
* the full path in its `title`, and CSS's own ellipsis would eat the tail this
|
|
237
|
+
* function exists to keep.
|
|
238
|
+
*
|
|
239
|
+
* @param path - the path as the instruction message named it.
|
|
240
|
+
* @param budget - characters to keep, the ellipsis included.
|
|
241
|
+
* @returns the shortened path.
|
|
242
|
+
*/
|
|
243
|
+
export declare function shortenPath(path: string, budget?: number): string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-claude-desktop-theme",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Claude Code Desktop-style theme for DeepSeek Harness (DSH) Desktop on macOS and Windows, with light/dark themes, model/effort pickers and an interactive pixel whale",
|
|
6
6
|
"author": "zkforge",
|
|
@@ -81,10 +81,12 @@
|
|
|
81
81
|
"typecheck": "tsc --noEmit",
|
|
82
82
|
"check:architecture": "node scripts/check-architecture.mjs",
|
|
83
83
|
"check:package": "node scripts/check-package.mjs",
|
|
84
|
-
"test": "node --test tests/*.test.ts",
|
|
84
|
+
"test": "node --test tests/*.test.ts tests/*.test.mjs",
|
|
85
85
|
"check": "npm run typecheck && npm run check:architecture && npm test && npm run build && npm run check:package",
|
|
86
86
|
"prepack": "npm run check",
|
|
87
|
-
"pack:local": "npm pack --pack-destination artifacts"
|
|
87
|
+
"pack:local": "npm pack --pack-destination artifacts",
|
|
88
|
+
"pet:gifs": "node scripts/pet-gifs.mjs",
|
|
89
|
+
"effort:gifs": "node scripts/effort-gif.mjs"
|
|
88
90
|
},
|
|
89
91
|
"dependencies": {
|
|
90
92
|
"@deepseek-ai/schemastery": "3.18.4"
|
|
@@ -110,6 +112,7 @@
|
|
|
110
112
|
"@types/react": "18.3.1",
|
|
111
113
|
"esbuild": "0.28.2",
|
|
112
114
|
"react": "18.3.1",
|
|
115
|
+
"react-dom": "18.3.1",
|
|
113
116
|
"typescript": "7.0.2",
|
|
114
117
|
"yaml": "2.9.1"
|
|
115
118
|
}
|