dsh-model-think-level 1.0.7 → 1.1.1
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/lib/client/EffortEditor.d.ts +16 -0
- package/lib/client/HeadersEditor.d.ts +42 -2
- package/lib/client/injection/add-card.d.ts +72 -0
- package/lib/client/injection/create-card-pane.d.ts +53 -0
- package/lib/client/injection/editor-tabs.d.ts +17 -0
- package/lib/client/injection/model-order-drag.d.ts +0 -2
- package/lib/client/injection/models-page-editor.d.ts +150 -1
- package/lib/client/injection/provider-disabled-group.d.ts +56 -0
- package/lib/client/injection/row-drag.d.ts +45 -3
- package/lib/client/injection/saved-notice.d.ts +28 -0
- package/lib/client/injection/staged-headers.d.ts +128 -0
- package/lib/client/locales.d.ts +3 -0
- package/lib/client/model-order.d.ts +0 -2
- package/lib/client/ops.d.ts +16 -0
- package/lib/client/styles.d.ts +1 -1
- package/lib/client.js +143 -15
- package/lib/client.js.map +4 -4
- package/package.json +1 -1
|
@@ -143,6 +143,22 @@ export declare function ladderIntent(nextThinking: boolean, built: EffortIntent,
|
|
|
143
143
|
* @returns the draft, or undefined when it holds no field at all.
|
|
144
144
|
*/
|
|
145
145
|
export declare function compatDraftOf(compat: CompatSuggestion | undefined): CompatSuggestion | undefined;
|
|
146
|
+
/**
|
|
147
|
+
* The compat advice three MANUAL field values spell, from the values handed in.
|
|
148
|
+
*
|
|
149
|
+
* The editor's three compat controls are its own edit surface, so the handler
|
|
150
|
+
* that just changed one must report the value it computed: re-reading the
|
|
151
|
+
* component state there reports the PRE-update field, because React commits
|
|
152
|
+
* that state after the event — the queued write then carries the previous
|
|
153
|
+
* advice while the control already shows the new one.
|
|
154
|
+
* @param parts - the three field values as they will read after the event.
|
|
155
|
+
* @returns the draft, or undefined when all three are empty.
|
|
156
|
+
*/
|
|
157
|
+
export declare function manualCompatOf(parts: {
|
|
158
|
+
budgetField: string;
|
|
159
|
+
priorityText: string;
|
|
160
|
+
maxOutput: string;
|
|
161
|
+
}): CompatSuggestion | undefined;
|
|
146
162
|
/**
|
|
147
163
|
* Assemble one complete pending write from the parts a caller resolved: the
|
|
148
164
|
* compat merge (the applied suggestion's block with the caller's own fields on
|
|
@@ -14,6 +14,12 @@
|
|
|
14
14
|
* card's own fields. The headers land through `llm-pi-ai.providers.<route>.headers`,
|
|
15
15
|
* the one channel the adapter actually sends.
|
|
16
16
|
*
|
|
17
|
+
* Deferred mode (`deferred`) is this same section rendered where there is no
|
|
18
|
+
* route to write TO yet — the create-provider card's 高级配置 pane, whose route
|
|
19
|
+
* the card's own 保存 declares. There the draft is reported instead of written,
|
|
20
|
+
* and the whole-dict discipline is what makes the two commit seams the same
|
|
21
|
+
* write: see {@link DeferredHeaders}.
|
|
22
|
+
*
|
|
17
23
|
* @module dsh-model-think-level/HeadersEditor
|
|
18
24
|
*/
|
|
19
25
|
import type { ReactNode } from 'react';
|
|
@@ -54,6 +60,22 @@ export interface HeaderEnvironment {
|
|
|
54
60
|
preferOfficialLayer: boolean;
|
|
55
61
|
};
|
|
56
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Deferred mode: how a pane that has no route yet reports its draft.
|
|
65
|
+
*
|
|
66
|
+
* The create-provider card's 高级配置 pane edits an id the settings document
|
|
67
|
+
* does not declare — the card's own 保存 is what declares it — so that pane has
|
|
68
|
+
* nothing to write and no button to press. It hands the editor this instead:
|
|
69
|
+
* every change to the draft goes out as the complete dict its Save would write,
|
|
70
|
+
* and a Cancel reports that the draft is gone. Same component, same whole-dict
|
|
71
|
+
* discipline; only the commit seam changes, from a write to a report.
|
|
72
|
+
*/
|
|
73
|
+
export interface DeferredHeaders {
|
|
74
|
+
/** Every change to the draft, as the dict the editor's Save would write. */
|
|
75
|
+
onDraft: (headers: Record<string, string>) => void;
|
|
76
|
+
/** The user cleared the draft: whatever was staged for it must go. */
|
|
77
|
+
onCancel: () => void;
|
|
78
|
+
}
|
|
57
79
|
/** Props of {@link HeadersEditor}. */
|
|
58
80
|
export interface HeadersEditorProps {
|
|
59
81
|
/** The provider route this card edits. */
|
|
@@ -62,6 +84,24 @@ export interface HeadersEditorProps {
|
|
|
62
84
|
api: RemoteApi;
|
|
63
85
|
/** Localized copy. */
|
|
64
86
|
t: (key: string, params?: Record<string, string | number>) => string;
|
|
87
|
+
/**
|
|
88
|
+
* Close the whole provider card, as the official footer's `取消` does.
|
|
89
|
+
*
|
|
90
|
+
* Only the mount that owns the card's DOM can press that button, so the
|
|
91
|
+
* editor takes the action as a prop instead of reaching for the DOM itself
|
|
92
|
+
* (this component needs no injection pass — see the module doc). Called by
|
|
93
|
+
* {@link cancel} in pane mode only; omitted, cancel stays a draft reset.
|
|
94
|
+
*/
|
|
95
|
+
onDismiss?: () => void;
|
|
96
|
+
/**
|
|
97
|
+
* Deferred mode: report the draft instead of writing it.
|
|
98
|
+
*
|
|
99
|
+
* Set by the create-provider card's pane, whose route does not exist yet. The
|
|
100
|
+
* editor then edits in place — no disclosure, no Edit step — and has no Save
|
|
101
|
+
* of its own: its copy states that the draft lands with the card's own 保存,
|
|
102
|
+
* which is the write that declares the route the draft is addressed to.
|
|
103
|
+
*/
|
|
104
|
+
deferred?: DeferredHeaders;
|
|
65
105
|
}
|
|
66
106
|
/** One editable header row; a stable `id` keeps React keys off the name. */
|
|
67
107
|
interface DraftRow {
|
|
@@ -79,8 +119,8 @@ interface Draft {
|
|
|
79
119
|
export declare function headersFromDraft(draft: Draft): Record<string, string>;
|
|
80
120
|
/**
|
|
81
121
|
* Render the provider card's request-header section.
|
|
82
|
-
* @param props - route identity, write seam and
|
|
122
|
+
* @param props - route identity, write seam, copy and the card dismiss seam.
|
|
83
123
|
* @returns the section.
|
|
84
124
|
*/
|
|
85
|
-
export declare function HeadersEditor({ route, api, t }: HeadersEditorProps): ReactNode;
|
|
125
|
+
export declare function HeadersEditor({ route, api, t, onDismiss, deferred }: HeadersEditorProps): ReactNode;
|
|
86
126
|
export {};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The add-provider card: the custom-API form on its own, carrying the tab bar.
|
|
3
|
+
*
|
|
4
|
+
* The official card offers two ways to add a provider — adopt one from the
|
|
5
|
+
* built-in catalogue, or declare a custom API endpoint — behind a segmented
|
|
6
|
+
* "How to add" control. The catalogue half only duplicates the configured
|
|
7
|
+
* provider rows above it, so this pass reduces the card to the custom half: it
|
|
8
|
+
* selects that mode, hides the switch, and gives the form the same three panes
|
|
9
|
+
* its sibling editor cards carry — the bar, and the 高级配置 section the card
|
|
10
|
+
* has nowhere else to put ({@link CreateCardPane}).
|
|
11
|
+
*
|
|
12
|
+
* The card is the SAME card the tab takeover dresses (`bre-tabbed`), and that
|
|
13
|
+
* takeover scopes its regions to the visible half of the form
|
|
14
|
+
* ({@link activeScopeOf}). What it cannot do is hang the bar: the official card
|
|
15
|
+
* renders the provider-card slot only inside the catalogue half, so the custom
|
|
16
|
+
* form has no plugin mount to render a bar into. The mount is created here
|
|
17
|
+
* instead, at the top of the visible form, and it is the ONLY node this module
|
|
18
|
+
* inserts. Everything else is attributes on nodes the official page owns.
|
|
19
|
+
*
|
|
20
|
+
* Degradation is total: a card with no custom half (no API protocol to declare,
|
|
21
|
+
* or no `llm-pi-ai` namespace to declare one into) is left exactly as the
|
|
22
|
+
* official page built it, switch and all, and a card whose dressing throws is
|
|
23
|
+
* rolled back to that same state.
|
|
24
|
+
*
|
|
25
|
+
* @module dsh-model-think-level/client/injection/add-card
|
|
26
|
+
*/
|
|
27
|
+
import type { ReactNode } from 'react';
|
|
28
|
+
import type { Translate } from '@deepseek-ai/dsh-client-ui-slots';
|
|
29
|
+
import { type ForeignMount } from './mount.js';
|
|
30
|
+
import type { StagedHeadersState } from './staged-headers.js';
|
|
31
|
+
import type { RemoteApi } from '../types.js';
|
|
32
|
+
export interface AddCardDeps {
|
|
33
|
+
/** The plugin's own translator, for the tab labels and the pane's copy. */
|
|
34
|
+
t: Translate;
|
|
35
|
+
/** The write seam the pane's header editor reads the settings document with. */
|
|
36
|
+
api: RemoteApi;
|
|
37
|
+
/** The official labels of the form's provider-id field, by locale. */
|
|
38
|
+
routeIdLabels: () => readonly string[];
|
|
39
|
+
/** The session's deferred-header ledger (see `staged-headers`). */
|
|
40
|
+
staged: StagedHeadersState;
|
|
41
|
+
/** The plugin's locale-refresh seam, so the pane's copy follows a language
|
|
42
|
+
* switch exactly as the seats the official slot mounts do. */
|
|
43
|
+
refreshed: (children: () => ReactNode) => ReactNode;
|
|
44
|
+
}
|
|
45
|
+
/** One card's mount, remembered by the card it belongs to. */
|
|
46
|
+
interface AddCardHost {
|
|
47
|
+
/** The node the root renders into, so a re-created form can be told from a
|
|
48
|
+
* settled one. */
|
|
49
|
+
readonly host: HTMLElement;
|
|
50
|
+
readonly mount: ForeignMount;
|
|
51
|
+
}
|
|
52
|
+
export interface AddCardState {
|
|
53
|
+
/** Cards this module mounted a bar into. The mount goes with the card, so a
|
|
54
|
+
* card the page has dropped is dropped here on the next visit. */
|
|
55
|
+
readonly hosts: Map<HTMLElement, AddCardHost>;
|
|
56
|
+
}
|
|
57
|
+
export declare function createAddCardState(): AddCardState;
|
|
58
|
+
/**
|
|
59
|
+
* Reduce every add-provider card on the page to its custom form.
|
|
60
|
+
*
|
|
61
|
+
* Idempotent by construction: a card already showing the custom form with its
|
|
62
|
+
* bar up is left untouched, and switching the mode is driven by the official
|
|
63
|
+
* control's own selected state, so a re-visit writes nothing.
|
|
64
|
+
*/
|
|
65
|
+
export declare function reconcileAddCards(root: HTMLElement, deps: AddCardDeps, state: AddCardState): void;
|
|
66
|
+
/**
|
|
67
|
+
* Give every card back to the official page: the mounts go, and the switch
|
|
68
|
+
* comes out of hiding. The mode the card is left in is the official page's own
|
|
69
|
+
* state, and it is not this module's to reset — React unmounts the card whole.
|
|
70
|
+
*/
|
|
71
|
+
export declare function teardownAddCards(state: AddCardState): void;
|
|
72
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The create-provider card's 高级配置 pane: the tab bar and the request-header
|
|
3
|
+
* editor, for a provider that does not exist yet.
|
|
4
|
+
*
|
|
5
|
+
* This is the half {@link add-card} used to leave out. On an existing provider
|
|
6
|
+
* the same editor arrives through the official `settings.models.provider-card`
|
|
7
|
+
* slot, which the official card renders only inside its CATALOGUE half — so the
|
|
8
|
+
* custom-only card this plugin reduces it to had a tab bar whose 高级配置 button
|
|
9
|
+
* could never light up: nothing in that half carried a `.bre-headers` section for
|
|
10
|
+
* the takeover to reveal. This pane is that section, mounted at the same seat
|
|
11
|
+
* the bar is.
|
|
12
|
+
*
|
|
13
|
+
* The route the pane edits is the id the user is TYPING, which is the one thing
|
|
14
|
+
* about this card the settings document does not have yet — the card's own 保存
|
|
15
|
+
* is what declares it. So the pane cannot write as the official card can:
|
|
16
|
+
*
|
|
17
|
+
* - the id is read off the live form (the panel's own label, never a position)
|
|
18
|
+
* and re-read on every keystroke, because the form's React state is not ours
|
|
19
|
+
* to subscribe to;
|
|
20
|
+
* - the rows are editable whether or not the id has been typed: a draft is held
|
|
21
|
+
* by the session ledger once it has a route to belong to, and until then it
|
|
22
|
+
* simply waits for the id — which the pane does NOT announce, because the id
|
|
23
|
+
* field is on the 供应商配置 tab of this same card and the official form
|
|
24
|
+
* already speaks for it;
|
|
25
|
+
* - once there is an id the editor runs in DEFERRED mode: it edits, and reports
|
|
26
|
+
* the draft to the session ledger, but the write happens when the route
|
|
27
|
+
* appears in the document — the proof the card's own create landed. The pane
|
|
28
|
+
* also asks, one keystroke in, whether that id was ALREADY declared, because
|
|
29
|
+
* only a route that was not can be this card's work. See
|
|
30
|
+
* {@link staged-headers} for why that is a gate and not a delay.
|
|
31
|
+
*
|
|
32
|
+
* @module dsh-model-think-level/client/injection/create-card-pane
|
|
33
|
+
*/
|
|
34
|
+
import type { ReactNode } from 'react';
|
|
35
|
+
import type { Translate } from '@deepseek-ai/dsh-client-ui-slots';
|
|
36
|
+
import type { RemoteApi } from '../types.js';
|
|
37
|
+
import { type StagedHeadersState } from './staged-headers.js';
|
|
38
|
+
export interface CreateCardPaneProps {
|
|
39
|
+
/** The plugin's own mount inside this card; the bar resolves the card from it. */
|
|
40
|
+
host: {
|
|
41
|
+
readonly current: HTMLDivElement | null;
|
|
42
|
+
};
|
|
43
|
+
/** The write seam, for the editor's own settings read. */
|
|
44
|
+
api: RemoteApi;
|
|
45
|
+
/** The plugin's translator. */
|
|
46
|
+
t: Translate;
|
|
47
|
+
/** The official labels of the form's provider-id field, by locale. */
|
|
48
|
+
routeIdLabels: () => readonly string[];
|
|
49
|
+
/** The session's deferred-header ledger. */
|
|
50
|
+
staged: StagedHeadersState;
|
|
51
|
+
}
|
|
52
|
+
/** One create card's pane: its bar, and the headers pane once there is a route. */
|
|
53
|
+
export declare function CreateCardPane({ host, api, t, routeIdLabels, staged }: CreateCardPaneProps): ReactNode;
|
|
@@ -75,6 +75,23 @@ export declare function createEditorTabsState(): EditorTabsState;
|
|
|
75
75
|
* other.
|
|
76
76
|
*/
|
|
77
77
|
export declare function editorCardOf(element: Element | null): HTMLElement | null;
|
|
78
|
+
/**
|
|
79
|
+
* The part of a card that presents its editor.
|
|
80
|
+
*
|
|
81
|
+
* A provider ROW has one editor and this returns the card itself. The
|
|
82
|
+
* create-provider card has TWO form halves — the built-in catalogue and the
|
|
83
|
+
* custom API declaration — and the official page mounts each once it is visited
|
|
84
|
+
* and hides the inactive one with `hidden`. Every lookup below is scoped to the
|
|
85
|
+
* visible half, because the hidden one carries a provider-card slot of its own:
|
|
86
|
+
* the plugin's mount inside it publishes a route, a tab bar and a headers
|
|
87
|
+
* section, and reading those would dress the visible form with the hidden
|
|
88
|
+
* form's provider.
|
|
89
|
+
*
|
|
90
|
+
* A card with both halves hidden (mid-swap, before React commits the show) and
|
|
91
|
+
* a card with none are both scoped to the card, which is the rule this module
|
|
92
|
+
* has always applied to a single-form card.
|
|
93
|
+
*/
|
|
94
|
+
export declare function activeScopeOf(card: HTMLElement): HTMLElement;
|
|
78
95
|
/**
|
|
79
96
|
* Switch one card to a tab. The card attribute is the single source of truth:
|
|
80
97
|
* the stylesheet swaps the pane on the attribute alone, and the bar's buttons
|
|
@@ -79,5 +79,3 @@ export declare function reconcileModelOrder(root: HTMLElement, deps: ModelOrderD
|
|
|
79
79
|
* persisting that order — on behalf of a fiber that no longer exists.
|
|
80
80
|
*/
|
|
81
81
|
export declare function teardownModelOrder(state: ModelOrderState): void;
|
|
82
|
-
/** The stored sequence for one provider, for callers that only need to read it. */
|
|
83
|
-
export declare function storedModelOrder(provider: string): readonly string[];
|
|
@@ -74,6 +74,14 @@ export interface HostLabels {
|
|
|
74
74
|
* offers this plugin no seat to stamp that name on.
|
|
75
75
|
*/
|
|
76
76
|
editProvider: readonly string[];
|
|
77
|
+
/**
|
|
78
|
+
* The official input-types control's image choice ("Image" / "图片"). When a
|
|
79
|
+
* row ships that control, it — not the plugin's ledger — is what tells the
|
|
80
|
+
* user (and the official Save) that a model takes images, so the copy is the
|
|
81
|
+
* anchor that finds the box a verdict belongs in: see
|
|
82
|
+
* {@link echoOfficialModality}.
|
|
83
|
+
*/
|
|
84
|
+
inputImage: readonly string[];
|
|
77
85
|
}
|
|
78
86
|
/** The join the injector renders from. */
|
|
79
87
|
export interface InjectorDeps {
|
|
@@ -103,6 +111,8 @@ export interface InjectorDeps {
|
|
|
103
111
|
* editing" is the only moment this side may safely write the document.
|
|
104
112
|
*/
|
|
105
113
|
onIdle?(): void;
|
|
114
|
+
/** Wake the debounced scan after provider-wide adaptation stages a row. */
|
|
115
|
+
onPendingChanged?(): void;
|
|
106
116
|
/**
|
|
107
117
|
* Arm a timed retry after a failed idle pass. The backoff clock only helps
|
|
108
118
|
* if something wakes the injector when it expires; a settled page has no DOM
|
|
@@ -389,6 +399,65 @@ export interface AutofillFootprint {
|
|
|
389
399
|
* position (the label's trailing number IS the position).
|
|
390
400
|
*/
|
|
391
401
|
export declare function inputValueByLabel(card: HTMLElement, labels: readonly string[]): string;
|
|
402
|
+
/**
|
|
403
|
+
* The official input-types checkbox an image verdict belongs to, or undefined
|
|
404
|
+
* when this row ships no such control (a legacy kernel) — or ships one whose
|
|
405
|
+
* boxes cannot be told apart.
|
|
406
|
+
*
|
|
407
|
+
* The official fieldset maps its two choices in a fixed order (`["text",
|
|
408
|
+
* "image"]`) and labels each one, so the image box is the one whose own label
|
|
409
|
+
* carries the image copy — matched like every other anchor, by copy, with the
|
|
410
|
+
* second box as the fallback for a host whose copy is in a language neither
|
|
411
|
+
* this plugin's locale nor its English floor knows: the order still identifies
|
|
412
|
+
* it.
|
|
413
|
+
*/
|
|
414
|
+
export declare function officialImageCheckboxOf(container: HTMLElement, imageLabels: readonly string[]): HTMLInputElement | undefined;
|
|
415
|
+
/**
|
|
416
|
+
* Print a verdict about images on the OFFICIAL input-types control, and report
|
|
417
|
+
* whether that control now owns the row's modality.
|
|
418
|
+
*
|
|
419
|
+
* The official checkbox renders the official card's own DRAFT, not the
|
|
420
|
+
* settings document, so a document write of the plugin's can never tick it
|
|
421
|
+
* before the card is saved and reopened — the gap the user hit. Clicking the
|
|
422
|
+
* box is a real user-equivalent edit: the official form holds the tick in its
|
|
423
|
+
* draft and its own Save is what persists it. That is also why a row reaching
|
|
424
|
+
* this path must keep the modality OUT of its own write (see
|
|
425
|
+
* {@link officialModalityIntent}): two writers for one field would let this
|
|
426
|
+
* plugin's held write land after that Save and revert whatever the user chose
|
|
427
|
+
* in the meantime.
|
|
428
|
+
*
|
|
429
|
+
* A missing box, or a DISABLED one (a read-only card, which will not carry the
|
|
430
|
+
* click anywhere), reports `false` so the caller keeps its own write — the
|
|
431
|
+
* only remaining way the verdict can reach the document.
|
|
432
|
+
*/
|
|
433
|
+
export declare function echoOfficialModality(container: HTMLElement, image: boolean, imageLabels: readonly string[]): boolean;
|
|
434
|
+
/**
|
|
435
|
+
* The modality intent a row's own write may carry, once the official
|
|
436
|
+
* input-types control has been offered the verdict.
|
|
437
|
+
*
|
|
438
|
+
* `undefined` out means the write states nothing about the modality: either it
|
|
439
|
+
* never carried one, or the official control took the verdict over. A row with
|
|
440
|
+
* no such control — a legacy kernel, or a row the user has left folded — hands
|
|
441
|
+
* its intent through unchanged, which keeps the document write as the way the
|
|
442
|
+
* verdict travels.
|
|
443
|
+
*
|
|
444
|
+
* `null` is the plugin's deliberate UNSET, which the official control cannot
|
|
445
|
+
* express at all (it knows only text and text+image): it passes through
|
|
446
|
+
* untouched.
|
|
447
|
+
*/
|
|
448
|
+
export declare function officialModalityIntent(container: HTMLElement | undefined, input: InputModalities | null | undefined, imageLabels: readonly string[]): InputModalities | null | undefined;
|
|
449
|
+
/**
|
|
450
|
+
* One model row a card shows on screen: what the walk needs to name it, plus
|
|
451
|
+
* the row element itself, so a verdict about its modality can be printed on
|
|
452
|
+
* the row's own official control (which lives inside the row, collapsed or
|
|
453
|
+
* not). The element is optional because a caller may know the rows only by
|
|
454
|
+
* their ids.
|
|
455
|
+
*/
|
|
456
|
+
export interface ScreenModel {
|
|
457
|
+
readonly id: string;
|
|
458
|
+
readonly name: string;
|
|
459
|
+
readonly row?: HTMLElement;
|
|
460
|
+
}
|
|
392
461
|
/**
|
|
393
462
|
* Adapt every model of one provider, the rows the user never unfolded
|
|
394
463
|
* included.
|
|
@@ -414,11 +483,91 @@ export declare function inputValueByLabel(card: HTMLElement, labels: readonly st
|
|
|
414
483
|
* @param state - mutable scan state: the adapting guard and the held ledger.
|
|
415
484
|
* @param deps - the injection dependencies.
|
|
416
485
|
* @param route - the provider route whose models are adapted.
|
|
486
|
+
* @param state - mutable scan state: the adapting guard and the held ledger.
|
|
487
|
+
* @param deps - the injection dependencies.
|
|
488
|
+
* @param route - the provider route whose models are adapted.
|
|
489
|
+
* @param visibleRows - the models the route's card shows on screen, from the
|
|
490
|
+
* scan's own rows (id and display name). The document cannot know the rows
|
|
491
|
+
* the card holds but has not saved yet, so the pass extends its walk with
|
|
492
|
+
* them: their holds land with the card's own Save, exactly like a row's own
|
|
493
|
+
* auto-adapt.
|
|
417
494
|
* @returns what the pass did, so the seat can say something true about the
|
|
418
495
|
* click: how many models it held an adaptation for, how many had no
|
|
419
496
|
* suggestion, and -- when nothing could be adapted at all -- why.
|
|
420
497
|
*/
|
|
421
|
-
export declare function adaptEveryModel(state: ScanState, deps: InjectorDeps, route: string): Promise<AutoAdaptReport>;
|
|
498
|
+
export declare function adaptEveryModel(state: ScanState, deps: InjectorDeps, route: string, visibleRows?: readonly ScreenModel[]): Promise<AutoAdaptReport>;
|
|
499
|
+
/**
|
|
500
|
+
* Hand a viewport delta back to whichever container actually moved the card.
|
|
501
|
+
*
|
|
502
|
+
* The card's distance from the top of the viewport is the sum of every scroll
|
|
503
|
+
* offset between it and the document, so the correction belongs to the
|
|
504
|
+
* container that really moved: usually the nearest scrollable one, but a
|
|
505
|
+
* deletion near the bottom of a long page moves the PAGE's offset instead. Each
|
|
506
|
+
* container is asked for as much of the delta as it can still give, the ones
|
|
507
|
+
* this window has already written first and the rest from the inside out, and
|
|
508
|
+
* every container that answers is watched from then on. A container whose
|
|
509
|
+
* offset is no longer the one this loop left is being scrolled by somebody
|
|
510
|
+
* else, and is skipped rather than fought.
|
|
511
|
+
*/
|
|
512
|
+
export declare function absorbDelta(chain: readonly HTMLElement[], watched: Set<HTMLElement>, lastSet: Map<HTMLElement, number>, delta: number): void;
|
|
513
|
+
/**
|
|
514
|
+
* Start recording where a click inside a model list leaves its scroll host.
|
|
515
|
+
*
|
|
516
|
+
* Document-level and capture-phase, like the stand-down listeners a re-pin
|
|
517
|
+
* window installs: the position has to be read before the official handler
|
|
518
|
+
* (and React's commit behind it) can shorten the list. Reference counted, so
|
|
519
|
+
* a reload that lands a second fiber over a page that already has one does not
|
|
520
|
+
* take the listeners away from the first.
|
|
521
|
+
*/
|
|
522
|
+
export declare function watchModelListOffsets(): void;
|
|
523
|
+
/**
|
|
524
|
+
* Stop watching, drop every blank, and forget the last position.
|
|
525
|
+
*
|
|
526
|
+
* The blanks are elements this plugin owns inside the official page: a fiber
|
|
527
|
+
* that goes away must not leave a scroll container holding a gap nothing
|
|
528
|
+
* maintains. Removing one can move the view (the range it held goes with it),
|
|
529
|
+
* which is the right thing to do to a page whose watcher is gone.
|
|
530
|
+
*/
|
|
531
|
+
export declare function unwatchModelListOffsets(): void;
|
|
532
|
+
/**
|
|
533
|
+
* Hold every open provider card's top across a model row burst, and hold the
|
|
534
|
+
* scroll host's own range open while it does.
|
|
535
|
+
*
|
|
536
|
+
* The official list keys rows by array index, so a deletion removes the LAST
|
|
537
|
+
* element and re-fills the survivors: the card loses a row's height INSIDE
|
|
538
|
+
* itself, above whatever its tail holds. Left to its anchoring the browser
|
|
539
|
+
* keeps a node near the hole visually stable instead — it buys the shrink
|
|
540
|
+
* back by moving the whole view, and deleting a model slides the page DOWN
|
|
541
|
+
* rather than letting the content below the list contract upward into the
|
|
542
|
+
* hole it opened. Suppressing anchoring from the burst's own microtask (the
|
|
543
|
+
* injector runs this before the browser has laid the deletion out) holds the
|
|
544
|
+
* card's top where it was: the header does not move, and the tail rises.
|
|
545
|
+
*
|
|
546
|
+
* Anchoring is only half of it, and the half that shows up when the list is
|
|
547
|
+
* scrolled near its end. There the shrink also takes the scroll RANGE away,
|
|
548
|
+
* and the browser clamps the offset down to the new maximum on the first
|
|
549
|
+
* flush that sees the shorter content — which is this very microtask.
|
|
550
|
+
* Measured on the live page (list scrolled to its end, eight deletions): every
|
|
551
|
+
* step moved the card's top by exactly the amount the clamp took, 54px per
|
|
552
|
+
* row, and no anchor suppression could stop it because nothing was anchoring:
|
|
553
|
+
* the range was simply gone. {@link holdListSlack} is what gives it back, from
|
|
554
|
+
* the position the row's own click recorded before the change.
|
|
555
|
+
*
|
|
556
|
+
* The window also covers the burst's second height change. The re-filled rows
|
|
557
|
+
* leave the mounted editor of the model that moved out of them STALE, and the
|
|
558
|
+
* debounced scan re-renders (or mounts) it a frame or two after the deletion
|
|
559
|
+
* itself — the same two-stage change {@link pinCardTop} was built for, now
|
|
560
|
+
* opened by the mutation rather than by a click.
|
|
561
|
+
*
|
|
562
|
+
* The card vocabulary is the same three flavours {@link wireEditRepin} knows:
|
|
563
|
+
* a deletion inside a NEW provider's setup card (or the add card) has no
|
|
564
|
+
* rowCard to climb into at all, and a pin that names only saved providers
|
|
565
|
+
* silently skips the very card the user is editing.
|
|
566
|
+
*
|
|
567
|
+
* @param root - the panel to search for model lists.
|
|
568
|
+
* @param windowMs - how long each found card stays pinned.
|
|
569
|
+
*/
|
|
570
|
+
export declare function pinModelListCards(root: HTMLElement, windowMs?: number): void;
|
|
422
571
|
/**
|
|
423
572
|
* Scan the settings DOM for official model rows and reconcile the injected
|
|
424
573
|
* editors. Idempotent: existing editors are left alone, new disclosures get
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The folded group of switched-off providers on the official Models page.
|
|
3
|
+
*
|
|
4
|
+
* A switched-off provider is not deleted: its row is the only place it can be
|
|
5
|
+
* switched back on, and its configuration is still the user's. It does not
|
|
6
|
+
* belong in the list the user reads either — a page whose built-in providers all
|
|
7
|
+
* stay in the way makes the two or three real ones hard to find. So the rows of
|
|
8
|
+
* the switched-off providers fold away behind one control at the end of the
|
|
9
|
+
* list, and that control says how many are folded and unfolds them again.
|
|
10
|
+
*
|
|
11
|
+
* Unfolding does not scatter them back among the providers that are on: the
|
|
12
|
+
* folded rows are ORDERED behind the control, so the group reads as one group
|
|
13
|
+
* under its own control. The order comes from the plugin's stylesheet
|
|
14
|
+
* (`.bre-row-folded`, `.bre-provider-disabled-seat`), where `!important` outranks
|
|
15
|
+
* the inline orders the drag passes write — this module therefore never has to
|
|
16
|
+
* fight them for the attribute, and the rows are never moved or rebuilt: their
|
|
17
|
+
* DOM slot, which the drag passes read back out of the DOM, stays exactly where
|
|
18
|
+
* the host put them.
|
|
19
|
+
*
|
|
20
|
+
* The only node this module inserts is its own control, as the list's last item:
|
|
21
|
+
* a control outside the list could not head rows inside it, and the list is a
|
|
22
|
+
* flex column the host itself lays out. Both the class writes and the insertion
|
|
23
|
+
* are guarded on the state they would produce: a scan that changes nothing
|
|
24
|
+
* writes nothing, because every write here would otherwise feed the observer
|
|
25
|
+
* that scheduled the scan.
|
|
26
|
+
*
|
|
27
|
+
* The folded state is per session and deliberately not stored. The point of the
|
|
28
|
+
* group is that the switched-off providers are out of the way, and a reload
|
|
29
|
+
* showing them folded again is the behaviour that was asked for.
|
|
30
|
+
*
|
|
31
|
+
* @module dsh-model-think-level/client/injection/provider-disabled-group
|
|
32
|
+
*/
|
|
33
|
+
import type { HostLabels } from './models-page-editor.js';
|
|
34
|
+
/** Per-scan bookkeeping (one instance lives in `models-page.ts`). */
|
|
35
|
+
export interface ProviderDisabledGroupState {
|
|
36
|
+
/** Whether the folded rows are currently unfolded. */
|
|
37
|
+
expanded: boolean;
|
|
38
|
+
}
|
|
39
|
+
export interface ProviderDisabledGroupDeps {
|
|
40
|
+
/** The plugin's own translator, for the control's label and tooltip. */
|
|
41
|
+
t: (key: string, params?: Record<string, string | number>) => string;
|
|
42
|
+
/** The official `settings.models` label sets, resolved fresh on every scan. */
|
|
43
|
+
labels: () => HostLabels;
|
|
44
|
+
}
|
|
45
|
+
export declare function createProviderDisabledGroupState(): ProviderDisabledGroupState;
|
|
46
|
+
/**
|
|
47
|
+
* Fold the switched-off providers away — or put them back when the control says
|
|
48
|
+
* so — and keep the control's own label, count and position in step.
|
|
49
|
+
*
|
|
50
|
+
* Idempotent: a row already folded stays folded, and the control is only ever
|
|
51
|
+
* written when a value genuinely changed, so a scan never mutates the page on
|
|
52
|
+
* behalf of a state that is already on it.
|
|
53
|
+
*/
|
|
54
|
+
export declare function reconcileProviderDisabledGroup(root: HTMLElement, deps: ProviderDisabledGroupDeps, state: ProviderDisabledGroupState): void;
|
|
55
|
+
/** Remove the control and unfold every row it hid. */
|
|
56
|
+
export declare function teardownProviderDisabledGroup(): void;
|
|
@@ -62,6 +62,13 @@ export interface DragSession {
|
|
|
62
62
|
frame: number | undefined;
|
|
63
63
|
dropped: boolean;
|
|
64
64
|
}
|
|
65
|
+
/** What a list looked like the last time a scan read it. */
|
|
66
|
+
export interface ListSighting {
|
|
67
|
+
/** The ids in the sequence they screened, in screen order. */
|
|
68
|
+
readonly ids: readonly string[];
|
|
69
|
+
/** The row elements that were on screen. */
|
|
70
|
+
readonly rows: readonly Element[];
|
|
71
|
+
}
|
|
65
72
|
/** Per-scan bookkeeping; one instance per module that owns a list. */
|
|
66
73
|
export interface DragState {
|
|
67
74
|
/** The drag sources already carrying listeners. */
|
|
@@ -70,6 +77,16 @@ export interface DragState {
|
|
|
70
77
|
readonly lists: WeakSet<Element>;
|
|
71
78
|
/** The insertion line; created on the first drag and reused until teardown. */
|
|
72
79
|
line: HTMLElement | undefined;
|
|
80
|
+
/**
|
|
81
|
+
* What each list looked like on the last pass, by list element.
|
|
82
|
+
*
|
|
83
|
+
* Needed because a removal cannot be told from a rename or an addition by
|
|
84
|
+
* looking at the current DOM: when the host keys its rows by index, deleting
|
|
85
|
+
* a model leaves every row below it drawn with the next model, so the rows on
|
|
86
|
+
* screen are the same elements holding the same `order`s they always had. See
|
|
87
|
+
* {@link arrangement} for what that does to the screen order.
|
|
88
|
+
*/
|
|
89
|
+
readonly sighted: WeakMap<Element, ListSighting>;
|
|
73
90
|
drag: DragSession | undefined;
|
|
74
91
|
}
|
|
75
92
|
export declare function createDragState(): DragState;
|
|
@@ -78,9 +95,11 @@ export declare function rowElements(parent: HTMLElement): HTMLElement[];
|
|
|
78
95
|
/** The rows in the sequence they currently screen. */
|
|
79
96
|
export declare function visualRows(list: HTMLElement): HTMLElement[];
|
|
80
97
|
/**
|
|
81
|
-
* Put `sequence` on screen
|
|
82
|
-
*
|
|
83
|
-
*
|
|
98
|
+
* Put `sequence` on screen against the order the rows currently screen in.
|
|
99
|
+
*
|
|
100
|
+
* Only for a gesture in flight: the pointer's arrangement IS the screen order,
|
|
101
|
+
* so that is the sequence the rows are placed against. Everything outside a
|
|
102
|
+
* gesture goes through {@link applyIdOrder}, which places against the DOM.
|
|
84
103
|
*/
|
|
85
104
|
export declare function placeRows(list: HTMLElement, sequence: readonly HTMLElement[]): void;
|
|
86
105
|
/**
|
|
@@ -96,6 +115,14 @@ export declare function orderedIds(list: HTMLElement, idOf: (row: HTMLElement) =
|
|
|
96
115
|
/**
|
|
97
116
|
* Place the rows in `ids` order. Rows without a known id are left exactly where
|
|
98
117
|
* they are: a row we cannot name is a row we must not move.
|
|
118
|
+
*
|
|
119
|
+
* The slots come from the DOM order. React owns the child list and keeps it
|
|
120
|
+
* compacted and truthful across a removal, while the inline `order`s still on
|
|
121
|
+
* the survivors describe where those models were before it. Taking the slots
|
|
122
|
+
* from the DOM and the sequence from {@link arrangement} is what lets the two
|
|
123
|
+
* meet: the rows land in the order the user had, in the slots the host now
|
|
124
|
+
* genuinely has, so the gap a deletion left closes instead of leaving a row
|
|
125
|
+
* behind the end of the list.
|
|
99
126
|
*/
|
|
100
127
|
export declare function applyIdOrder(list: HTMLElement, ids: readonly string[], idOf: (row: HTMLElement) => string | undefined): void;
|
|
101
128
|
/** Hand every row back the inline geometry a drag wrote on it. */
|
|
@@ -121,6 +148,21 @@ export declare function dropLineTop(row: HTMLElement, after: boolean): number;
|
|
|
121
148
|
export declare function makeGhost(row: HTMLElement, label?: string): HTMLElement;
|
|
122
149
|
/** The nearest ancestor that actually scrolls, for the edge auto-scroll. */
|
|
123
150
|
export declare function scrollHostOf(node: HTMLElement): HTMLElement | undefined;
|
|
151
|
+
/**
|
|
152
|
+
* Every container that can hold `node` in place, nearest first.
|
|
153
|
+
*
|
|
154
|
+
* {@link scrollHostOf} answers the one container an edge-drag has to drive. A
|
|
155
|
+
* window that CORRECTS the view needs the rest of the chain as well: the offset
|
|
156
|
+
* a change takes away is held by whichever ancestor the browser is using as the
|
|
157
|
+
* scrolling box, and the nearest container is only the innermost candidate for
|
|
158
|
+
* that — an ancestor the nearest one delegates to can be the one that moved.
|
|
159
|
+
*
|
|
160
|
+
* The page's own scroller is the last candidate even though it never declares
|
|
161
|
+
* an overflow of its own: a long settings page scrolls there when nothing
|
|
162
|
+
* inside it does, and a row deletion near the bottom of that page takes its
|
|
163
|
+
* range away exactly like a deletion inside a small panel would.
|
|
164
|
+
*/
|
|
165
|
+
export declare function scrollChainOf(node: HTMLElement): HTMLElement[];
|
|
124
166
|
/** The pixels per frame `host` should scroll while the pointer is at `clientY`. */
|
|
125
167
|
export declare function edgeScrollStep(host: HTMLElement, clientY: number): number;
|
|
126
168
|
/**
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The official "Saved <provider>." line, taken out of the flow and
|
|
3
|
+
* answered by a bubble at the top of the window instead.
|
|
4
|
+
*
|
|
5
|
+
* The host confirms a save by inserting a paragraph above the provider list
|
|
6
|
+
* (`ModelsSection`'s `savedNotice`, `role="status"`) that stays there until the
|
|
7
|
+
* next edit, remove or add clears it. It is feedback, not state — the row it
|
|
8
|
+
* names is on screen and shows the save itself — but it holds a whole line of
|
|
9
|
+
* the panel's height the whole time the user works in the card, which is the
|
|
10
|
+
* "the hint eats space" report. So the line is hidden (never removed: React
|
|
11
|
+
* owns it, and it must survive the page's own re-renders) and the same words go
|
|
12
|
+
* up as the plugin's fixed top-centre bubble, which takes itself away after
|
|
13
|
+
* {@link NOTE_TTL_MS} — the treatment the auto-adapt seat's verdict already
|
|
14
|
+
* gets, and the one the host's own toast uses.
|
|
15
|
+
*
|
|
16
|
+
* The pass owns no wire and no state of its own: it is DOM-only, idempotent and
|
|
17
|
+
* compare-before-write, so a settled page emits no mutation of its own.
|
|
18
|
+
*
|
|
19
|
+
* @module dsh-model-think-level/client/injection/saved-notice
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Hide every official saved line inside the panel, and put up a bubble for the
|
|
23
|
+
* one that just appeared.
|
|
24
|
+
* @param root - the Models panel the passes all decorate.
|
|
25
|
+
*/
|
|
26
|
+
export declare function reconcileSavedNotice(root: HTMLElement): void;
|
|
27
|
+
/** Put every official line back and take the bubble down (plugin dispose). */
|
|
28
|
+
export declare function teardownSavedNotice(): void;
|