@deepwatch/dsh-client-settings 0.1.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/LICENSE +21 -0
- package/README.md +82 -0
- package/lib/client/binding-state.d.ts +334 -0
- package/lib/client/binding-state.js +543 -0
- package/lib/client/binding-state.js.map +1 -0
- package/lib/client/chat-gate.d.ts +100 -0
- package/lib/client/chat-gate.js +141 -0
- package/lib/client/chat-gate.js.map +1 -0
- package/lib/client/components.d.ts +194 -0
- package/lib/client/components.js +273 -0
- package/lib/client/components.js.map +1 -0
- package/lib/client/index.d.ts +35 -0
- package/lib/client/index.js +273 -0
- package/lib/client/index.js.map +1 -0
- package/lib/client/onboarding.d.ts +28 -0
- package/lib/client/onboarding.js +31 -0
- package/lib/client/onboarding.js.map +1 -0
- package/lib/client/readiness.d.ts +36 -0
- package/lib/client/readiness.js +207 -0
- package/lib/client/readiness.js.map +1 -0
- package/lib/client/role-bindings.d.ts +118 -0
- package/lib/client/role-bindings.js +211 -0
- package/lib/client/role-bindings.js.map +1 -0
- package/lib/client.js +3888 -0
- package/lib/client.js.map +1 -0
- package/lib/index.d.ts +13 -0
- package/lib/index.js +13 -0
- package/lib/index.js.map +1 -0
- package/lib/ocr-measured.d.ts +27 -0
- package/lib/ocr-measured.js +66 -0
- package/lib/ocr-measured.js.map +1 -0
- package/lib/providers.d.ts +26 -0
- package/lib/providers.js +26 -0
- package/lib/providers.js.map +1 -0
- package/package.json +83 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* The composer, closed until something is actually bound to Chat — and the
|
|
4
|
+
* fastest way to bind it, offered where the person is standing.
|
|
5
|
+
*
|
|
6
|
+
* A person typed a message into a composer that looked ready, pressed send,
|
|
7
|
+
* and the prompt was routed to a provider they had never configured. Nothing
|
|
8
|
+
* about the composer had told them otherwise: the model chip named a DeepSeek
|
|
9
|
+
* model they had not chosen, the send button was live, and the first thing
|
|
10
|
+
* that disagreed was a failed turn.
|
|
11
|
+
*
|
|
12
|
+
* This is the client half of preflight. It raises the block upstream provides
|
|
13
|
+
* for exactly this — *"Composer blocks: the one way another plugin stops a
|
|
14
|
+
* session's input"* — so the textarea goes inert, the send button stops
|
|
15
|
+
* accepting, and the placeholder says why in the words of whoever knows.
|
|
16
|
+
*
|
|
17
|
+
* **This is an affordance, and it is not the enforcement.** It lives in a
|
|
18
|
+
* browser tab. The Host refuses an unbound route regardless of what any client
|
|
19
|
+
* disables (`@deepwatch/dsh-technology/routing`), and the composed default
|
|
20
|
+
* names no route so the Harness's own admission boundary refuses a turn before
|
|
21
|
+
* one exists. The value of this layer is not that it makes refusal certain — it
|
|
22
|
+
* is that a person finds out *before* typing rather than after sending.
|
|
23
|
+
*
|
|
24
|
+
* **The draft survives.** Blocking is one inert textarea, never a second tree:
|
|
25
|
+
* the composer keeps its DOM, so whatever was typed is still there when the
|
|
26
|
+
* binding is fixed. That is upstream's design and this file's reason for using
|
|
27
|
+
* it rather than rendering a replacement.
|
|
28
|
+
*
|
|
29
|
+
* **The way out is here, not somewhere else.** The settings panel's open state
|
|
30
|
+
* is component-local to `SettingsRoot`, so no plugin can navigate to a section
|
|
31
|
+
* — and a button that names a screen it cannot open is worse than no button.
|
|
32
|
+
* So the fix is offered inline: the same provider-and-model picker the Role
|
|
33
|
+
* Bindings screen uses, writing through the same store, so the two surfaces
|
|
34
|
+
* cannot disagree about what a provider offers.
|
|
35
|
+
*
|
|
36
|
+
* @module @deepwatch/dsh-client-settings/chat-gate
|
|
37
|
+
*/
|
|
38
|
+
import { useEffect, useState, useSyncExternalStore } from 'react';
|
|
39
|
+
import { PRIMARY_ROLE, ROLE_LABEL, cardForBlocker, isExecutable } from '@deepwatch/dsh-contracts';
|
|
40
|
+
import { StatusChip } from './components.js';
|
|
41
|
+
import { BindingEditor, CONTROL } from './role-bindings.js';
|
|
42
|
+
import { chatReadiness } from './binding-state.js';
|
|
43
|
+
/**
|
|
44
|
+
* The placeholder an inert composer carries.
|
|
45
|
+
*
|
|
46
|
+
* The blocker's own sentence, prefixed with the capability it is about. A
|
|
47
|
+
* placeholder reading only "Not configured" leaves a person guessing which of
|
|
48
|
+
* six things is missing, which is the failure the ordered blocker list exists
|
|
49
|
+
* to prevent.
|
|
50
|
+
*/
|
|
51
|
+
export function blockReason(detail) {
|
|
52
|
+
return `${ROLE_LABEL[PRIMARY_ROLE]} is not ready — ${detail}`;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The block this snapshot calls for, or undefined when the composer may open.
|
|
56
|
+
*
|
|
57
|
+
* A pure function rather than a branch inside the effect, so the decision can
|
|
58
|
+
* be tested as the decision. A test that re-derived the same condition beside
|
|
59
|
+
* the component would agree with itself and prove nothing about what a person
|
|
60
|
+
* gets.
|
|
61
|
+
*
|
|
62
|
+
* `idle` and `loading` deliberately produce nothing. Blocking a composer
|
|
63
|
+
* because an answer has not arrived yet would make every reload look like a
|
|
64
|
+
* misconfiguration — and the Host refuses an unbound route regardless, so the
|
|
65
|
+
* safe direction here is to say nothing until something is known.
|
|
66
|
+
*
|
|
67
|
+
* @param snapshot - what the store currently knows.
|
|
68
|
+
* @returns the block to raise, or undefined to lift any standing one.
|
|
69
|
+
*/
|
|
70
|
+
export function blockFor(snapshot) {
|
|
71
|
+
if (snapshot.status !== 'ready')
|
|
72
|
+
return undefined;
|
|
73
|
+
const chat = chatReadiness(snapshot);
|
|
74
|
+
if (chat === null || isExecutable(chat) || chat.primaryBlocker === null)
|
|
75
|
+
return undefined;
|
|
76
|
+
return { reason: blockReason(cardForBlocker(chat.primaryBlocker).detail) };
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Raise or clear this session's composer block, and offer the way out.
|
|
80
|
+
*
|
|
81
|
+
* @param props - see {@link ChatGateProps}.
|
|
82
|
+
* @returns the setup card while Chat cannot run, and nothing once it can.
|
|
83
|
+
*/
|
|
84
|
+
export function ChatGate({ sessionId, store, blocks }) {
|
|
85
|
+
const snapshot = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
|
|
86
|
+
const [open, setOpen] = useState(false);
|
|
87
|
+
// One load per mounted gate, not one per render. The store guards against a
|
|
88
|
+
// slow answer landing after a newer one, so a second session mounting while
|
|
89
|
+
// the first is loading does not produce a stale snapshot.
|
|
90
|
+
useEffect(() => { void store.load(); }, [store]);
|
|
91
|
+
const chat = chatReadiness(snapshot);
|
|
92
|
+
const ready = chat !== null && isExecutable(chat);
|
|
93
|
+
const card = chat === null || chat.primaryBlocker === null
|
|
94
|
+
? null
|
|
95
|
+
: cardForBlocker(chat.primaryBlocker);
|
|
96
|
+
// The same decision function the tests assert on, so what a person gets and
|
|
97
|
+
// what is asserted cannot drift apart.
|
|
98
|
+
const block = blockFor(snapshot);
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
const registry = blocks();
|
|
101
|
+
if (registry === undefined)
|
|
102
|
+
return;
|
|
103
|
+
registry.set(sessionId, block);
|
|
104
|
+
// Clearing on unmount matters: a session whose gate is gone must not keep
|
|
105
|
+
// a block nothing is left to lift.
|
|
106
|
+
return () => { registry.set(sessionId, undefined); };
|
|
107
|
+
}, [blocks, sessionId, block?.reason]);
|
|
108
|
+
if (ready || card === null || chat === null)
|
|
109
|
+
return null;
|
|
110
|
+
return (_jsxs("section", { "aria-label": `${ROLE_LABEL[PRIMARY_ROLE]} setup`, style: {
|
|
111
|
+
border: '1px solid color-mix(in srgb, var(--watch-accent) 68%, var(--dsw-alias-border-l2))',
|
|
112
|
+
borderRadius: '14px',
|
|
113
|
+
padding: '15px 17px',
|
|
114
|
+
margin: '0 0 8px',
|
|
115
|
+
background: 'linear-gradient(145deg, color-mix(in srgb, var(--watch-accent) 7%, var(--dsw-alias-bg-layer-2)), var(--dsw-alias-bg-base))',
|
|
116
|
+
boxShadow: '0 10px 30px color-mix(in srgb, var(--watch-accent) 9%, transparent)',
|
|
117
|
+
}, children: [_jsxs("div", { style: {
|
|
118
|
+
display: 'flex', alignItems: 'baseline', gap: '10px',
|
|
119
|
+
justifyContent: 'space-between', flexWrap: 'wrap',
|
|
120
|
+
}, children: [_jsx("h3", { style: { fontSize: '13px', fontWeight: 600, margin: 0 }, children: card.title }), _jsx(StatusChip, { tone: "neutral", children: chat.status === 'bound_unverified' ? 'Configured · not tested' : 'Not ready' })] }), _jsx("p", { style: {
|
|
121
|
+
fontSize: '12px', lineHeight: 1.55, margin: '6px 0 0',
|
|
122
|
+
color: 'var(--dsw-alias-label-secondary)',
|
|
123
|
+
}, children: card.detail }), open
|
|
124
|
+
? (_jsx(BindingEditor, { row: {
|
|
125
|
+
role: PRIMARY_ROLE,
|
|
126
|
+
provider: null,
|
|
127
|
+
model: null,
|
|
128
|
+
readiness: chat,
|
|
129
|
+
}, providers: snapshot.providers, saving: snapshot.saving, onBind: (provider, model) => {
|
|
130
|
+
void store.bind(PRIMARY_ROLE, provider, model).then(() => { setOpen(false); });
|
|
131
|
+
}, onCancel: () => { setOpen(false); } }))
|
|
132
|
+
: (_jsxs("div", { style: CONTROL.row, children: [_jsx("button", { type: "button", style: CONTROL.primary, disabled: !snapshot.writable || snapshot.testingRole !== null, "aria-describedby": snapshot.writable ? undefined : 'watch-chat-gate-readonly', onClick: () => {
|
|
133
|
+
if (chat.primaryBlocker === 'provider_untested')
|
|
134
|
+
void store.testRole(PRIMARY_ROLE);
|
|
135
|
+
else
|
|
136
|
+
setOpen(true);
|
|
137
|
+
}, children: snapshot.testingRole === PRIMARY_ROLE ? 'Testing provider…' : card.action }), snapshot.writable
|
|
138
|
+
? null
|
|
139
|
+
: (_jsx("span", { id: "watch-chat-gate-readonly", style: { fontSize: '12px', color: 'var(--dsw-alias-label-tertiary)' }, children: "This Workspace cannot change settings here, so Chat cannot be bound from this screen. What is shown is still read from the running system." })), _jsx("span", { style: { fontSize: '12px', color: 'var(--dsw-alias-label-tertiary)' }, children: "Settings \u2192 Role Bindings has the full view." })] }))] }));
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=chat-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat-gate.js","sourceRoot":"","sources":["../../src/client/chat-gate.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Technology & Capability Center.
|
|
3
|
+
*
|
|
4
|
+
* Seven surfaces that answer one question each, and one rule that governs all
|
|
5
|
+
* of them: **nothing here may claim a capability works.** Every status shown is
|
|
6
|
+
* either read from a descriptor that says what was actually established, or
|
|
7
|
+
* labelled as not established. A settings page that flatters the installation
|
|
8
|
+
* is worse than no settings page, because it is the screen a person checks
|
|
9
|
+
* before trusting a result.
|
|
10
|
+
*
|
|
11
|
+
* That is why there is no "Ready" anywhere that is not backed by
|
|
12
|
+
* `machine_tested`, and no accuracy or speed figure at all for an engine that
|
|
13
|
+
* has not run on this machine.
|
|
14
|
+
*
|
|
15
|
+
* @module @deepwatch/dsh-client-settings/components
|
|
16
|
+
*/
|
|
17
|
+
import type { ReactNode } from 'react';
|
|
18
|
+
import type { CoreHealthReport } from '@deepwatch/dsh-contracts/query/wire';
|
|
19
|
+
import type { BrandTone } from '@deepwatch/dsh-client-brand';
|
|
20
|
+
import type { RoleRow } from './binding-state.js';
|
|
21
|
+
/** What a settings section is handed by DSH. */
|
|
22
|
+
export interface SectionProps {
|
|
23
|
+
readonly close?: () => void;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The panel vocabulary every surface here shares.
|
|
27
|
+
*
|
|
28
|
+
* Exported because Role Bindings moved into its own file once it stopped being
|
|
29
|
+
* static copy, and two settings screens with independently-invented padding is
|
|
30
|
+
* how a panel starts looking like two products.
|
|
31
|
+
*/
|
|
32
|
+
export declare const T: {
|
|
33
|
+
page: {
|
|
34
|
+
padding: string;
|
|
35
|
+
maxWidth: string;
|
|
36
|
+
background: string;
|
|
37
|
+
};
|
|
38
|
+
lead: {
|
|
39
|
+
fontSize: string;
|
|
40
|
+
lineHeight: number;
|
|
41
|
+
color: string;
|
|
42
|
+
margin: string;
|
|
43
|
+
maxWidth: string;
|
|
44
|
+
};
|
|
45
|
+
card: {
|
|
46
|
+
border: string;
|
|
47
|
+
borderRadius: string;
|
|
48
|
+
padding: string;
|
|
49
|
+
marginBottom: string;
|
|
50
|
+
background: string;
|
|
51
|
+
boxShadow: string;
|
|
52
|
+
};
|
|
53
|
+
cardHead: {
|
|
54
|
+
display: string;
|
|
55
|
+
alignItems: string;
|
|
56
|
+
gap: string;
|
|
57
|
+
justifyContent: string;
|
|
58
|
+
flexWrap: "wrap";
|
|
59
|
+
};
|
|
60
|
+
title: {
|
|
61
|
+
fontSize: string;
|
|
62
|
+
fontWeight: number;
|
|
63
|
+
margin: number;
|
|
64
|
+
letterSpacing: string;
|
|
65
|
+
};
|
|
66
|
+
meta: {
|
|
67
|
+
display: string;
|
|
68
|
+
gridTemplateColumns: string;
|
|
69
|
+
columnGap: string;
|
|
70
|
+
rowGap: string;
|
|
71
|
+
fontSize: string;
|
|
72
|
+
marginTop: string;
|
|
73
|
+
};
|
|
74
|
+
key: {
|
|
75
|
+
color: string;
|
|
76
|
+
};
|
|
77
|
+
value: {
|
|
78
|
+
color: string;
|
|
79
|
+
};
|
|
80
|
+
note: {
|
|
81
|
+
fontSize: string;
|
|
82
|
+
lineHeight: number;
|
|
83
|
+
color: string;
|
|
84
|
+
borderInlineStart: string;
|
|
85
|
+
paddingInlineStart: string;
|
|
86
|
+
margin: string;
|
|
87
|
+
};
|
|
88
|
+
h2: {
|
|
89
|
+
fontSize: string;
|
|
90
|
+
fontWeight: number;
|
|
91
|
+
letterSpacing: string;
|
|
92
|
+
textTransform: "uppercase";
|
|
93
|
+
color: string;
|
|
94
|
+
margin: string;
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* The tones a settings chip may use.
|
|
99
|
+
*
|
|
100
|
+
* `success` is deliberately not among them. Green is reserved for a VERIFIED
|
|
101
|
+
* verdict, and nothing on a settings page is a verdict — a configured
|
|
102
|
+
* capability reads as `active`, which is a different colour and a different
|
|
103
|
+
* claim.
|
|
104
|
+
*/
|
|
105
|
+
export type ChipTone = Exclude<BrandTone, 'success'>;
|
|
106
|
+
/**
|
|
107
|
+
* A status chip.
|
|
108
|
+
*
|
|
109
|
+
* The tone vocabulary is the brand's, and `success` is deliberately absent:
|
|
110
|
+
* nothing in a settings page is a verification verdict, so nothing here is
|
|
111
|
+
* allowed to be green. A capability that is genuinely working reads as
|
|
112
|
+
* `active`, which is a different colour and a different claim.
|
|
113
|
+
*/
|
|
114
|
+
export declare function StatusChip({ tone, children }: {
|
|
115
|
+
readonly tone: ChipTone;
|
|
116
|
+
readonly children: ReactNode;
|
|
117
|
+
}): ReactNode;
|
|
118
|
+
/**
|
|
119
|
+
* The empty state every surface here needs.
|
|
120
|
+
*
|
|
121
|
+
* A capability that is not configured must still render something a person can
|
|
122
|
+
* act on. A dead control that fails when clicked teaches people the product is
|
|
123
|
+
* broken; a sentence explaining what is missing and what would fix it does not.
|
|
124
|
+
*/
|
|
125
|
+
export declare function NotConfigured({ what, why, fix }: {
|
|
126
|
+
readonly what: string;
|
|
127
|
+
readonly why: string;
|
|
128
|
+
readonly fix: string;
|
|
129
|
+
}): ReactNode;
|
|
130
|
+
/**
|
|
131
|
+
* Perception Engines.
|
|
132
|
+
*
|
|
133
|
+
* An Engine Runtime is not a Provider Connection, and the distinction is the
|
|
134
|
+
* whole point of the screen: a provider is a credential and an endpoint, an
|
|
135
|
+
* engine is software that runs here. Presence on disk is not readiness, so the
|
|
136
|
+
* lifecycle state is shown verbatim rather than collapsed into a tick.
|
|
137
|
+
*
|
|
138
|
+
* No engine shows a quality or speed number. On a machine where nothing has
|
|
139
|
+
* been measured, every such number would be invented.
|
|
140
|
+
*/
|
|
141
|
+
export declare function EnginesSection(): ReactNode;
|
|
142
|
+
/**
|
|
143
|
+
* Sources & Devices.
|
|
144
|
+
*
|
|
145
|
+
* Nothing here asks the operating system for anything. A permission prompt on
|
|
146
|
+
* page load trains people to click Allow without reading, so a permission is
|
|
147
|
+
* requested when a capability is first used and not before — which is also why
|
|
148
|
+
* every row says when it would ask.
|
|
149
|
+
*/
|
|
150
|
+
export declare function SourcesSection(): ReactNode;
|
|
151
|
+
/**
|
|
152
|
+
* Memory & Retrieval.
|
|
153
|
+
*
|
|
154
|
+
* The encryption row is the one that matters. The ledger is a plain file with
|
|
155
|
+
* the profile's permissions, and saying so is the difference between a product
|
|
156
|
+
* a person can calibrate their trust against and one that misleads them about
|
|
157
|
+
* where their data sits.
|
|
158
|
+
*/
|
|
159
|
+
export declare function MemorySection(): ReactNode;
|
|
160
|
+
/**
|
|
161
|
+
* Verification.
|
|
162
|
+
*
|
|
163
|
+
* The screen leads with the distinction the whole product rests on, because
|
|
164
|
+
* this is where somebody configures how much proof they want and needs to know
|
|
165
|
+
* what a verdict does and does not mean.
|
|
166
|
+
*/
|
|
167
|
+
export declare function VerificationSection(): ReactNode;
|
|
168
|
+
/**
|
|
169
|
+
* Diagnostics. What is actually running, and what is not.
|
|
170
|
+
*
|
|
171
|
+
* The capability readiness list lives here rather than in the first-run notice.
|
|
172
|
+
* It needs the settings panel's width; the onboarding seat is 256 pixels wide,
|
|
173
|
+
* and putting this there once already spilled two thousand pixels out of a
|
|
174
|
+
* clipped sidebar column.
|
|
175
|
+
*/
|
|
176
|
+
export declare function DiagnosticsSection({ openSection, roles, health, reading, onRefresh }?: {
|
|
177
|
+
readonly openSection?: ((id: string) => void) | undefined;
|
|
178
|
+
/** Live role readiness, so this screen and Role Bindings agree. */
|
|
179
|
+
readonly roles?: readonly RoleRow[] | undefined;
|
|
180
|
+
/** The engine, as the Host last read it. Null when it could not be read. */
|
|
181
|
+
readonly health?: CoreHealthReport | null | undefined;
|
|
182
|
+
readonly reading?: boolean | undefined;
|
|
183
|
+
readonly onRefresh?: (() => void) | undefined;
|
|
184
|
+
}): ReactNode;
|
|
185
|
+
/**
|
|
186
|
+
* About.
|
|
187
|
+
*
|
|
188
|
+
* This is where the foundation becomes explicit. Watch is the product and
|
|
189
|
+
* DeepSeek Harness is what it is built on; both statements belong on the same
|
|
190
|
+
* screen, and the independence disclosure belongs beside them so the
|
|
191
|
+
* attribution cannot be read as an endorsement.
|
|
192
|
+
*/
|
|
193
|
+
export declare function AboutSection(): ReactNode;
|
|
194
|
+
//# sourceMappingURL=components.d.ts.map
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ATTRIBUTION, INDEPENDENCE, PRODUCT_NAME, WATCH_MARK_PNG, tokenFor } from '@deepwatch/dsh-client-brand';
|
|
3
|
+
// The `/descriptors` subpath, not the package root: the root re-exports the
|
|
4
|
+
// OCR worker, which imports `node:child_process` to supervise a real process.
|
|
5
|
+
// Correct on the host, fatal in a browser bundle.
|
|
6
|
+
import { OCR_ENGINES } from '@deepwatch/dsh-technology/descriptors';
|
|
7
|
+
import { ReadinessList } from './readiness.js';
|
|
8
|
+
import { OCR_BY_WORKLOAD, OCR_DEVICE, OCR_ENGINE, OCR_MEASURED } from '../ocr-measured.js';
|
|
9
|
+
/* ── shared presentation ────────────────────────────────────────────────── */
|
|
10
|
+
/**
|
|
11
|
+
* The panel vocabulary every surface here shares.
|
|
12
|
+
*
|
|
13
|
+
* Exported because Role Bindings moved into its own file once it stopped being
|
|
14
|
+
* static copy, and two settings screens with independently-invented padding is
|
|
15
|
+
* how a panel starts looking like two products.
|
|
16
|
+
*/
|
|
17
|
+
export const T = {
|
|
18
|
+
page: {
|
|
19
|
+
padding: '6px 4px 30px', maxWidth: '880px',
|
|
20
|
+
background: 'radial-gradient(circle at 0 0, color-mix(in srgb, var(--watch-accent) 5%, transparent), transparent 34%)',
|
|
21
|
+
},
|
|
22
|
+
lead: {
|
|
23
|
+
fontSize: '13.5px', lineHeight: 1.65,
|
|
24
|
+
color: 'var(--dsw-alias-label-secondary)', margin: '0 0 20px', maxWidth: '72ch',
|
|
25
|
+
},
|
|
26
|
+
card: {
|
|
27
|
+
border: '1px solid color-mix(in srgb, var(--watch-accent) 9%, var(--dsw-alias-border-l2))',
|
|
28
|
+
borderRadius: '14px',
|
|
29
|
+
padding: '16px 18px',
|
|
30
|
+
marginBottom: '12px',
|
|
31
|
+
background: 'linear-gradient(145deg, color-mix(in srgb, var(--watch-accent) 3%, var(--dsw-alias-bg-layer-2)), var(--dsw-alias-bg-base))',
|
|
32
|
+
boxShadow: '0 10px 28px color-mix(in srgb, black 8%, transparent)',
|
|
33
|
+
},
|
|
34
|
+
cardHead: {
|
|
35
|
+
display: 'flex', alignItems: 'baseline', gap: '10px',
|
|
36
|
+
justifyContent: 'space-between', flexWrap: 'wrap',
|
|
37
|
+
},
|
|
38
|
+
title: { fontSize: '14px', fontWeight: 620, margin: 0, letterSpacing: '-0.01em' },
|
|
39
|
+
meta: {
|
|
40
|
+
display: 'grid',
|
|
41
|
+
gridTemplateColumns: 'max-content 1fr',
|
|
42
|
+
columnGap: '14px', rowGap: '4px',
|
|
43
|
+
fontSize: '12px', marginTop: '10px',
|
|
44
|
+
},
|
|
45
|
+
key: { color: 'var(--dsw-alias-label-tertiary)' },
|
|
46
|
+
value: { color: 'var(--dsw-alias-label-secondary)' },
|
|
47
|
+
note: {
|
|
48
|
+
fontSize: '12px', lineHeight: 1.55,
|
|
49
|
+
color: 'var(--dsw-alias-label-tertiary)',
|
|
50
|
+
borderInlineStart: '2px solid var(--watch-accent)',
|
|
51
|
+
paddingInlineStart: '10px', margin: '14px 0 0',
|
|
52
|
+
},
|
|
53
|
+
h2: { fontSize: '11px', fontWeight: 700, letterSpacing: '0.11em', textTransform: 'uppercase', color: 'var(--watch-accent)', margin: '24px 0 9px' },
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* A status chip.
|
|
57
|
+
*
|
|
58
|
+
* The tone vocabulary is the brand's, and `success` is deliberately absent:
|
|
59
|
+
* nothing in a settings page is a verification verdict, so nothing here is
|
|
60
|
+
* allowed to be green. A capability that is genuinely working reads as
|
|
61
|
+
* `active`, which is a different colour and a different claim.
|
|
62
|
+
*/
|
|
63
|
+
export function StatusChip({ tone, children }) {
|
|
64
|
+
// `tokenFor` rather than a colour: a feature package asks for a tone and gets
|
|
65
|
+
// a custom property, which is what keeps the palette from being re-invented
|
|
66
|
+
// slightly differently in every panel and makes a theme change one edit.
|
|
67
|
+
const colour = tokenFor(tone);
|
|
68
|
+
return (_jsx("span", { style: {
|
|
69
|
+
display: 'inline-flex', alignItems: 'center', gap: '5px',
|
|
70
|
+
fontSize: '11px', lineHeight: 1.4, padding: '2px 8px',
|
|
71
|
+
borderRadius: '999px', whiteSpace: 'nowrap',
|
|
72
|
+
border: `1px solid color-mix(in srgb, ${colour} 60%, transparent)`, color: colour,
|
|
73
|
+
background: `color-mix(in srgb, ${colour} 8%, transparent)`,
|
|
74
|
+
}, children: children }));
|
|
75
|
+
}
|
|
76
|
+
/** A local/remote label, because where the data goes is a product fact. */
|
|
77
|
+
function Where({ local }) {
|
|
78
|
+
return _jsx(StatusChip, { tone: local ? 'active' : 'info', children: local ? 'Local' : 'Remote' });
|
|
79
|
+
}
|
|
80
|
+
function Row({ label, children }) {
|
|
81
|
+
return (_jsxs(_Fragment, { children: [_jsx("span", { style: T.key, children: label }), _jsx("span", { style: T.value, children: children })] }));
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The empty state every surface here needs.
|
|
85
|
+
*
|
|
86
|
+
* A capability that is not configured must still render something a person can
|
|
87
|
+
* act on. A dead control that fails when clicked teaches people the product is
|
|
88
|
+
* broken; a sentence explaining what is missing and what would fix it does not.
|
|
89
|
+
*/
|
|
90
|
+
export function NotConfigured({ what, why, fix }) {
|
|
91
|
+
return (_jsxs("div", { style: { ...T.card, borderStyle: 'dashed' }, children: [_jsxs("div", { style: T.cardHead, children: [_jsx("h3", { style: T.title, children: what }), _jsx(StatusChip, { tone: "neutral", children: "Not configured" })] }), _jsx("p", { style: { ...T.lead, margin: '8px 0 0' }, children: why }), _jsx("p", { style: { ...T.note, marginTop: '10px' }, children: fix })] }));
|
|
92
|
+
}
|
|
93
|
+
/* ── 2. Perception Engines ──────────────────────────────────────────────── */
|
|
94
|
+
/**
|
|
95
|
+
* Lifecycle words a person can act on, and the tone each earns.
|
|
96
|
+
*
|
|
97
|
+
* `not_tested` is the state every engine is in on a machine where no capability
|
|
98
|
+
* check has run, and it is what `untestedHealth` returns. It is deliberately
|
|
99
|
+
* not styled as an error: nothing is broken, nobody has looked.
|
|
100
|
+
*/
|
|
101
|
+
function lifecycleChip(state) {
|
|
102
|
+
const map = {
|
|
103
|
+
machine_tested: { tone: 'active', text: 'Machine tested' },
|
|
104
|
+
ready: { tone: 'active', text: 'Ready' },
|
|
105
|
+
probed: { tone: 'caution', text: 'Probed, not measured' },
|
|
106
|
+
installed: { tone: 'caution', text: 'Installed, not tested' },
|
|
107
|
+
discovered: { tone: 'neutral', text: 'Not tested' },
|
|
108
|
+
not_tested: { tone: 'neutral', text: 'Not tested' },
|
|
109
|
+
not_installed: { tone: 'neutral', text: 'Not installed' },
|
|
110
|
+
installing: { tone: 'caution', text: 'Installing' },
|
|
111
|
+
degraded: { tone: 'error', text: 'Degraded' },
|
|
112
|
+
unavailable: { tone: 'error', text: 'Unavailable' },
|
|
113
|
+
incompatible: { tone: 'error', text: 'Incompatible' },
|
|
114
|
+
disabled: { tone: 'neutral', text: 'Disabled' },
|
|
115
|
+
};
|
|
116
|
+
const entry = map[state] ?? { tone: 'neutral', text: state };
|
|
117
|
+
return _jsx(StatusChip, { tone: entry.tone, children: entry.text });
|
|
118
|
+
}
|
|
119
|
+
function gigabytes(bytes) {
|
|
120
|
+
if (bytes === null)
|
|
121
|
+
return 'unknown';
|
|
122
|
+
return `${(bytes / 1_000_000_000).toFixed(1)} GB`;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Perception Engines.
|
|
126
|
+
*
|
|
127
|
+
* An Engine Runtime is not a Provider Connection, and the distinction is the
|
|
128
|
+
* whole point of the screen: a provider is a credential and an endpoint, an
|
|
129
|
+
* engine is software that runs here. Presence on disk is not readiness, so the
|
|
130
|
+
* lifecycle state is shown verbatim rather than collapsed into a tick.
|
|
131
|
+
*
|
|
132
|
+
* No engine shows a quality or speed number. On a machine where nothing has
|
|
133
|
+
* been measured, every such number would be invented.
|
|
134
|
+
*/
|
|
135
|
+
export function EnginesSection() {
|
|
136
|
+
return (_jsxs("div", { style: T.page, children: [_jsx("p", { style: T.lead, children: "An engine runs on this machine; a provider is something you connect to. Presence on disk is not readiness, so each engine shows the furthest state actually reached. Nothing here shows an accuracy or speed figure: none has been measured on this hardware, and a figure that was not measured would be fiction." }), _jsx("h2", { style: T.h2, children: "OCR and layout" }), OCR_ENGINES.map((engine) => (_jsxs("div", { style: T.card, children: [_jsxs("div", { style: T.cardHead, children: [_jsx("h3", { style: T.title, children: engine.displayName }), _jsxs("span", { style: { display: 'flex', gap: '6px' }, children: [_jsx(Where, { local: engine.runtime !== 'remote' }), lifecycleChip('not_tested')] })] }), _jsxs("div", { style: T.meta, children: [_jsx(Row, { label: "Version", children: engine.version }), _jsx(Row, { label: "Runtime", children: engine.runtime }), _jsx(Row, { label: "Hardware", children: engine.hardware.gpu === 'required'
|
|
137
|
+
? `GPU required${engine.hardware.minVramGb === null ? '' : `, ${String(engine.hardware.minVramGb)} GB VRAM`}`
|
|
138
|
+
: engine.hardware.gpu === 'optional' ? 'GPU optional' : 'CPU only' }), _jsx(Row, { label: "Egress", children: engine.privacy.egress === 'none'
|
|
139
|
+
? 'Nothing leaves this machine'
|
|
140
|
+
: engine.privacy.egress === 'metadata_only' ? 'Metadata only' : 'Sends content off this machine' }), _jsx(Row, { label: "Offline", children: engine.privacy.worksOffline ? 'Works offline' : 'Requires network' }), _jsxs(Row, { label: "Install", children: [engine.install.method === 'bundled' ? 'Bundled' : engine.install.method, engine.install.downloadBytes === null ? '' : ` · ${gigabytes(engine.install.downloadBytes)} download`] }), _jsx(Row, { label: "Quality", children: OCR_MEASURED && engine.id.includes(OCR_ENGINE)
|
|
141
|
+
? 'Measured on this machine — see the table below.'
|
|
142
|
+
: 'Not measured on this machine' }), _jsx(Row, { label: "How it would be checked", children: engine.testMethod ?? engine.probeMethod ?? 'No check is defined for this engine' })] })] }, engine.id))), OCR_MEASURED
|
|
143
|
+
? (_jsxs(_Fragment, { children: [_jsx("h2", { style: T.h2, children: `Measured accuracy — ${OCR_ENGINE} on ${OCR_DEVICE}` }), _jsxs("div", { style: T.card, children: [_jsx("p", { style: { ...T.lead, margin: '0 0 10px' }, children: "A real run over a versioned ground-truth corpus, scored against thresholds committed before the benchmark existed. Reported per workload rather than as one average: an engine can be entirely fit for reading a settings panel and entirely unfit for reading grey-on-grey, and a single number hides both." }), _jsx("div", { style: T.meta, children: OCR_BY_WORKLOAD.map(row => (_jsxs(Row, { label: row.workload, children: [_jsx(StatusChip, { tone: row.passes ? 'active' : 'error', children: row.passes ? 'Qualified' : 'Not qualified' }), ` CER ${String(row.cer)} · word accuracy ${String(row.wordAccuracy)} `, `· invented words ${String(row.hallucination)} · ${String(row.samples)} sample(s)`] }, row.workload))) }), _jsx("p", { style: T.note, children: "Accuracy is not a GPU question and is measured here. GPU throughput is not, because there is no GPU on this machine \u2014 that remains externally unvalidated." })] })] }))
|
|
144
|
+
: null, _jsx("p", { style: T.note, children: "An install is never automatic. Where a download is required, its size, hardware requirement and licence are shown before anything is fetched \u2014 multi-gigabyte weights are not something a product should acquire because a page was opened." })] }));
|
|
145
|
+
}
|
|
146
|
+
/* ── 3. Sources & Devices ───────────────────────────────────────────────── */
|
|
147
|
+
const SOURCES = [
|
|
148
|
+
{ id: 'files', name: 'Files', purpose: 'Documents and recordings you open.', local: true, permission: 'Granted per file, when you pick one' },
|
|
149
|
+
{ id: 'video', name: 'Video', purpose: 'Recorded footage, indexed so a citation can point at a moment.', local: true, permission: 'Granted per file' },
|
|
150
|
+
{ id: 'browser', name: 'Browser', purpose: 'A supervised browser that acts and reports a receipt.', local: true, permission: 'No OS permission needed' },
|
|
151
|
+
{ id: 'screen', name: 'Screen', purpose: 'The whole display.', local: true, permission: 'Requested at first use' },
|
|
152
|
+
{ id: 'window', name: 'Window', purpose: 'One application window.', local: true, permission: 'Requested at first use' },
|
|
153
|
+
{ id: 'camera', name: 'Camera', purpose: 'Live visual input.', local: true, permission: 'Requested at first use' },
|
|
154
|
+
{ id: 'microphone', name: 'Microphone', purpose: 'Live audio input.', local: true, permission: 'Requested at first use' },
|
|
155
|
+
{ id: 'live', name: 'Live session', purpose: 'A continuous session over one or more of the above.', local: true, permission: 'Inherits its sources' },
|
|
156
|
+
];
|
|
157
|
+
/**
|
|
158
|
+
* Sources & Devices.
|
|
159
|
+
*
|
|
160
|
+
* Nothing here asks the operating system for anything. A permission prompt on
|
|
161
|
+
* page load trains people to click Allow without reading, so a permission is
|
|
162
|
+
* requested when a capability is first used and not before — which is also why
|
|
163
|
+
* every row says when it would ask.
|
|
164
|
+
*/
|
|
165
|
+
export function SourcesSection() {
|
|
166
|
+
return (_jsxs("div", { style: T.page, children: [_jsx("p", { style: T.lead, children: "Opening this page requests nothing. A permission is asked for when a capability is first used, because a prompt on load teaches people to allow without reading. Every source below is local: what it captures stays on this machine unless a separate media-upload consent is given." }), SOURCES.map(source => (_jsxs("div", { style: T.card, children: [_jsxs("div", { style: T.cardHead, children: [_jsx("h3", { style: T.title, children: source.name }), _jsxs("span", { style: { display: 'flex', gap: '6px' }, children: [_jsx(Where, { local: source.local }), _jsx(StatusChip, { tone: "neutral", children: "Not requested" })] })] }), _jsx("p", { style: { ...T.lead, margin: '6px 0 0' }, children: source.purpose }), _jsx("div", { style: T.meta, children: _jsx(Row, { label: "Permission", children: source.permission }) })] }, source.id)))] }));
|
|
167
|
+
}
|
|
168
|
+
/* ── 4. Memory & Retrieval ──────────────────────────────────────────────── */
|
|
169
|
+
const MEMORY_MODES = [
|
|
170
|
+
{ id: 'off', name: 'Off', detail: 'Nothing is written and nothing is recalled.' },
|
|
171
|
+
{ id: 'session_only', name: 'Session only', detail: 'Kept for this session and never reaches a later one.' },
|
|
172
|
+
{ id: 'local_personal', name: 'Local Personal', detail: 'A ledger on this machine, for this profile.' },
|
|
173
|
+
{ id: 'workspace_shared', name: 'Workspace Shared', detail: 'Knowledge and decisions are shared; personal taste is not.' },
|
|
174
|
+
];
|
|
175
|
+
/**
|
|
176
|
+
* Memory & Retrieval.
|
|
177
|
+
*
|
|
178
|
+
* The encryption row is the one that matters. The ledger is a plain file with
|
|
179
|
+
* the profile's permissions, and saying so is the difference between a product
|
|
180
|
+
* a person can calibrate their trust against and one that misleads them about
|
|
181
|
+
* where their data sits.
|
|
182
|
+
*/
|
|
183
|
+
export function MemorySection() {
|
|
184
|
+
return (_jsxs("div", { style: T.page, children: [_jsx("p", { style: T.lead, children: "Memory is a product capability, not a plugin. The ledger is the authority and every projection \u2014 taste, index, log \u2014 is rebuilt from it, which is what makes Forget remove a record rather than hide it." }), _jsx("h2", { style: T.h2, children: "Mode" }), MEMORY_MODES.map(mode => (_jsxs("div", { style: T.card, children: [_jsxs("div", { style: T.cardHead, children: [_jsx("h3", { style: T.title, children: mode.name }), mode.id === 'local_personal'
|
|
185
|
+
? _jsx(StatusChip, { tone: "active", children: "Selected in this profile" })
|
|
186
|
+
: _jsx(StatusChip, { tone: "neutral", children: "Available" })] }), _jsx("p", { style: { ...T.lead, margin: '6px 0 0' }, children: mode.detail })] }, mode.id))), _jsx("h2", { style: T.h2, children: "Storage" }), _jsxs("div", { style: T.card, children: [_jsxs("div", { style: T.meta, children: [_jsx(Row, { label: "Ledger", children: "An append-only event log on this machine" }), _jsx(Row, { label: "Projections", children: "Rebuilt from the ledger, never edited in place" }), _jsx(Row, { label: "Embeddings", children: "Unbound \u2014 see Role Bindings. Retrieval falls back to lexical matching." }), _jsx(Row, { label: "Retention", children: "Kept until forgotten. Forget writes a tombstone." }), _jsx(Row, { label: "Encryption at rest", children: _jsx(StatusChip, { tone: "caution", children: "Not encrypted" }) })] }), _jsx("p", { style: T.note, children: "The ledger is a plain file. It is created owner-only \u2014 no group, no others \u2014 on systems that enforce file permissions; Windows has no equivalent and the file inherits the folder it sits in. On Desktop it is intended to move behind the OS keychain; until that is implemented and tested, this page will keep saying it is not encrypted. Claiming otherwise would be the one thing a privacy setting must never do." })] })] }));
|
|
187
|
+
}
|
|
188
|
+
/* ── 5. Verification ────────────────────────────────────────────────────── */
|
|
189
|
+
/**
|
|
190
|
+
* Verification.
|
|
191
|
+
*
|
|
192
|
+
* The screen leads with the distinction the whole product rests on, because
|
|
193
|
+
* this is where somebody configures how much proof they want and needs to know
|
|
194
|
+
* what a verdict does and does not mean.
|
|
195
|
+
*/
|
|
196
|
+
export function VerificationSection() {
|
|
197
|
+
return (_jsxs("div", { style: T.page, children: [_jsxs("div", { style: { ...T.card, borderColor: 'var(--watch-accent)' }, children: [_jsx("h3", { style: T.title, children: "Agent completed \u2260 Verified" }), _jsx("p", { style: { ...T.lead, margin: '8px 0 0' }, children: "A tool returning without an error means the call finished. It does not mean the thing happened. Only a verification against the world produces a verdict, and only Watch Core produces one \u2014 no plugin, no client and no model can mint a verdict, by construction rather than by convention." })] }), _jsx("h2", { style: T.h2, children: "Verifier" }), _jsxs("div", { style: T.card, children: [_jsxs("div", { style: T.cardHead, children: [_jsx("h3", { style: T.title, children: "Deterministic checks" }), _jsxs("span", { style: { display: 'flex', gap: '6px' }, children: [_jsx(Where, { local: true }), _jsx(StatusChip, { tone: "active", children: "Available" })] })] }), _jsx("p", { style: { ...T.lead, margin: '6px 0 0' }, children: "Reads a row, an HTTP status, a file, an exit code. Needs no model, so it works offline and its result does not depend on a provider." })] }), _jsxs("div", { style: T.card, children: [_jsxs("div", { style: T.cardHead, children: [_jsx("h3", { style: T.title, children: "Model-assisted checks" }), _jsx(StatusChip, { tone: "neutral", children: "Verifier role not bound" })] }), _jsx("p", { style: { ...T.lead, margin: '6px 0 0' }, children: "For expectations a deterministic check cannot express. Bind the Verifier role to enable them; until then, expectations that need one return INCONCLUSIVE rather than a guess." })] }), _jsx("h2", { style: T.h2, children: "Verdicts" }), _jsxs("div", { style: T.card, children: [_jsxs("div", { style: T.meta, children: [_jsx(Row, { label: "VERIFIED", children: "Checked against the world, and it held." }), _jsx(Row, { label: "FAILED", children: "Checked, and it did not hold." }), _jsx(Row, { label: "UNVERIFIED", children: "Not checked. Not a failure \u2014 an absence." }), _jsx(Row, { label: "INCONCLUSIVE", children: "Checked, and the evidence did not settle it." })] }), _jsx("p", { style: T.note, children: "Green is reserved for VERIFIED and nothing else reaches it \u2014 not a high confidence, not a completed turn, not five checks out of six." })] })] }));
|
|
198
|
+
}
|
|
199
|
+
/* ── 6. Diagnostics ─────────────────────────────────────────────────────── */
|
|
200
|
+
/**
|
|
201
|
+
* What the engine is doing, in one chip.
|
|
202
|
+
*
|
|
203
|
+
* `connected` is the only state that gets the active tone. Everything else —
|
|
204
|
+
* including the mock backend, which is *working* and is still not the product
|
|
205
|
+
* — reads as a problem, because on this screen it is one.
|
|
206
|
+
*/
|
|
207
|
+
function CoreStateChip({ health, reading }) {
|
|
208
|
+
if (health === null || health === undefined) {
|
|
209
|
+
return (_jsx(StatusChip, { tone: "neutral", children: reading ? 'Reading…' : 'Could not be read' }));
|
|
210
|
+
}
|
|
211
|
+
if (health.blocker === 'connected')
|
|
212
|
+
return _jsx(StatusChip, { tone: "active", children: "Connected" });
|
|
213
|
+
if (health.isTestOnlyMock)
|
|
214
|
+
return _jsx(StatusChip, { tone: "caution", children: "Test-only mock" });
|
|
215
|
+
return _jsx(StatusChip, { tone: "caution", children: health.phase });
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Diagnostics. What is actually running, and what is not.
|
|
219
|
+
*
|
|
220
|
+
* The capability readiness list lives here rather than in the first-run notice.
|
|
221
|
+
* It needs the settings panel's width; the onboarding seat is 256 pixels wide,
|
|
222
|
+
* and putting this there once already spilled two thousand pixels out of a
|
|
223
|
+
* clipped sidebar column.
|
|
224
|
+
*/
|
|
225
|
+
export function DiagnosticsSection({ openSection, roles, health, reading, onRefresh } = {}) {
|
|
226
|
+
return (_jsxs("div", { style: T.page, children: [_jsx("p", { style: T.lead, children: "What this installation actually consists of. Where a value cannot be read from the running system it says so, rather than showing a plausible default." }), _jsx("h2", { style: T.h2, children: "Capability readiness" }), _jsx(ReadinessList, { openSection: openSection, roles: roles, health: health, reading: reading }), _jsx("h2", { style: T.h2, children: "Versions" }), _jsx("div", { style: T.card, children: _jsxs("div", { style: T.meta, children: [_jsx(Row, { label: "DeepWatch", children: "0.1.0" }), _jsx(Row, { label: "DeepSeek Harness", children: "0.1.1-rc.2" }), _jsx(Row, { label: "Watch Core", children: health === null || health === undefined || health.coreVersion === null
|
|
227
|
+
? _jsx(StatusChip, { tone: "neutral", children: "Not reported" })
|
|
228
|
+
: health.coreVersion }), _jsx(Row, { label: "Bridge protocol", children: health === null || health === undefined || health.protocolVersion === null
|
|
229
|
+
? _jsx(StatusChip, { tone: "neutral", children: "Not reported" })
|
|
230
|
+
: `${String(health.protocolVersion)} (Core supports ${health.protocolMin === null ? '?' : String(health.protocolMin)}-${health.protocolVersion === null ? '?' : String(health.protocolVersion)})` }), _jsx(Row, { label: "Memory store schema", children: "1" })] }) }), _jsx("h2", { style: T.h2, children: "Health" }), _jsxs("div", { style: T.card, children: [_jsxs("div", { style: T.meta, children: [_jsx(Row, { label: "Watch Core", children: _jsxs("span", { style: { display: 'inline-flex', alignItems: 'center', gap: '8px' }, children: [_jsx(CoreStateChip, { health: health, reading: reading === true }), onRefresh === undefined
|
|
231
|
+
? null
|
|
232
|
+
: (_jsx("button", { type: "button", onClick: onRefresh, disabled: reading === true, style: {
|
|
233
|
+
background: 'none', border: 'none', padding: 0,
|
|
234
|
+
font: 'inherit', fontSize: '12px', cursor: 'pointer',
|
|
235
|
+
color: tokenFor('info'), textDecoration: 'underline',
|
|
236
|
+
}, children: "Re-read" }))] }) }), _jsx(Row, { label: "Bridge transport", children: health === null || health === undefined
|
|
237
|
+
? _jsx(StatusChip, { tone: "neutral", children: "Not reported" })
|
|
238
|
+
: health.isTestOnlyMock
|
|
239
|
+
? _jsx(StatusChip, { tone: "caution", children: "Test-only mock backend" })
|
|
240
|
+
: health.transport === null
|
|
241
|
+
? _jsx(StatusChip, { tone: "neutral", children: "Not reported" })
|
|
242
|
+
: health.transport }), _jsx(Row, { label: "Contract", children: health === null || health === undefined
|
|
243
|
+
? _jsx(StatusChip, { tone: "neutral", children: "Not reported" })
|
|
244
|
+
: health.contractsMatch
|
|
245
|
+
? _jsx(StatusChip, { tone: "active", children: "Matches this build" })
|
|
246
|
+
: _jsx(StatusChip, { tone: "caution", children: health.contractDrift.length === 0
|
|
247
|
+
? 'Unverified'
|
|
248
|
+
: `Drifted: ${health.contractDrift.join(', ')}` }) }), _jsx(Row, { label: "Capabilities", children: health === null || health === undefined
|
|
249
|
+
? _jsx(StatusChip, { tone: "neutral", children: "Not reported" })
|
|
250
|
+
: `${String(health.capabilities.ready)} ready · `
|
|
251
|
+
+ `${String(health.capabilities.degraded)} degraded · `
|
|
252
|
+
+ `${String(health.capabilities.unavailable)} unavailable · `
|
|
253
|
+
+ `${String(health.capabilities.unknown)} unknown` }), _jsx(Row, { label: "Last handshake", children: health === null || health === undefined || health.lastHandshakeAt === null
|
|
254
|
+
? _jsx(StatusChip, { tone: "neutral", children: "Never" })
|
|
255
|
+
: health.lastHandshakeAt }), _jsx(Row, { label: "Engine starts", children: health === null || health === undefined
|
|
256
|
+
? _jsx(StatusChip, { tone: "neutral", children: "Not reported" })
|
|
257
|
+
: String(health.restartCount) }), health !== null && health !== undefined && health.blocker !== 'connected'
|
|
258
|
+
? (_jsx(Row, { label: "Blocker", children: _jsxs("span", { children: [_jsx(StatusChip, { tone: "caution", children: health.blocker }), health.fix === '' ? null : _jsxs("span", { style: T.note, children: [" ", health.fix] })] }) }))
|
|
259
|
+
: null, _jsx(Row, { label: "Offline", children: _jsx(StatusChip, { tone: "active", children: "Offline only" }) }), _jsx(Row, { label: "Media upload consent", children: _jsx(StatusChip, { tone: "neutral", children: "Not given" }) })] }), _jsx("p", { style: T.note, children: "Offline-only and media-upload consent are two separate settings on purpose. Holding a provider credential is not permission to upload a frame, a transcript or a screen capture, and no agent can change either from inside a session." })] })] }));
|
|
260
|
+
}
|
|
261
|
+
/* ── 7. About ───────────────────────────────────────────────────────────── */
|
|
262
|
+
/**
|
|
263
|
+
* About.
|
|
264
|
+
*
|
|
265
|
+
* This is where the foundation becomes explicit. Watch is the product and
|
|
266
|
+
* DeepSeek Harness is what it is built on; both statements belong on the same
|
|
267
|
+
* screen, and the independence disclosure belongs beside them so the
|
|
268
|
+
* attribution cannot be read as an endorsement.
|
|
269
|
+
*/
|
|
270
|
+
export function AboutSection() {
|
|
271
|
+
return (_jsxs("div", { style: T.page, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '14px', marginBottom: '4px' }, children: [_jsx("img", { src: WATCH_MARK_PNG, width: 44, height: 44, alt: "", "aria-hidden": "true", style: { width: '44px', height: '44px', objectFit: 'contain', flexShrink: 0 } }), _jsxs("div", { children: [_jsx("h3", { style: { ...T.title, fontSize: '19px' }, children: PRODUCT_NAME }), _jsx("p", { style: { ...T.lead, margin: '2px 0 0' }, children: "An agent that sees, remembers, and can prove what actually happened." })] })] }), _jsx("div", { style: T.card, children: _jsxs("div", { style: T.meta, children: [_jsx(Row, { label: "DeepWatch", children: "0.1.0" }), _jsx(Row, { label: "Watch Core", children: "Reported by the Bridge, not read from here" }), _jsx(Row, { label: "Built on", children: "DeepSeek Harness 0.1.1-rc.2" }), _jsx(Row, { label: "DSH commit", children: "b150a551b8d465e31e418e1b2eaf5e79bbb7d28e" })] }) }), _jsxs("div", { style: { ...T.card, borderColor: 'var(--watch-accent)' }, children: [_jsx("p", { style: { margin: 0, fontSize: '13px', lineHeight: 1.6 }, children: ATTRIBUTION }), _jsx("p", { style: { margin: '8px 0 0', fontSize: '13px', lineHeight: 1.6, color: 'var(--dsw-alias-label-secondary)' }, children: INDEPENDENCE })] }), _jsx("h2", { style: T.h2, children: "Licences" }), _jsx("div", { style: T.card, children: _jsxs("div", { style: T.meta, children: [_jsx(Row, { label: "DeepWatch", children: "MIT" }), _jsx(Row, { label: "DeepSeek Harness", children: "MIT, and its notice is carried unmodified" }), _jsx(Row, { label: "Third-party notices", children: "THIRD_PARTY_NOTICES.md, shipped with this distribution" }), _jsx(Row, { label: "Model weights", children: "Distributed with none. A code licence is not a weights licence." })] }) })] }));
|
|
272
|
+
}
|
|
273
|
+
//# sourceMappingURL=components.js.map
|