@rightkit/system-ui 0.0.0-stage → 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/CHANGELOG.md +5 -0
- package/LICENSE-APACHE +201 -0
- package/LICENSE-MIT +21 -0
- package/README.md +36 -2
- package/dist/activityLease.d.ts +1 -0
- package/dist/activityLease.js +20 -0
- package/dist/api.d.ts +1011 -0
- package/dist/api.js +296 -0
- package/dist/chart.d.ts +14 -0
- package/dist/chart.js +94 -0
- package/dist/components/Chart.d.ts +40 -0
- package/dist/components/Chart.js +109 -0
- package/dist/env.d.ts +60 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +28 -0
- package/dist/kept.d.ts +3 -0
- package/dist/kept.js +21 -0
- package/dist/lib/chatKind.d.ts +4 -0
- package/dist/lib/chatKind.js +15 -0
- package/dist/lib/legal.d.ts +26 -0
- package/dist/lib/legal.js +39 -0
- package/dist/lib/onboardingSettings.d.ts +2 -0
- package/dist/lib/onboardingSettings.js +17 -0
- package/dist/lib/qr.d.ts +9 -0
- package/dist/lib/qr.js +286 -0
- package/dist/lib/rk.d.ts +6 -0
- package/dist/lib/rk.js +30 -0
- package/dist/metrics.d.ts +24 -0
- package/dist/metrics.js +76 -0
- package/dist/onboarding/flow.d.ts +31 -0
- package/dist/onboarding/flow.js +81 -0
- package/dist/onboarding/gate.d.ts +8 -0
- package/dist/onboarding/gate.js +52 -0
- package/dist/styles.css +8 -0
- package/dist/views/Activity.d.ts +3 -0
- package/dist/views/Activity.js +111 -0
- package/dist/views/Apps.d.ts +1 -0
- package/dist/views/Apps.js +478 -0
- package/dist/views/Chat.d.ts +1 -0
- package/dist/views/Chat.js +255 -0
- package/dist/views/ChromeSnapshots.d.ts +6 -0
- package/dist/views/ChromeSnapshots.js +19 -0
- package/dist/views/Cleanup.d.ts +4 -0
- package/dist/views/Cleanup.js +291 -0
- package/dist/views/DriveHealth.d.ts +14 -0
- package/dist/views/DriveHealth.js +81 -0
- package/dist/views/Duplicates.d.ts +2 -0
- package/dist/views/Duplicates.js +99 -0
- package/dist/views/LauncherSettings.d.ts +63 -0
- package/dist/views/LauncherSettings.js +242 -0
- package/dist/views/NearbySettings.d.ts +117 -0
- package/dist/views/NearbySettings.js +237 -0
- package/dist/views/Storage.d.ts +9 -0
- package/dist/views/Storage.js +916 -0
- package/dist/views/activity.css +94 -0
- package/dist/views/apps.css +98 -0
- package/dist/views/chat.css +61 -0
- package/dist/views/cleanup.css +49 -0
- package/dist/views/health.css +17 -0
- package/dist/views/membrane.css +23 -0
- package/dist/views/monitor.css +99 -0
- package/dist/views/settings.css +176 -0
- package/dist/views/settingsKit.d.ts +14 -0
- package/dist/views/settingsKit.js +43 -0
- package/dist/views/storage.css +121 -0
- package/dist/views/storageEstimate.d.ts +6 -0
- package/dist/views/storageEstimate.js +7 -0
- package/dist/visible.d.ts +3 -0
- package/dist/visible.js +42 -0
- package/package.json +110 -3
package/dist/api.js
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import { invoke } from "@tauri-apps/api/core";
|
|
2
|
+
import { getPlatform } from "@rightkit/platform-ui";
|
|
3
|
+
import { formatBytes } from "@rightkit/settings-ui/core";
|
|
4
|
+
/** True in the Windows webview: the hub runs one page on both systems and a few strings differ. */
|
|
5
|
+
export const isWindows = getPlatform() === "windows";
|
|
6
|
+
export const api = {
|
|
7
|
+
status: () => invoke("status"),
|
|
8
|
+
processes: () => invoke("processes"),
|
|
9
|
+
scan: (path) => invoke("scan", { path: path ?? null }),
|
|
10
|
+
scanStatus: () => invoke("scan_status"),
|
|
11
|
+
lastScan: () => invoke("last_scan"),
|
|
12
|
+
growth: () => invoke("growth"),
|
|
13
|
+
children: (path) => invoke("children", { path }),
|
|
14
|
+
/** `minBytes` keeps only items larger than that many bytes. */
|
|
15
|
+
search: (query, limit, extensions, minBytes) => invoke("search", { query, limit: limit ?? null, extensions: extensions ?? null, minBytes: minBytes ?? null, changed: null }).then((r) => r.rows),
|
|
16
|
+
/** `search` with a modified-time range; says how many matches had no date. */
|
|
17
|
+
searchChanged: (query, minBytes, changed, limit) => invoke("search", { query, limit: limit ?? null, extensions: null, minBytes: minBytes ?? null, changed: changed ?? null }),
|
|
18
|
+
diskIndexStatus: () => invoke("disk_index_status"),
|
|
19
|
+
diskIndexSizes: (paths) => invoke("disk_index_sizes", { paths }),
|
|
20
|
+
apps: () => invoke("apps_list"),
|
|
21
|
+
appDetail: (path) => invoke("app_detail", { path }),
|
|
22
|
+
uninstall: (path, bundleId, items) => invoke("app_uninstall", { path, bundleId, items }),
|
|
23
|
+
processRows: () => invoke("process_rows"),
|
|
24
|
+
quit: (r) => invoke("process_quit", { key: r.key, pid: r.lead.pid, startTime: r.lead.start_time }),
|
|
25
|
+
forceQuit: (r) => invoke("process_force_quit", { key: r.key, pid: r.lead.pid, startTime: r.lead.start_time }),
|
|
26
|
+
/** Listening ports and open files of one process, read now. Nothing is sampled in the background. */
|
|
27
|
+
inspectProcess: (r) => invoke("process_inspect", { pid: r.lead.pid, startTime: r.lead.start_time }),
|
|
28
|
+
volumes: () => invoke("volumes"),
|
|
29
|
+
eject: (mount) => invoke("eject", { mount }),
|
|
30
|
+
/** Opens the Full Disk Access pane in System Settings. */
|
|
31
|
+
openFullDiskAccess: () => invoke("fda_request"),
|
|
32
|
+
fullDiskAccessStatus: () => invoke("fda_status"),
|
|
33
|
+
cleanupScan: () => invoke("cleanup_scan"),
|
|
34
|
+
/** The last saved findings, or null when none were saved. Never scans. */
|
|
35
|
+
cleanupCached: () => invoke("cleanup_cached"),
|
|
36
|
+
cleanupApply: (items) => invoke("cleanup_apply", {
|
|
37
|
+
items: items.map((f) => ({ rule_id: f.rule_id, path: f.path, dev: f.dev, ino: f.ino })),
|
|
38
|
+
}),
|
|
39
|
+
cleanupRestore: (id) => invoke("cleanup_restore", { id }),
|
|
40
|
+
reveal: (path) => invoke("reveal", { path }),
|
|
41
|
+
/** Device and inode of an item now; the file actions check them again before acting. */
|
|
42
|
+
fileIdentity: (path) => invoke("file_identity", { path }),
|
|
43
|
+
/** Opens a folder, or selects a file, in Finder. Read-only. */
|
|
44
|
+
finderOpen: (path) => invoke("finder_open", { path }),
|
|
45
|
+
/** The native folder picker; null when cancelled. */
|
|
46
|
+
fileChooseFolder: () => invoke("file_choose_folder"),
|
|
47
|
+
fileMovePlan: (path, id, destination) => invoke("file_move_plan", { path, dev: id.dev, ino: id.ino, destination }),
|
|
48
|
+
/** Renames within one drive; across drives, copies then trashes the original when copyAcrossVolumes is set. */
|
|
49
|
+
fileMove: (path, id, destination, copyAcrossVolumes) => invoke("file_move", { path, dev: id.dev, ino: id.ino, destination, copyAcrossVolumes }),
|
|
50
|
+
fileTrash: (path, id) => invoke("file_trash", { path, dev: id.dev, ino: id.ino }),
|
|
51
|
+
mediaCompressStatus: () => invoke("media_compress_status"),
|
|
52
|
+
mediaCompress: (path, options) => invoke("media_compress", { path, options }),
|
|
53
|
+
/** Facts the scan does not hold for one item: read-only, one path, never recursive. */
|
|
54
|
+
storageInspect: (path) => invoke("storage_inspect", { path }),
|
|
55
|
+
/** The raw bytes of one image for the inspector's preview; rejects with the reason when there is no preview. */
|
|
56
|
+
storagePreview: (path) => invoke("storage_preview", { path }),
|
|
57
|
+
/** Drive health for these mounts; samples smartctl only when ten minutes have passed. */
|
|
58
|
+
driveHealth: (mounts) => invoke("drive_health", { mounts }),
|
|
59
|
+
/** Read-only merge of the records each feature keeps; newest first, at most `limit` rows older than `before`. */
|
|
60
|
+
activityTimeline: (limit, before) => invoke("activity_timeline", { limit, before }),
|
|
61
|
+
homePath: () => invoke("home_path"),
|
|
62
|
+
duplicatesScan: (path) => invoke("duplicates_scan", { path }),
|
|
63
|
+
/** Moves each extra copy to the Trash after re-checking it; the kept copy is never touched. */
|
|
64
|
+
duplicatesTrash: (items) => invoke("duplicates_trash", { items }),
|
|
65
|
+
};
|
|
66
|
+
/** Decimal byte count ("1.5 GB"), or "—" when the value is unknown. */
|
|
67
|
+
export function bytes(n) {
|
|
68
|
+
return n == null ? "—" : formatBytes(n);
|
|
69
|
+
}
|
|
70
|
+
/** "136.3 GB more reclaimable by macOS" when it is at least 1 GB and 2% of the drive, else null. */
|
|
71
|
+
export function reclaimableNote(v) {
|
|
72
|
+
const r = v.reclaimable_bytes;
|
|
73
|
+
if (r == null || v.total_bytes == null || v.total_bytes <= 0)
|
|
74
|
+
return null;
|
|
75
|
+
return r >= 1e9 && r >= v.total_bytes * 0.02 ? `${bytes(r)} more reclaimable by macOS` : null;
|
|
76
|
+
}
|
|
77
|
+
/** Saved results older than this refresh in the background when their view opens. */
|
|
78
|
+
const STALE_SECS = 10 * 60;
|
|
79
|
+
/** True when a saved result (Unix seconds) is old enough to refresh. */
|
|
80
|
+
export function isStale(secs) {
|
|
81
|
+
return Date.now() / 1000 - secs > STALE_SECS;
|
|
82
|
+
}
|
|
83
|
+
/** "just now", "5 min ago", "2 h ago", or the date for anything older. */
|
|
84
|
+
export function ago(secs) {
|
|
85
|
+
const elapsed = Math.max(0, Date.now() / 1000 - secs);
|
|
86
|
+
if (elapsed < 90)
|
|
87
|
+
return "just now";
|
|
88
|
+
if (elapsed < 3600)
|
|
89
|
+
return `${Math.round(elapsed / 60)} min ago`;
|
|
90
|
+
if (elapsed < 86400)
|
|
91
|
+
return `${Math.round(elapsed / 3600)} h ago`;
|
|
92
|
+
return new Date(secs * 1000).toLocaleDateString(undefined, { month: "short", day: "numeric" });
|
|
93
|
+
}
|
|
94
|
+
/** Colour for a 0–1 share, matching the notch rings. */
|
|
95
|
+
export function tone(fraction) {
|
|
96
|
+
if (fraction >= 0.85)
|
|
97
|
+
return "var(--rk-bad)";
|
|
98
|
+
if (fraction >= 0.6)
|
|
99
|
+
return "var(--rk-warn)";
|
|
100
|
+
return "var(--rk-ok)";
|
|
101
|
+
}
|
|
102
|
+
export function signedBytes(n) {
|
|
103
|
+
return `${n < 0 ? "−" : "+"}${bytes(Math.abs(n))}`;
|
|
104
|
+
}
|
|
105
|
+
export const appsApi = {
|
|
106
|
+
cached: () => invoke("apps_cached"),
|
|
107
|
+
refresh: () => invoke("apps_refresh"),
|
|
108
|
+
summary: (path) => invoke("app_summary", { path }),
|
|
109
|
+
leftovers: (path) => invoke("app_leftovers", { path }),
|
|
110
|
+
/** Cached icons by path; the rest arrive as `apps-icon` events. */
|
|
111
|
+
icons: (paths) => invoke("app_icons", { paths }),
|
|
112
|
+
updatesCached: () => invoke("apps_updates_cached"),
|
|
113
|
+
updatesRefresh: (force) => invoke("apps_updates_refresh", { force }),
|
|
114
|
+
/** Returns "running", "opened" or "store". */
|
|
115
|
+
update: (path) => invoke("app_update", { path }),
|
|
116
|
+
/** Startup and login items, read only. Called when the Starts by itself section opens. */
|
|
117
|
+
startupItems: () => invoke("startup_items"),
|
|
118
|
+
};
|
|
119
|
+
export const shareApi = {
|
|
120
|
+
state: () => invoke("share_state"),
|
|
121
|
+
devices: () => invoke("share_devices"),
|
|
122
|
+
/** Send files and/or text to a device (by fingerprint). Resolves with the transfer id. */
|
|
123
|
+
send: (to, paths, text) => invoke("share_send", { to, paths, text }),
|
|
124
|
+
accept: (id) => invoke("share_accept", { id }),
|
|
125
|
+
decline: (id) => invoke("share_decline", { id }),
|
|
126
|
+
cancel: (id) => invoke("share_cancel", { id }),
|
|
127
|
+
dismiss: (id) => invoke("share_dismiss", { id }),
|
|
128
|
+
/**
|
|
129
|
+
* Events from the service: `share-devices` (ShareDevice[]), `share-incoming` (ShareIncoming),
|
|
130
|
+
* `share-incoming-resolved` (id), `share-progress` (ShareTransfer).
|
|
131
|
+
*/
|
|
132
|
+
};
|
|
133
|
+
// ---- devices (device link; the resident engine's `device.*` methods, hub/src-tauri/src/commands.rs) ----
|
|
134
|
+
/** Effect classes a paired device may request (`agent/effect.rs`, kebab-case). */
|
|
135
|
+
export const DEVICE_GRANT_CLASSES = ["reversible", "irreversible-destruction", "spend", "publication"];
|
|
136
|
+
/** The engine's refusal text is `code: message`; these codes mean the owner check wants a (new) PIN. */
|
|
137
|
+
export const OWNER_CHECK_CODES = /^(unlock_needs_auth|pin_not_set|pin_wrong|pin_locked|pin_invalid): (.*)$/s;
|
|
138
|
+
export const devicesApi = {
|
|
139
|
+
list: () => invoke("device_list"),
|
|
140
|
+
pending: () => invoke("device_pair_pending"),
|
|
141
|
+
/** Opens a phone pairing. The engine checks the owner (`pin`, the the host app PIN) first. */
|
|
142
|
+
pairStart: (pin, proof) => invoke("device_pair_start", { pin: pin ?? null, challenge: proof?.challenge ?? null, sig: proof?.sig ?? null }),
|
|
143
|
+
/** Joins the computer whose owner made `token` (`pulse-join:1:...`); nothing else is asked. */
|
|
144
|
+
joinWith: (token) => invoke("device_join", { token }),
|
|
145
|
+
/** The engine checks the owner itself: `pin` is the the host app PIN. Without it the engine refuses `unlock_needs_auth`. */
|
|
146
|
+
pairApprove: (pairingId, pin, proof) => invoke("device_pair_approve", { pairingId, pin: pin ?? null, challenge: proof?.challenge ?? null, sig: proof?.sig ?? null }),
|
|
147
|
+
/** Grants or takes away a computer's `admit` (it may add devices for the circle); owner-checked like Add a device. */
|
|
148
|
+
admitSet: (deviceId, allowed, pin, proof) => invoke("device_admit_set", { deviceId, allowed, pin: pin ?? null, challenge: proof?.challenge ?? null, sig: proof?.sig ?? null }),
|
|
149
|
+
/** The enrolled owner-check keys (`remote.owner.key.status`; the Mac notch's and Windows Hello's). */
|
|
150
|
+
ownerKeys: () => invoke("owner_key_status"),
|
|
151
|
+
/** Confirms this computer's owner-check key (binds it to this device) with one owner check. Windows signs in the hub; the Mac's notch signs (`notch_command`, `signOwnerChallenge`, op `bind`). */
|
|
152
|
+
ownerKeyBind: () => invoke("owner_key_bind"),
|
|
153
|
+
/** Removes one owner-check key; the engine checks `pin`. */
|
|
154
|
+
ownerKeyRevoke: (keyId, pin) => invoke("owner_key_revoke", { keyId, pin }),
|
|
155
|
+
/** "Add a device": the engine checks `pin` (the the host app PIN) first, then makes one join command valid for ten minutes. */
|
|
156
|
+
joinCreate: (pin, proof) => invoke("device_join_create", { pin: pin ?? null, challenge: proof?.challenge ?? null, sig: proof?.sig ?? null }),
|
|
157
|
+
/** "Make a guest link" (#492): the same owner check as Add a device, then one single-use link for a cloud box. */
|
|
158
|
+
guestCreate: (leaseMs, pin, proof) => invoke("device_guest_create", { pin: pin ?? null, challenge: proof?.challenge ?? null, sig: proof?.sig ?? null, leaseMs }),
|
|
159
|
+
guests: () => invoke("device_guest_list"),
|
|
160
|
+
/** Owner UI only: makes a pending cloud agent one of your machines until its lease ends. */
|
|
161
|
+
guestApprove: (deviceId) => invoke("device_guest_approve", { deviceId }),
|
|
162
|
+
/** Voids the open join command. */
|
|
163
|
+
joinCancel: () => invoke("device_join_cancel"),
|
|
164
|
+
pairReject: (pairingId) => invoke("device_pair_reject", { pairingId }),
|
|
165
|
+
revoke: (deviceId) => invoke("device_revoke", { deviceId }),
|
|
166
|
+
phoneAccess: () => invoke("device_phone_access"),
|
|
167
|
+
/** `read` allow|deny|default; `send` allow|ask|deny|default; omit one to leave it. */
|
|
168
|
+
phoneAccessSet: (deviceId, change) => invoke("device_phone_access_set", { deviceId, ...change }),
|
|
169
|
+
/** Off revokes the phone's approval key first (system prompt); rejects if cancelled, leaving admin on. */
|
|
170
|
+
phoneAdminSet: (deviceId, allow) => invoke("device_phone_admin_set", { deviceId, allow }),
|
|
171
|
+
phoneAsks: () => invoke("device_phone_asks"),
|
|
172
|
+
phoneAnswer: (askId, allow) => invoke("device_phone_answer", { askId, allow }),
|
|
173
|
+
computerName: () => invoke("device_computer_name"),
|
|
174
|
+
/** `revision` is the one the device was read at; `expiresAtMs` null means no expiry. */
|
|
175
|
+
grantsSet: (deviceId, revision, classes, expiresAtMs) => invoke("device_grants_set", { deviceId, revision, classes, expiresAtMs }),
|
|
176
|
+
};
|
|
177
|
+
/** The engine answers `remote.approvals.pending` with `{ items: [...] }` (engine/src/contract.rs); a bare list or `{ approvals }` is read too. */
|
|
178
|
+
export function pendingApprovals(answer) {
|
|
179
|
+
const wrapped = answer;
|
|
180
|
+
const list = Array.isArray(answer) ? answer : (wrapped?.items ?? wrapped?.approvals);
|
|
181
|
+
return Array.isArray(list) ? list.filter((a) => a && typeof a.id === "string") : [];
|
|
182
|
+
}
|
|
183
|
+
export const remoteApprovalsApi = {
|
|
184
|
+
pending: () => invoke("remote_approvals_pending"),
|
|
185
|
+
/** Approves or denies one pending request, bound to the item as displayed (the engine compares a digest of its fields). Owner UI only: the engine checks that the caller is the the host app hub. */
|
|
186
|
+
decide: (a, approve, pin) => invoke("remote_approval_decide", {
|
|
187
|
+
id: a.id,
|
|
188
|
+
approve,
|
|
189
|
+
pin: approve ? pin ?? "" : null,
|
|
190
|
+
kind: a.kind,
|
|
191
|
+
scope: a.scope,
|
|
192
|
+
origin: a.origin,
|
|
193
|
+
bind: a.bind,
|
|
194
|
+
summary: a.summary,
|
|
195
|
+
}),
|
|
196
|
+
};
|
|
197
|
+
/** The per-chat unlock: the the host app PIN and the owner's answer to an unlock request (`scope === "unlock"`). Never log or keep a PIN. */
|
|
198
|
+
export const remoteUnlockApi = {
|
|
199
|
+
pinStatus: () => invoke("remote_unlock_pin_status"),
|
|
200
|
+
/** `current` is needed only when a PIN is already set. */
|
|
201
|
+
pinSet: (pin, current) => invoke("remote_unlock_pin_set", { pin, current: current || null }),
|
|
202
|
+
/** Denies, or approves with the the host app PIN (the hub cannot show the system prompt). */
|
|
203
|
+
decide: (a, approve, pin) => invoke("remote_unlock_decide", {
|
|
204
|
+
args: {
|
|
205
|
+
id: a.id,
|
|
206
|
+
approve,
|
|
207
|
+
kind: a.kind,
|
|
208
|
+
scope: a.scope,
|
|
209
|
+
origin: a.origin,
|
|
210
|
+
bind: a.bind,
|
|
211
|
+
summary: a.summary,
|
|
212
|
+
pin: approve ? pin ?? "" : null,
|
|
213
|
+
},
|
|
214
|
+
}),
|
|
215
|
+
};
|
|
216
|
+
/** The rows a list call answers: a bare list, or the list under `items` or the given key. */
|
|
217
|
+
export function browserRows(answer, key) {
|
|
218
|
+
if (Array.isArray(answer))
|
|
219
|
+
return answer;
|
|
220
|
+
const wrapped = answer;
|
|
221
|
+
const list = wrapped?.items ?? (key ? wrapped?.[key] : undefined);
|
|
222
|
+
return Array.isArray(list) ? list : [];
|
|
223
|
+
}
|
|
224
|
+
export const browserRemoteApi = {
|
|
225
|
+
pending: (device) => invoke("browser_remote_pending", { device }),
|
|
226
|
+
/** A single-use nonce for one approval (valid for 60 seconds). */
|
|
227
|
+
challenge: (device, id) => invoke("browser_remote_challenge", { device, id }),
|
|
228
|
+
approve: (device, id, nonce) => invoke("browser_remote_approve", { device, id, nonce }),
|
|
229
|
+
deny: (device, id) => invoke("browser_remote_deny", { device, id }),
|
|
230
|
+
sessions: (device) => invoke("browser_remote_sessions", { device }),
|
|
231
|
+
frame: (device, session, afterSeq) => invoke("browser_remote_frame", { device, session, afterSeq }),
|
|
232
|
+
input: (device, session, events) => invoke("browser_remote_input", { device, session, events }),
|
|
233
|
+
/** Hands the session back to the agent. */
|
|
234
|
+
done: (device, session) => invoke("browser_remote_done", { device, session }),
|
|
235
|
+
};
|
|
236
|
+
/**
|
|
237
|
+
* The agent secure box (#345 logins, #346 virtual cards). The renderer sends only non-secret fields and the PIN;
|
|
238
|
+
* the username, password, card number, name, expiry and CVC are typed into a system dialog opened by the command
|
|
239
|
+
* and go straight to the engine, so no secret ever exists in this page.
|
|
240
|
+
*/
|
|
241
|
+
export const secureBoxApi = {
|
|
242
|
+
list: () => invoke("remote_secure_list"),
|
|
243
|
+
remove: (id) => invoke("remote_secure_remove", { id }),
|
|
244
|
+
saveLogin: (label, host, pin, withTotp = false) => invoke("remote_secure_save_login", { label, host, pin, withTotp }),
|
|
245
|
+
/** Adds or replaces a saved login's one-time code; the secret is typed into the system dialog. */
|
|
246
|
+
setTotp: (id, host, pin) => invoke("remote_secure_set_totp", { id, host, pin }),
|
|
247
|
+
saveCard: (c, pin) => invoke("remote_secure_save_card", { ...c, pin }),
|
|
248
|
+
};
|
|
249
|
+
/** The sentence the Devices page shows when the engine refuses pairing with `listen_off`. */
|
|
250
|
+
export const LISTEN_OFF_SENTENCE = "Turn on Settings > Devices > Accept device links (LAN only) on this computer first.";
|
|
251
|
+
/** The engine's `listen_off` refusal (engine/src/devicelink_host.rs, pair_start). The hub's engine link
|
|
252
|
+
* passes only the refusal's message, not its code, so the message text identifies it. */
|
|
253
|
+
export function isListenOff(e) {
|
|
254
|
+
const text = typeof e === "string" ? e : e instanceof Error ? e.message : String(e);
|
|
255
|
+
return text.startsWith("listen_off:") || text.includes("turn on Accept device links first");
|
|
256
|
+
}
|
|
257
|
+
/** The owner-only security settings (`PROTECTED_KEYS`, core/src/remote/settings.rs; listed in docs/settings.md).
|
|
258
|
+
* A `hub-commands` file never changes them: they go through the engine's owner check (`remote_setting_set`). */
|
|
259
|
+
const PROTECTED_KEYS = new Set([
|
|
260
|
+
"chatUnlock", "chatUnlockMinutes", "remoteBlockedApps", "agentActions", "agentCliPath", "agentBrowser", "agentBrowserSessionMinutes", "remoteRoots", "remoteGateway",
|
|
261
|
+
"remoteChatRead", "phoneChatReadAll", "remoteDocs", "remoteLaunch", "remoteMedia",
|
|
262
|
+
"remoteReadMaxBytes", "remoteFindMaxResults", "remoteChatReadMaxTurns", "remoteChatReadMaxBytes", "remoteImageMaxBytes", "remoteDocsMaxBytes",
|
|
263
|
+
"remoteFormFill", "remoteFormFillStanding", "remoteFormFillProfile", "remoteScreenObserve", "remoteScreenAct",
|
|
264
|
+
"remoteScreenDisclosure", "remoteTerminals", "remoteTermStanding", "remoteTermAllow", "remoteTermDeny",
|
|
265
|
+
"remoteClipboard", "remoteReadOnly", "ledgerTranscriptSearch", "ledgerTranscriptShare", "remoteFilesWrite", "remoteTools",
|
|
266
|
+
]);
|
|
267
|
+
/** `PROTECTED_PREFIXES` in core: every key under one of these is owner-only too. */
|
|
268
|
+
const PROTECTED_PREFIXES = ["remoteFormFill", "remoteScreen"];
|
|
269
|
+
export const isProtectedKey = (key) => PROTECTED_KEYS.has(key) || PROTECTED_PREFIXES.some((prefix) => key.startsWith(prefix));
|
|
270
|
+
let pinListener = null;
|
|
271
|
+
export function onProtectedPin(fn) {
|
|
272
|
+
pinListener = fn;
|
|
273
|
+
}
|
|
274
|
+
/** Sends one protected change to the engine. Without a PIN a change that loosens a gate is refused with
|
|
275
|
+
* `unlock_needs_auth`; that opens the PIN prompt (`ProtectedPinPrompt`). Never logs or keeps the PIN. */
|
|
276
|
+
export async function setProtectedSetting(key, value, pin) {
|
|
277
|
+
try {
|
|
278
|
+
await invoke("remote_setting_set", { key, value, pin: pin ? pin : null });
|
|
279
|
+
pinListener?.(null);
|
|
280
|
+
}
|
|
281
|
+
catch (e) {
|
|
282
|
+
const text = typeof e === "string" ? e : e instanceof Error ? e.message : String(e);
|
|
283
|
+
const m = /^(unlock_needs_auth|pin_not_set|pin_wrong|pin_locked|pin_invalid): (.*)$/s.exec(text);
|
|
284
|
+
if (m && pinListener)
|
|
285
|
+
pinListener({ key, value, problem: pin || m[1] !== "unlock_needs_auth" ? m[2] : null });
|
|
286
|
+
else
|
|
287
|
+
throw e;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
/** Sets one notch setting the way the Settings page does: `notch_command` with a `set` command
|
|
291
|
+
* (a protected key goes through the engine instead). */
|
|
292
|
+
export function setNotchSetting(key, value) {
|
|
293
|
+
if (isProtectedKey(key))
|
|
294
|
+
return setProtectedSetting(key, value);
|
|
295
|
+
return invoke("notch_command", { command: { command: "set", key, value } });
|
|
296
|
+
}
|
package/dist/chart.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type KindId = "apps" | "downloads" | "developer" | "caches" | "media" | "documents" | "appdata" | "system" | "mixed";
|
|
2
|
+
export declare const KINDS: Record<KindId, {
|
|
3
|
+
label: string;
|
|
4
|
+
color: string;
|
|
5
|
+
}>;
|
|
6
|
+
export declare function kindOf(path: string, isDir: boolean): KindId;
|
|
7
|
+
export interface Tile {
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
w: number;
|
|
11
|
+
h: number;
|
|
12
|
+
}
|
|
13
|
+
/** Squarified treemap. `values` must be positive and sorted largest first. */
|
|
14
|
+
export declare function squarify(values: number[], W: number, H: number): Tile[];
|
package/dist/chart.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// Kinds and the treemap layout for the Storage view.
|
|
2
|
+
//
|
|
3
|
+
// Adapted from Petal (https://github.com/henrydennis/petal, MIT; pinned in docs/donors.md):
|
|
4
|
+
// - the nine kinds and their colours are Petal's `src/classify/mod.rs` and
|
|
5
|
+
// `classify/model.rs`. Petal judges kind with a small trained model; this
|
|
6
|
+
// uses plain path rules instead, so a wrong guess only costs a wrong
|
|
7
|
+
// colour. It never decides what is safe to delete.
|
|
8
|
+
// - `squarify` is the squarified treemap (Bruls, Huizing and van Wijk) that
|
|
9
|
+
// Petal's `src/treemap.rs` lays tiles out with, rewritten for SVG.
|
|
10
|
+
export const KINDS = {
|
|
11
|
+
apps: { label: "Apps", color: "#d95926" },
|
|
12
|
+
downloads: { label: "Downloads & Trash", color: "#3987e5" },
|
|
13
|
+
developer: { label: "Developer files", color: "#008300" },
|
|
14
|
+
caches: { label: "Caches & logs", color: "#199e70" },
|
|
15
|
+
media: { label: "Photos, video & music", color: "#d55181" },
|
|
16
|
+
documents: { label: "Documents", color: "#c98500" },
|
|
17
|
+
appdata: { label: "App data & backups", color: "#9085e9" },
|
|
18
|
+
system: { label: "System", color: "#5b5e66" },
|
|
19
|
+
mixed: { label: "Other", color: "#6b6e78" },
|
|
20
|
+
};
|
|
21
|
+
const MEDIA_EXT = /\.(mov|mp4|m4v|mkv|avi|mp3|m4a|wav|flac|jpg|jpeg|png|heic|raw|cr2|dng|photoslibrary|musiclibrary|fcpbundle)$/i;
|
|
22
|
+
const DEV_NAMES = new Set([
|
|
23
|
+
"node_modules", "target", ".build", "DerivedData", ".git", ".cargo", ".rustup", ".npm", ".pnpm-store",
|
|
24
|
+
".gradle", ".nvm", "Developer", "Pods", "venv", ".venv", "dist",
|
|
25
|
+
]);
|
|
26
|
+
export function kindOf(path, isDir) {
|
|
27
|
+
const name = path.split("/").pop() ?? "";
|
|
28
|
+
const rel = path.replace(/^\/Users\/[^/]+/, "~");
|
|
29
|
+
if (rel.includes("/Library/Caches") || rel.includes("/Library/Logs") || name === ".cache" || name === "Caches")
|
|
30
|
+
return "caches";
|
|
31
|
+
if (rel.startsWith("/Applications") || /\.app$/.test(name))
|
|
32
|
+
return "apps";
|
|
33
|
+
if (DEV_NAMES.has(name) || rel.includes("/Library/Developer") || rel.includes("/node_modules/"))
|
|
34
|
+
return "developer";
|
|
35
|
+
if (rel.startsWith("~/Downloads") || rel.startsWith("~/.Trash"))
|
|
36
|
+
return "downloads";
|
|
37
|
+
if (MEDIA_EXT.test(name) || /^~\/(Movies|Music|Pictures)(\/|$)/.test(rel))
|
|
38
|
+
return "media";
|
|
39
|
+
if (/^~\/(Documents|Desktop)(\/|$)/.test(rel))
|
|
40
|
+
return "documents";
|
|
41
|
+
if (rel.startsWith("~/Library"))
|
|
42
|
+
return "appdata";
|
|
43
|
+
if (/^\/(System|private|usr|bin|sbin|var|opt|cores|Library)(\/|$)/.test(rel))
|
|
44
|
+
return "system";
|
|
45
|
+
return isDir ? "mixed" : "documents";
|
|
46
|
+
}
|
|
47
|
+
/** Squarified treemap. `values` must be positive and sorted largest first. */
|
|
48
|
+
export function squarify(values, W, H) {
|
|
49
|
+
const total = values.reduce((a, b) => a + b, 0);
|
|
50
|
+
const out = values.map(() => ({ x: 0, y: 0, w: 0, h: 0 }));
|
|
51
|
+
if (total <= 0)
|
|
52
|
+
return out;
|
|
53
|
+
const areas = values.map((v) => (v / total) * W * H);
|
|
54
|
+
const sum = (row) => row.reduce((a, b) => a + b, 0);
|
|
55
|
+
const worst = (row, side) => {
|
|
56
|
+
const s = sum(row);
|
|
57
|
+
return Math.max((side * side * Math.max(...row)) / (s * s), (s * s) / (side * side * Math.min(...row)));
|
|
58
|
+
};
|
|
59
|
+
let x = 0, y = 0, w = W, h = H, i = 0;
|
|
60
|
+
while (i < areas.length) {
|
|
61
|
+
const side = Math.min(w, h);
|
|
62
|
+
const row = [areas[i]];
|
|
63
|
+
let j = i + 1;
|
|
64
|
+
while (j < areas.length && worst([...row, areas[j]], side) <= worst(row, side)) {
|
|
65
|
+
row.push(areas[j]);
|
|
66
|
+
j++;
|
|
67
|
+
}
|
|
68
|
+
const s = sum(row);
|
|
69
|
+
if (w >= h) {
|
|
70
|
+
const cw = s / h;
|
|
71
|
+
let cy = y;
|
|
72
|
+
row.forEach((a, k) => {
|
|
73
|
+
const ch = a / cw;
|
|
74
|
+
out[i + k] = { x, y: cy, w: cw, h: ch };
|
|
75
|
+
cy += ch;
|
|
76
|
+
});
|
|
77
|
+
x += cw;
|
|
78
|
+
w -= cw;
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
const rh = s / w;
|
|
82
|
+
let cx = x;
|
|
83
|
+
row.forEach((a, k) => {
|
|
84
|
+
const cw = a / rh;
|
|
85
|
+
out[i + k] = { x: cx, y, w: cw, h: rh };
|
|
86
|
+
cx += cw;
|
|
87
|
+
});
|
|
88
|
+
y += rh;
|
|
89
|
+
h -= rh;
|
|
90
|
+
}
|
|
91
|
+
i = j;
|
|
92
|
+
}
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export interface Series {
|
|
2
|
+
label: string;
|
|
3
|
+
/** A CSS colour, normally a `var(--rk-…)`. */
|
|
4
|
+
color: string;
|
|
5
|
+
/** One value per time; null leaves a gap. */
|
|
6
|
+
values: (number | null)[];
|
|
7
|
+
dashed?: boolean;
|
|
8
|
+
}
|
|
9
|
+
/** Usage colour: below 70% ok, 70–90% warn, 90% and up bad. */
|
|
10
|
+
export declare function levelColor(percent: number): string;
|
|
11
|
+
/** A small inline line for a card. One or two series on a shared scale. */
|
|
12
|
+
export declare function Sparkline({ label, series, max, minTop, height, format, minutes, }: {
|
|
13
|
+
label: string;
|
|
14
|
+
series: Series[];
|
|
15
|
+
/** Top of the scale. Omitted: the largest value, rounded up. */
|
|
16
|
+
max?: number;
|
|
17
|
+
/** Smallest top of an automatic scale. */
|
|
18
|
+
minTop?: number;
|
|
19
|
+
height?: number;
|
|
20
|
+
format?: (v: number) => string;
|
|
21
|
+
/** Only for the accessible summary. */
|
|
22
|
+
minutes?: number;
|
|
23
|
+
}): import("react").JSX.Element;
|
|
24
|
+
/**
|
|
25
|
+
* Time on x (the last `rangeMs` ending at the newest sample), value on y.
|
|
26
|
+
* Hover shows a vertical rule with the time and each series' value.
|
|
27
|
+
*/
|
|
28
|
+
export declare function AreaChart({ label, times, series, rangeMs, max, minTop, format, height, }: {
|
|
29
|
+
label: string;
|
|
30
|
+
/** Sample times, oldest first; every series has one value per time. */
|
|
31
|
+
times: number[];
|
|
32
|
+
series: Series[];
|
|
33
|
+
rangeMs: number;
|
|
34
|
+
/** Fixed top of the scale (100 for percentages). Omitted: the largest value, rounded up. */
|
|
35
|
+
max?: number;
|
|
36
|
+
/** Smallest top of an automatic scale. */
|
|
37
|
+
minTop?: number;
|
|
38
|
+
format: (v: number) => string;
|
|
39
|
+
height?: number;
|
|
40
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
/** Usage colour: below 70% ok, 70–90% warn, 90% and up bad. */
|
|
4
|
+
export function levelColor(percent) {
|
|
5
|
+
if (percent >= 90)
|
|
6
|
+
return "var(--rk-bad)";
|
|
7
|
+
if (percent >= 70)
|
|
8
|
+
return "var(--rk-warn)";
|
|
9
|
+
return "var(--rk-ok)";
|
|
10
|
+
}
|
|
11
|
+
/** Contiguous runs of known points, so a gap in the data is a gap in the line. */
|
|
12
|
+
function runs(points) {
|
|
13
|
+
const out = [];
|
|
14
|
+
let run = [];
|
|
15
|
+
for (const p of points) {
|
|
16
|
+
if (p)
|
|
17
|
+
run.push(p);
|
|
18
|
+
else if (run.length) {
|
|
19
|
+
out.push(run);
|
|
20
|
+
run = [];
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
if (run.length)
|
|
24
|
+
out.push(run);
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
const fmt = (n) => n.toFixed(1);
|
|
28
|
+
const line = (run) => run.map(([x, y], i) => `${i ? "L" : "M"}${fmt(x)} ${fmt(y)}`).join("");
|
|
29
|
+
const area = (run, h) => `${line(run)}L${fmt(run[run.length - 1][0])} ${h}L${fmt(run[0][0])} ${h}Z`;
|
|
30
|
+
function niceMax(v) {
|
|
31
|
+
if (v <= 0)
|
|
32
|
+
return 1;
|
|
33
|
+
const pow = 10 ** Math.floor(Math.log10(v));
|
|
34
|
+
const f = v / pow;
|
|
35
|
+
return (f <= 1 ? 1 : f <= 2 ? 2 : f <= 5 ? 5 : 10) * pow;
|
|
36
|
+
}
|
|
37
|
+
function known(series) {
|
|
38
|
+
return series.flatMap((s) => s.values.filter((v) => v != null));
|
|
39
|
+
}
|
|
40
|
+
function summary(label, series, format, minutes) {
|
|
41
|
+
const first = series[0];
|
|
42
|
+
if (!first)
|
|
43
|
+
return `${label}: no data yet`;
|
|
44
|
+
const all = known(series);
|
|
45
|
+
if (all.length === 0)
|
|
46
|
+
return `${label}: no data yet`;
|
|
47
|
+
const parts = series.map((s) => {
|
|
48
|
+
const v = [...s.values].reverse().find((x) => x != null);
|
|
49
|
+
return v == null ? null : `${series.length > 1 ? `${s.label} ` : ""}${format(v)}`;
|
|
50
|
+
});
|
|
51
|
+
return `${label}: now ${parts.filter(Boolean).join(", ")}; over the last ${minutes} minutes from ${format(Math.min(...all))} to ${format(Math.max(...all))}`;
|
|
52
|
+
}
|
|
53
|
+
/** A small inline line for a card. One or two series on a shared scale. */
|
|
54
|
+
export function Sparkline({ label, series, max, minTop = 0, height = 28, format = (v) => `${Math.round(v)}`, minutes = 10, }) {
|
|
55
|
+
const W = 200;
|
|
56
|
+
const top = max ?? niceMax(Math.max(minTop, ...known(series)));
|
|
57
|
+
const n = Math.max(0, ...series.map((s) => s.values.length));
|
|
58
|
+
const x = (i) => (n <= 1 ? W : (i / (n - 1)) * W);
|
|
59
|
+
const y = (v) => 1.5 + (1 - Math.min(Math.max(v / top, 0), 1)) * (height - 3);
|
|
60
|
+
return (_jsx("svg", { className: "chart-spark", role: "img", "aria-label": summary(label, series, format, minutes), viewBox: `0 0 ${W} ${height}`, preserveAspectRatio: "none", style: { height }, children: series.map((s, si) => {
|
|
61
|
+
const pts = s.values.map((v, i) => (v == null ? null : [x(i), y(v)]));
|
|
62
|
+
return runs(pts).map((r, ri) => (_jsxs("g", { style: { color: s.color }, children: [si === 0 && r.length > 1 && _jsx("path", { className: "chart-fill", d: area(r, height) }), _jsx("path", { className: `chart-line${s.dashed ? " chart-dashed" : ""}`, d: line(r) })] }, `${si}-${ri}`)));
|
|
63
|
+
}) }));
|
|
64
|
+
}
|
|
65
|
+
const timeText = (t, seconds = true) => new Date(t).toLocaleTimeString(undefined, { hour: "numeric", minute: "2-digit", ...(seconds ? { second: "2-digit" } : {}) });
|
|
66
|
+
/**
|
|
67
|
+
* Time on x (the last `rangeMs` ending at the newest sample), value on y.
|
|
68
|
+
* Hover shows a vertical rule with the time and each series' value.
|
|
69
|
+
*/
|
|
70
|
+
export function AreaChart({ label, times, series, rangeMs, max, minTop = 0, format, height = 120, }) {
|
|
71
|
+
const [hover, setHover] = useState(null);
|
|
72
|
+
const W = 600;
|
|
73
|
+
const end = times.length ? times[times.length - 1] : 0;
|
|
74
|
+
const from = Math.max(0, times.findIndex((t) => t >= end - rangeMs));
|
|
75
|
+
const visible = times.slice(from);
|
|
76
|
+
// With less history than the range, the samples we have fill the whole width.
|
|
77
|
+
const short = times.length > 0 && times[0] > end - rangeMs;
|
|
78
|
+
const start = short ? times[0] : end - rangeMs;
|
|
79
|
+
const span = Math.max(end - start, 1);
|
|
80
|
+
const shown = series.map((s) => ({ ...s, values: s.values.slice(from) }));
|
|
81
|
+
const top = max ?? niceMax(Math.max(minTop, ...known(shown)));
|
|
82
|
+
const x = (t) => ((t - start) / span) * W;
|
|
83
|
+
const y = (v) => 1.5 + (1 - Math.min(Math.max(v / top, 0), 1)) * (height - 3);
|
|
84
|
+
const minutes = Math.round(rangeMs / 60_000);
|
|
85
|
+
const move = (e) => {
|
|
86
|
+
if (visible.length === 0)
|
|
87
|
+
return;
|
|
88
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
89
|
+
if (rect.width <= 0)
|
|
90
|
+
return;
|
|
91
|
+
const t = start + ((e.clientX - rect.left) / rect.width) * span;
|
|
92
|
+
let best = 0;
|
|
93
|
+
for (let i = 1; i < visible.length; i++) {
|
|
94
|
+
if (Math.abs(visible[i] - t) < Math.abs(visible[best] - t))
|
|
95
|
+
best = i;
|
|
96
|
+
}
|
|
97
|
+
setHover(best);
|
|
98
|
+
};
|
|
99
|
+
const at = hover != null && hover < visible.length ? hover : null;
|
|
100
|
+
const frac = at == null ? 0 : (visible[at] - start) / span;
|
|
101
|
+
const tipStyle = { left: `${frac * 100}%`, transform: frac > 0.6 ? "translateX(calc(-100% - 8px))" : "translateX(8px)" };
|
|
102
|
+
return (_jsxs("div", { className: "chart", children: [_jsxs("div", { className: "chart-row", children: [_jsxs("div", { className: "chart-yaxis", style: { height }, "aria-hidden": "true", children: [_jsx("span", { children: format(top) }), _jsx("span", { children: format(0) })] }), _jsxs("div", { className: "chart-plot", style: { height }, onPointerMove: move, onPointerLeave: () => setHover(null), onPointerCancel: () => setHover(null), children: [_jsxs("svg", { className: "chart-svg", role: "img", "aria-label": summary(label, shown, format, minutes), viewBox: `0 0 ${W} ${height}`, preserveAspectRatio: "none", children: [_jsx("line", { className: "chart-grid", x1: "0", x2: W, y1: y(top), y2: y(top) }), _jsx("line", { className: "chart-grid", x1: "0", x2: W, y1: y(top / 2), y2: y(top / 2) }), _jsx("line", { className: "chart-grid chart-base", x1: "0", x2: W, y1: height - 0.5, y2: height - 0.5 }), shown.map((s, si) => {
|
|
103
|
+
const pts = s.values.map((v, i) => (v == null ? null : [x(visible[i]), y(v)]));
|
|
104
|
+
return runs(pts).map((r, ri) => (_jsxs("g", { style: { color: s.color }, children: [si === 0 && r.length > 1 && _jsx("path", { className: "chart-fill", d: area(r, height) }), _jsx("path", { className: `chart-line${s.dashed ? " chart-dashed" : ""}`, d: line(r) })] }, `${si}-${ri}`)));
|
|
105
|
+
})] }), at != null && (_jsxs(_Fragment, { children: [_jsx("div", { className: "chart-rule", style: { left: `${frac * 100}%` }, "aria-hidden": "true" }), _jsxs("div", { className: "chart-tip", style: tipStyle, "aria-hidden": "true", children: [_jsx("span", { className: "chart-tip-time", children: timeText(visible[at]) }), shown.map((s) => {
|
|
106
|
+
const v = s.values[at];
|
|
107
|
+
return (_jsxs("span", { className: "chart-tip-row", children: [_jsx("i", { style: { background: s.color } }), s.label, ": ", v == null ? "no reading" : format(v)] }, s.label));
|
|
108
|
+
})] })] }))] })] }), _jsxs("div", { className: "chart-axis", "aria-hidden": "true", children: [_jsx("span", { children: short ? `since ${timeText(times[0], false)}` : `${minutes} min ago` }), _jsx("span", { children: "now" })] })] }));
|
|
109
|
+
}
|
package/dist/env.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
2
|
+
|
|
3
|
+
declare module "*.css";
|
|
4
|
+
declare module "*.svg" {
|
|
5
|
+
const src: string;
|
|
6
|
+
export default src;
|
|
7
|
+
}
|
|
8
|
+
declare module "*.png" {
|
|
9
|
+
const src: string;
|
|
10
|
+
export default src;
|
|
11
|
+
}
|
|
12
|
+
declare module "*.txt?raw" {
|
|
13
|
+
const text: string;
|
|
14
|
+
export default text;
|
|
15
|
+
}
|
|
16
|
+
declare module "*.tsv?raw" {
|
|
17
|
+
const text: string;
|
|
18
|
+
export default text;
|
|
19
|
+
}
|
|
20
|
+
declare module "*?url" {
|
|
21
|
+
const src: string;
|
|
22
|
+
export default src;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Build-time switches a host may define (Vite `define`); every read is typeof-guarded.
|
|
26
|
+
declare const __RIGHTKIT_QA__: boolean | undefined;
|
|
27
|
+
declare const __RIGHTKIT_DEMO__: boolean | undefined;
|
|
28
|
+
declare const __RIGHTKIT_MOCK__: boolean | undefined;
|
|
29
|
+
declare const __APP_VERSION__: string | undefined;
|
|
30
|
+
|
|
31
|
+
interface Window {
|
|
32
|
+
/** UI QA only: the data source a QA shell selects. */
|
|
33
|
+
__RIGHTKIT_QA_DATA_SOURCE__?: "engine";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Host Vite plugin module with the legal documents shown in the first-run gate.
|
|
37
|
+
declare module "virtual:rightkit-legal" {
|
|
38
|
+
export const LEGAL_DOCUMENTS: readonly { title: string; content: string }[];
|
|
39
|
+
export const LEGAL_VERSION: string;
|
|
40
|
+
export const LEGAL_DOCUMENTS_HASH: string;
|
|
41
|
+
export const LEGAL_RECEIPT_DOCUMENTS: readonly { id: string; role: string; version: string; sha256: string; treatment: string; material: boolean }[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
declare module "@rightkit-host/legal/legal-manifest.json" {
|
|
45
|
+
const manifest: {
|
|
46
|
+
documents: Array<{ id: string; role: string; title: string; publicUrl: string; version?: string; sha256?: string; [key: string]: unknown }>;
|
|
47
|
+
[key: string]: unknown;
|
|
48
|
+
};
|
|
49
|
+
export default manifest;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
declare module "@rightkit-host/legal/legal-manifest.json?raw" {
|
|
53
|
+
const text: string;
|
|
54
|
+
export default text;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
declare module "@rightkit-host/src/generated/legal-contract.json" {
|
|
58
|
+
const contract: { acceptanceVersion: string; materialSha256: string; [key: string]: unknown };
|
|
59
|
+
export default contract;
|
|
60
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export * from "./activityLease.js";
|
|
2
|
+
export * from "./api.js";
|
|
3
|
+
export * from "./chart.js";
|
|
4
|
+
export * from "./components/Chart.js";
|
|
5
|
+
export * from "./kept.js";
|
|
6
|
+
export * from "./lib/chatKind.js";
|
|
7
|
+
export * from "./lib/legal.js";
|
|
8
|
+
export * from "./lib/onboardingSettings.js";
|
|
9
|
+
export * from "./lib/qr.js";
|
|
10
|
+
export * from "./lib/rk.js";
|
|
11
|
+
export * from "./metrics.js";
|
|
12
|
+
export * from "./onboarding/flow.js";
|
|
13
|
+
export * from "./onboarding/gate.js";
|
|
14
|
+
export * from "./views/Activity.js";
|
|
15
|
+
export * from "./views/Apps.js";
|
|
16
|
+
export * from "./views/Chat.js";
|
|
17
|
+
export * from "./views/ChromeSnapshots.js";
|
|
18
|
+
export * from "./views/Cleanup.js";
|
|
19
|
+
export * from "./views/DriveHealth.js";
|
|
20
|
+
export * from "./views/Duplicates.js";
|
|
21
|
+
export * from "./views/LauncherSettings.js";
|
|
22
|
+
export * from "./views/NearbySettings.js";
|
|
23
|
+
export * from "./views/Storage.js";
|
|
24
|
+
export * from "./views/settingsKit.js";
|
|
25
|
+
export * from "./views/storageEstimate.js";
|
|
26
|
+
export * from "./visible.js";
|