dock-spreadsheet 0.0.0-stage → 0.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/LICENSE +21 -0
- package/README.en.md +108 -0
- package/README.md +107 -2
- package/assets/spreadsheet-compact.png +0 -0
- package/assets/spreadsheet-filter.png +0 -0
- package/assets/spreadsheet-viewer.png +0 -0
- package/cordis.patch.yml +3 -0
- package/lib/client.js +43208 -0
- package/lib/client.js.map +1 -0
- package/lib/index.js +146 -0
- package/lib/types/client/SpreadsheetView.d.ts +3 -0
- package/lib/types/client/contract.d.ts +291 -0
- package/lib/types/client/index.d.ts +3 -0
- package/lib/types/client/links.d.ts +26 -0
- package/lib/types/client/styles.d.ts +2 -0
- package/lib/types/client/tabulator-css.generated.d.ts +2 -0
- package/lib/types/client/workbook.d.ts +12 -0
- package/lib/types/index.d.ts +38 -0
- package/package.json +77 -4
package/lib/index.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { open } from "node:fs/promises";
|
|
2
|
+
import { extname, isAbsolute, resolve } from "node:path";
|
|
3
|
+
//#region src/index.ts
|
|
4
|
+
const name = "dock-spreadsheet";
|
|
5
|
+
/**
|
|
6
|
+
* Services required before mounting.
|
|
7
|
+
*
|
|
8
|
+
* The declared-authority list these routes fence with used to be injected here
|
|
9
|
+
* as `webRuntime`. DSH renamed that service to `webStartup` in 0.2.1, so the
|
|
10
|
+
* list is resolved lazily through `declaredTrustedHosts` instead: a renamed
|
|
11
|
+
* service can no longer park this plugin, and older hosts still mount.
|
|
12
|
+
*/
|
|
13
|
+
const inject = ["webServer"];
|
|
14
|
+
const MAX_FILE_BYTES = 20971520;
|
|
15
|
+
const MAX_REQUEST_BYTES = 8192;
|
|
16
|
+
const EXTENSIONS = /* @__PURE__ */ new Set([
|
|
17
|
+
".xlsx",
|
|
18
|
+
".xls",
|
|
19
|
+
".ods",
|
|
20
|
+
".csv",
|
|
21
|
+
".tsv"
|
|
22
|
+
]);
|
|
23
|
+
var ReadError = class extends Error {
|
|
24
|
+
status;
|
|
25
|
+
code;
|
|
26
|
+
constructor(status, code, message) {
|
|
27
|
+
super(message);
|
|
28
|
+
this.status = status;
|
|
29
|
+
this.code = code;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Authorities this deployment declared besides loopback.
|
|
34
|
+
*
|
|
35
|
+
* DSH publishes them as `trustedHosts` on `webStartup` (0.2.1+); `webRuntime`
|
|
36
|
+
* is the same service under its 0.1.x / 0.2.0 name, and a host that exposes
|
|
37
|
+
* only the connection RPC carries the list on `connection`. Probe all three
|
|
38
|
+
* lazily: a host with none of them keeps the loopback-only fence, and no
|
|
39
|
+
* rename can park this plugin on a missing service again.
|
|
40
|
+
*/
|
|
41
|
+
function declaredTrustedHosts(ctx) {
|
|
42
|
+
if (typeof ctx.get !== "function") return [];
|
|
43
|
+
for (const name of [
|
|
44
|
+
"webStartup",
|
|
45
|
+
"webRuntime",
|
|
46
|
+
"connection"
|
|
47
|
+
]) {
|
|
48
|
+
const declared = ctx.get(name)?.trustedHosts;
|
|
49
|
+
if (!Array.isArray(declared)) continue;
|
|
50
|
+
return declared.filter((entry) => typeof entry === "string");
|
|
51
|
+
}
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
function trusted(req, trustedHosts) {
|
|
55
|
+
const host = req.headers.host;
|
|
56
|
+
if (!host || req.headers["sec-fetch-site"] === "cross-site") return false;
|
|
57
|
+
let target;
|
|
58
|
+
try {
|
|
59
|
+
target = new URL(`http://${host}`);
|
|
60
|
+
} catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
const octets = target.hostname.split(".");
|
|
64
|
+
const loopback = target.hostname === "localhost" || target.hostname === "[::1]" || octets.length === 4 && octets[0] === "127" && octets.every((part) => /^\d{1,3}$/.test(part) && Number(part) <= 255);
|
|
65
|
+
const allowed = trustedHosts.some((entry) => {
|
|
66
|
+
try {
|
|
67
|
+
const configured = new URL(`http://${entry}`);
|
|
68
|
+
return configured.host === target.host || !configured.port && configured.hostname === target.hostname;
|
|
69
|
+
} catch {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
if (!loopback && !allowed) return false;
|
|
74
|
+
if (typeof req.headers.origin === "string") try {
|
|
75
|
+
if (new URL(req.headers.origin).host !== target.host) return false;
|
|
76
|
+
} catch {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
function fail(res, status, code, message) {
|
|
82
|
+
res.writeHead(status, {
|
|
83
|
+
"content-type": "application/json; charset=utf-8",
|
|
84
|
+
"cache-control": "no-store"
|
|
85
|
+
});
|
|
86
|
+
res.end(JSON.stringify({
|
|
87
|
+
ok: false,
|
|
88
|
+
error: {
|
|
89
|
+
code,
|
|
90
|
+
message
|
|
91
|
+
}
|
|
92
|
+
}));
|
|
93
|
+
}
|
|
94
|
+
async function requestPath(req) {
|
|
95
|
+
let body = "";
|
|
96
|
+
for await (const chunk of req) {
|
|
97
|
+
body += Buffer.from(chunk).toString("utf8");
|
|
98
|
+
if (Buffer.byteLength(body) > MAX_REQUEST_BYTES) throw new ReadError(413, "too-large", "request too large");
|
|
99
|
+
}
|
|
100
|
+
let path;
|
|
101
|
+
try {
|
|
102
|
+
path = JSON.parse(body).path;
|
|
103
|
+
} catch {
|
|
104
|
+
throw new ReadError(400, "bad-request", "invalid JSON request");
|
|
105
|
+
}
|
|
106
|
+
if (typeof path !== "string" || !isAbsolute(path) || !EXTENSIONS.has(extname(path).toLowerCase())) throw new ReadError(400, "bad-request", "absolute spreadsheet path required");
|
|
107
|
+
return resolve(path);
|
|
108
|
+
}
|
|
109
|
+
async function readSpreadsheet(path) {
|
|
110
|
+
const file = await open(path, "r");
|
|
111
|
+
try {
|
|
112
|
+
const info = await file.stat();
|
|
113
|
+
if (!info.isFile()) throw new ReadError(400, "bad-request", "not a regular file");
|
|
114
|
+
if (info.size > 20971520) throw new ReadError(413, "too-large", "spreadsheet exceeds 20 MiB limit");
|
|
115
|
+
const bytes = await file.readFile();
|
|
116
|
+
if (bytes.length > 20971520) throw new ReadError(413, "too-large", "spreadsheet exceeds 20 MiB limit");
|
|
117
|
+
return bytes;
|
|
118
|
+
} finally {
|
|
119
|
+
await file.close();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function apply(ctx) {
|
|
123
|
+
ctx.effect(() => ctx.webServer.register({
|
|
124
|
+
kind: "prefix",
|
|
125
|
+
path: "/dock-spreadsheet",
|
|
126
|
+
handler: async (req, res) => {
|
|
127
|
+
if (!trusted(req, declaredTrustedHosts(ctx))) return fail(res, 403, "forbidden", "forbidden");
|
|
128
|
+
if (req.method !== "POST" || new URL(req.url ?? "/", "http://dsh.internal").pathname !== "/dock-spreadsheet/read") return fail(res, 404, "not-found", "unknown route");
|
|
129
|
+
try {
|
|
130
|
+
const bytes = await readSpreadsheet(await requestPath(req));
|
|
131
|
+
res.writeHead(200, {
|
|
132
|
+
"content-type": "application/octet-stream",
|
|
133
|
+
"content-length": bytes.length,
|
|
134
|
+
"cache-control": "no-store",
|
|
135
|
+
"x-content-type-options": "nosniff"
|
|
136
|
+
});
|
|
137
|
+
res.end(bytes);
|
|
138
|
+
} catch (cause) {
|
|
139
|
+
if (cause instanceof ReadError) fail(res, cause.status, cause.code, cause.message);
|
|
140
|
+
else fail(res, 400, "fs-error", cause instanceof Error ? cause.message : "read failed");
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}), "dock-spreadsheet: read route");
|
|
144
|
+
}
|
|
145
|
+
//#endregion
|
|
146
|
+
export { MAX_FILE_BYTES, ReadError, apply, inject, name, readSpreadsheet };
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vendored copy of the dock workbench contract (AKS1st/dock, src/client/contract.ts).
|
|
3
|
+
*
|
|
4
|
+
* This plugin imports the contract types type-only; the file is copied here so
|
|
5
|
+
* the repo builds standalone without a package dependency on the dock base
|
|
6
|
+
* (`dock` is not published to npm; `link:` devDependencies would break clean
|
|
7
|
+
* clones). Keep this file in sync with the upstream contract when it changes.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Public contract of the dock base (client side).
|
|
11
|
+
*
|
|
12
|
+
* Feature plugins consume this contract via type-only import
|
|
13
|
+
* (`import type {} from './contract.ts'`), which also pulls
|
|
14
|
+
* in the `Context.workbench` augmentation below — the single restatement
|
|
15
|
+
* point, so plugins never re-declare the service on their own (purity gate
|
|
16
|
+
* friendly: type-only imports are erased at build time).
|
|
17
|
+
*
|
|
18
|
+
* Runtime collaboration happens exclusively through `ctx.workbench` method
|
|
19
|
+
* calls; value-importing another plugin's bundle is forbidden by the
|
|
20
|
+
* client-bundle purity convention.
|
|
21
|
+
*/
|
|
22
|
+
import type { ComponentType, ReactNode } from 'react';
|
|
23
|
+
/** The cordis context face the workbench hands to view components
|
|
24
|
+
* (structural subset; feature plugins may extend it locally). */
|
|
25
|
+
export interface WorkbenchContext {
|
|
26
|
+
effect(fn: () => void | (() => void), label?: string): void;
|
|
27
|
+
get<T = unknown>(name: string): T | undefined;
|
|
28
|
+
on(event: string, listener: (...args: unknown[]) => void): () => void;
|
|
29
|
+
provide<T>(name: string, value: T): void;
|
|
30
|
+
[name: string]: unknown;
|
|
31
|
+
}
|
|
32
|
+
/** Props every registered view component receives. */
|
|
33
|
+
export interface ViewProps {
|
|
34
|
+
ctx: WorkbenchContext;
|
|
35
|
+
/** The view's registered id (`'files'`, `'my-plugin:db'`). */
|
|
36
|
+
viewId: string;
|
|
37
|
+
/** The active DSH conversation id when one is known. */
|
|
38
|
+
sessionId?: string;
|
|
39
|
+
/** Whether this view is the one on screen (inactive views may pause polling). */
|
|
40
|
+
active: boolean;
|
|
41
|
+
/** The open seed this tab was opened with (file path, title, meta). */
|
|
42
|
+
seed?: unknown;
|
|
43
|
+
}
|
|
44
|
+
/** A view component, or a lazy factory resolving to one on first use. */
|
|
45
|
+
export type ViewComponent = ComponentType<ViewProps> | (() => Promise<ComponentType<ViewProps>>);
|
|
46
|
+
/**
|
|
47
|
+
* An SVG icon spec, rendered by the dock shell with `currentColor` so it
|
|
48
|
+
* follows the theme. `path` is a single SVG path `d` (fill style by
|
|
49
|
+
* default; `stroke: true` switches to lucide-style stroke rendering).
|
|
50
|
+
*/
|
|
51
|
+
export interface IconSpec {
|
|
52
|
+
/** SVG path `d` data (24×24 viewBox by default). */
|
|
53
|
+
path: string;
|
|
54
|
+
/** Rendered size in px; default 16. */
|
|
55
|
+
size?: number;
|
|
56
|
+
/** Override the viewBox (default '0 0 24 24'). */
|
|
57
|
+
viewBox?: string;
|
|
58
|
+
/** Stroke style instead of fill: stroke=currentColor, stroke-width 2, round caps/joins. */
|
|
59
|
+
stroke?: boolean;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* What a registration may pass as an icon: any React node (emoji, custom
|
|
63
|
+
* component) or an SVG spec (`{ path: 'M...' }`), rendered by the shell.
|
|
64
|
+
*/
|
|
65
|
+
export type IconRef = ReactNode | IconSpec;
|
|
66
|
+
/** Input handed to a view's `beforeClose` hook when one of its instances is about to close. */
|
|
67
|
+
export interface CloseRequest {
|
|
68
|
+
/** The view's registered id. */
|
|
69
|
+
viewId: string;
|
|
70
|
+
/** The instance being closed (shared vocabulary with editorTabs / floatingWindows). */
|
|
71
|
+
instanceId: string;
|
|
72
|
+
/** The instance's open seed — views read instance-level state (e.g. `seed.meta?.dirty`) here. */
|
|
73
|
+
seed?: EditorOpenSeed;
|
|
74
|
+
}
|
|
75
|
+
/** One view registered into a workbench region (side bar pane / editor area tab). */
|
|
76
|
+
export interface ViewDefinition {
|
|
77
|
+
/** Unique id; also the `viewId` handed to the component. */
|
|
78
|
+
id: string;
|
|
79
|
+
/** Title (i18n friendly: string or () => string). */
|
|
80
|
+
title: string | (() => string);
|
|
81
|
+
/** Icon shown in the activity bar / tab strip. */
|
|
82
|
+
icon?: IconRef;
|
|
83
|
+
/** Sort order (ascending); default 100. */
|
|
84
|
+
order?: number;
|
|
85
|
+
/** The component (or lazy factory). */
|
|
86
|
+
component: ViewComponent;
|
|
87
|
+
/**
|
|
88
|
+
* Close gate: called with the instance's `{ viewId, instanceId, seed }`
|
|
89
|
+
* before the shell removes its tab / floating window. Return `false` (or a
|
|
90
|
+
* promise resolving to `false`) to cancel the close; any other value lets
|
|
91
|
+
* it proceed. Views use this to confirm unsaved changes — typically read
|
|
92
|
+
* `seed.meta?.dirty` and `window.confirm(...)` themselves. The hook is
|
|
93
|
+
* definition-level (registered once with the view); per-instance state
|
|
94
|
+
* lives on the seed (see `updateViewSeed`).
|
|
95
|
+
*/
|
|
96
|
+
beforeClose?(instance: CloseRequest): boolean | Promise<boolean>;
|
|
97
|
+
}
|
|
98
|
+
/** One activity-bar item (the left vertical strip, VSCode style). */
|
|
99
|
+
export interface ActivityBarItemDefinition {
|
|
100
|
+
id: string;
|
|
101
|
+
pluginId?: string;
|
|
102
|
+
title: string;
|
|
103
|
+
icon: IconRef;
|
|
104
|
+
/** Sort order (ascending); default 100. */
|
|
105
|
+
order?: number;
|
|
106
|
+
/** The side bar pane to reveal when this item is activated. */
|
|
107
|
+
paneId: string;
|
|
108
|
+
}
|
|
109
|
+
/** One status-bar item (bottom strip, left/right groups in Phase 2). */
|
|
110
|
+
export interface StatusBarItemDefinition {
|
|
111
|
+
id: string;
|
|
112
|
+
/** Sort order (ascending); default 100. */
|
|
113
|
+
order?: number;
|
|
114
|
+
component: ComponentType<{
|
|
115
|
+
ctx: WorkbenchContext;
|
|
116
|
+
}>;
|
|
117
|
+
}
|
|
118
|
+
/** One command, invocable through `executeCommand` (keybindings in Phase 2). */
|
|
119
|
+
export interface CommandDefinition {
|
|
120
|
+
id: string;
|
|
121
|
+
title: string;
|
|
122
|
+
run: (...args: unknown[]) => unknown | Promise<unknown>;
|
|
123
|
+
}
|
|
124
|
+
/** The screen edge the workbench docks to. */
|
|
125
|
+
export type DockPosition = 'left' | 'right' | 'top' | 'bottom';
|
|
126
|
+
/** Minimum floating-window width in px (enforced by drag math and CSS). */
|
|
127
|
+
export declare const FLOATING_MIN_WIDTH = 240;
|
|
128
|
+
/** Minimum floating-window height in px (enforced by drag math and CSS). */
|
|
129
|
+
export declare const FLOATING_MIN_HEIGHT = 160;
|
|
130
|
+
/** Floating-window title bar height in px (`.dsh-wb-floating-head`); the
|
|
131
|
+
* viewport clamp keeps at least this strip reachable on screen. */
|
|
132
|
+
export declare const FLOATING_HEAD_HEIGHT = 34;
|
|
133
|
+
/**
|
|
134
|
+
* The workbench layout snapshot: which activity is active, whether the side
|
|
135
|
+
* bar is open, which editor views are open (tab strip), the dock
|
|
136
|
+
* configuration (edge / auto-hide), and the independent floating windows.
|
|
137
|
+
* The workbench always runs in dock mode (macOS-like floating bar); the
|
|
138
|
+
* embedded panel presentation was removed.
|
|
139
|
+
*/
|
|
140
|
+
export interface WorkbenchLayout {
|
|
141
|
+
/** Active activity-bar item id; null collapses the workbench to the strip. */
|
|
142
|
+
activity: string | null;
|
|
143
|
+
sideBarOpen: boolean;
|
|
144
|
+
/** Open editor view instances in the editor area (tab order). */
|
|
145
|
+
editorTabs: EditorTab[];
|
|
146
|
+
/** The focused editor instance id. */
|
|
147
|
+
activeEditorTab: string | null;
|
|
148
|
+
/** The screen edge this workbench docks to. */
|
|
149
|
+
dock: DockPosition;
|
|
150
|
+
/** Auto-hide behavior: 'off' (always visible) or 'edge' (hide when the mouse leaves). */
|
|
151
|
+
autoHide: 'off' | 'edge';
|
|
152
|
+
/** User-ordered activity items (drag-sorted; items not listed keep their registered order). */
|
|
153
|
+
activityOrder: string[];
|
|
154
|
+
/** Independent floating windows (viewId -> window). */
|
|
155
|
+
floatingWindows: Record<string, FloatingWindow>;
|
|
156
|
+
}
|
|
157
|
+
/** Payload an editor view carries to the component (file path, title, custom state). */
|
|
158
|
+
export interface EditorOpenSeed {
|
|
159
|
+
/** A file path the view loads (editor reads fs through its own route). */
|
|
160
|
+
path?: string;
|
|
161
|
+
/** Overrides the descriptor title (the editor tab shows the file name). */
|
|
162
|
+
title?: string;
|
|
163
|
+
/** JSON-serializable custom state carried on the tab (persisted across reloads). */
|
|
164
|
+
meta?: unknown;
|
|
165
|
+
}
|
|
166
|
+
/** One independent floating window (an open instance + geometry, persisted). */
|
|
167
|
+
export interface FloatingWindow {
|
|
168
|
+
/** The open instance id (shared vocabulary with editorTabs). */
|
|
169
|
+
instanceId: string;
|
|
170
|
+
viewId: string;
|
|
171
|
+
seed?: EditorOpenSeed;
|
|
172
|
+
x: number;
|
|
173
|
+
y: number;
|
|
174
|
+
width: number;
|
|
175
|
+
height: number;
|
|
176
|
+
}
|
|
177
|
+
/** One open editor instance (a view + its seed) hosted in the editor area. */
|
|
178
|
+
export interface EditorTab {
|
|
179
|
+
/** Unique instance id (stable across container moves, survives reloads). */
|
|
180
|
+
instanceId: string;
|
|
181
|
+
viewId: string;
|
|
182
|
+
seed?: EditorOpenSeed;
|
|
183
|
+
}
|
|
184
|
+
/** Options for opening a file path through the workbench (system entry). */
|
|
185
|
+
export interface OpenPathOptions {
|
|
186
|
+
/** Explicit title (defaults to the file name). */
|
|
187
|
+
title?: string;
|
|
188
|
+
/** Target editor view id (defaults to the registered default file view). */
|
|
189
|
+
viewId?: string;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* The registry service published as `ctx.workbench` by the workbench base.
|
|
193
|
+
* Every `register*` call returns a disposer that unregisters the item; the
|
|
194
|
+
* consuming plugin wraps it in `ctx.effect(...)` so Cordis fiber disposal
|
|
195
|
+
* (HMR / disable) reverts the registration.
|
|
196
|
+
*/
|
|
197
|
+
export interface PluginDefinition {
|
|
198
|
+
id: string;
|
|
199
|
+
title: string;
|
|
200
|
+
description?: string;
|
|
201
|
+
icon?: IconRef;
|
|
202
|
+
hasEntry: boolean;
|
|
203
|
+
order?: number;
|
|
204
|
+
}
|
|
205
|
+
export interface WorkbenchService {
|
|
206
|
+
registerPlugin(def: PluginDefinition): () => void;
|
|
207
|
+
registerActivityBarItem(def: ActivityBarItemDefinition): () => void;
|
|
208
|
+
registerPanel(def: ViewDefinition & {
|
|
209
|
+
region: 'sideBar';
|
|
210
|
+
}): () => void;
|
|
211
|
+
registerEditorView(def: ViewDefinition): () => void;
|
|
212
|
+
registerStatusBarItem(def: StatusBarItemDefinition): () => void;
|
|
213
|
+
registerCommand(def: CommandDefinition): () => void;
|
|
214
|
+
executeCommand(id: string, ...args: unknown[]): Promise<unknown>;
|
|
215
|
+
/** Read the current layout (synchronous snapshot). */
|
|
216
|
+
getLayout(): WorkbenchLayout;
|
|
217
|
+
/** Patch the layout (persisted to localStorage). */
|
|
218
|
+
updateLayout(patch: Partial<WorkbenchLayout>): void;
|
|
219
|
+
/** Subscribe to layout changes; returns the disposer. */
|
|
220
|
+
onDidChangeLayout(listener: () => void): () => void;
|
|
221
|
+
/**
|
|
222
|
+
* Open (or focus) one view instance. Defaults to the editor area (tab);
|
|
223
|
+
* options.floating hosts it in an independent floating window. Returns
|
|
224
|
+
* the instance id (a matching open focuses the existing instance).
|
|
225
|
+
* Replacing an existing instance's seed — opening a new file into an
|
|
226
|
+
* already-open window/tab — is gated by the view's `beforeClose` hook
|
|
227
|
+
* exactly like closing it: a `false` verdict aborts the open and keeps the
|
|
228
|
+
* old seed, so a dirty editor's content is never silently discarded.
|
|
229
|
+
*/
|
|
230
|
+
openView(viewId: string, seed?: EditorOpenSeed, options?: {
|
|
231
|
+
floating?: boolean;
|
|
232
|
+
}): string;
|
|
233
|
+
/**
|
|
234
|
+
* Close a view instance wherever it lives (tab or floating); unknown ids
|
|
235
|
+
* are a no-op. Before removing the instance the shell consults the
|
|
236
|
+
* instance's view definition: when the view registered a `beforeClose`
|
|
237
|
+
* hook it is called with `{ viewId, instanceId, seed }`; returning `false`
|
|
238
|
+
* (or a promise resolving to `false`) cancels the close. The call is
|
|
239
|
+
* fire-and-forget — with an async hook the confirm dialog shows while the
|
|
240
|
+
* layout stays untouched, and the removal happens on approval.
|
|
241
|
+
*/
|
|
242
|
+
closeViewInstance(instanceId: string): void;
|
|
243
|
+
/**
|
|
244
|
+
* Patch one open instance's seed in place (editor tab or floating window;
|
|
245
|
+
* unknown ids are a no-op). `patch.meta` is shallow-merged into the
|
|
246
|
+
* instance's current meta when both are plain objects, so an editor writes
|
|
247
|
+
* its dirty flag as `updateViewSeed(id, { meta: { dirty: true } })`; all
|
|
248
|
+
* other patch fields replace the seed's field wholesale.
|
|
249
|
+
*/
|
|
250
|
+
updateViewSeed(instanceId: string, patch: Partial<EditorOpenSeed>): void;
|
|
251
|
+
/** Move a floating window (persisted); the resulting rect is clamped so
|
|
252
|
+
* the title bar stays on-screen. */
|
|
253
|
+
moveFloatingWindow(instanceId: string, x: number, y: number): void;
|
|
254
|
+
/** Resize a floating window from any edge/corner (persisted): the full
|
|
255
|
+
* rect is passed because dragging a west/north edge also moves the
|
|
256
|
+
* window. The resulting rect is clamped so the title bar stays on-screen. */
|
|
257
|
+
resizeFloatingWindow(instanceId: string, x: number, y: number, width: number, height: number): void;
|
|
258
|
+
/** Pull every open floating window back so its title bar is on-screen
|
|
259
|
+
* (viewports shrink, or geometry remembered on a larger screen). Drags
|
|
260
|
+
* already clamp live; this covers resize/mount recovery. */
|
|
261
|
+
clampFloatingWindowsIntoView(): void;
|
|
262
|
+
/**
|
|
263
|
+
* Unified file-path entry: system interception (chat links, produced
|
|
264
|
+
* files) and third-party plugins route here; the registered open-path
|
|
265
|
+
* handler (the file domain host, e.g. dock-files) owns the path.
|
|
266
|
+
*/
|
|
267
|
+
openPath(path: string, options?: OpenPathOptions): void;
|
|
268
|
+
/** The file-domain host declares it can open file paths. Returns the disposer. */
|
|
269
|
+
registerOpenPathHandler(handler: (path: string, options?: OpenPathOptions) => void): () => void;
|
|
270
|
+
/** Registry lookups (undefined when not registered). */
|
|
271
|
+
getPanel(id: string): (ViewDefinition & {
|
|
272
|
+
region: 'sideBar';
|
|
273
|
+
}) | undefined;
|
|
274
|
+
getEditorView(id: string): ViewDefinition | undefined;
|
|
275
|
+
getActivityItem(id: string): ActivityBarItemDefinition | undefined;
|
|
276
|
+
/** Snapshot of all registered items (for the shell render). */
|
|
277
|
+
getPanels(): readonly (ViewDefinition & {
|
|
278
|
+
region: 'sideBar';
|
|
279
|
+
})[];
|
|
280
|
+
getEditorViews(): readonly ViewDefinition[];
|
|
281
|
+
getActivityItems(): readonly ActivityBarItemDefinition[];
|
|
282
|
+
getStatusItems(): readonly StatusBarItemDefinition[];
|
|
283
|
+
getCommands(): readonly CommandDefinition[];
|
|
284
|
+
/** Subscribe to registry changes; returns the disposer. */
|
|
285
|
+
subscribe(listener: () => void): () => void;
|
|
286
|
+
}
|
|
287
|
+
declare module 'cordis' {
|
|
288
|
+
interface Context {
|
|
289
|
+
workbench: WorkbenchService;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL detection for spreadsheet cell text.
|
|
3
|
+
*
|
|
4
|
+
* Cells are rendered as plain text so workbook content can never become DOM,
|
|
5
|
+
* which means a URL sitting in a cell is inert until it is recognised here and
|
|
6
|
+
* wrapped in an anchor by the cell formatter. Detection is deliberately narrow:
|
|
7
|
+
* an explicit `http(s)://` scheme or a `www.` prefix. Bare domains and e-mail
|
|
8
|
+
* addresses are left as text because their false-positive rate on arbitrary
|
|
9
|
+
* tabular data is high.
|
|
10
|
+
*/
|
|
11
|
+
/** One run of cell text: a URL when `url` is present, otherwise literal text. */
|
|
12
|
+
export interface LinkSegment {
|
|
13
|
+
/** The text exactly as it appears in the cell. */
|
|
14
|
+
text: string;
|
|
15
|
+
/** Absolute URL to open, or `undefined` for literal text. */
|
|
16
|
+
url?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Split cell text into literal runs and detected URLs, preserving the original
|
|
20
|
+
* text verbatim. A candidate immediately preceded by a word character is not
|
|
21
|
+
* detected, so `abchttps://x` stays text.
|
|
22
|
+
*
|
|
23
|
+
* @param text - Raw cell text.
|
|
24
|
+
* @returns Segments in source order; a URL segment carries its absolute `url`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function splitLinks(text: string): LinkSegment[];
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/** Viewer-only chrome layered over Tabulator's bundled stylesheet. */
|
|
2
|
+
export declare const viewerCss = "\n.ds-sheet {\n --ds-bg: var(--dsw-alias-fill-primary, #fff);\n --ds-surface: var(--dsw-alias-fill-control, #f8fafc);\n --ds-text: var(--dsw-alias-label-primary, #202631);\n --ds-muted: var(--dsw-alias-label-secondary, #667085);\n --ds-line: var(--dsw-alias-border-light, #dce3e8);\n --ds-accent: var(--dsw-alias-interactive-fg-accent, #1766d5);\n display: flex; flex-direction: column; height: 100%; min-height: 0;\n container: ds-viewer / inline-size;\n overflow: hidden; background: var(--ds-bg); color: var(--ds-text);\n font: 12px/1.4 system-ui, -apple-system, 'Segoe UI', sans-serif;\n}\n.ds-sheet * { box-sizing: border-box; }\n.ds-sheet-head { display: flex; align-items: center; gap: 8px; min-height: 36px; padding: 4px 12px; border-bottom: 1px solid var(--ds-line); flex: none; }\n.ds-sheet-filemark { display: grid; place-items: center; flex: none; width: 25px; height: 25px; border-radius: 5px; background: #e6f3ed; color: #14734e; font-weight: 750; font-size: 14px; }\n.ds-sheet-title { font-weight: 650; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; flex: 1; }\n.ds-sheet-readonly { flex: none; padding: 2px 7px; border: 1px solid var(--ds-line); border-radius: 10px; color: var(--ds-muted); background: var(--ds-surface); font-size: 11px; }\n.ds-sheet-tools { display: flex; align-items: center; gap: 8px; padding: 4px 12px; min-height: 36px; border-bottom: 1px solid var(--ds-line); flex: none; }\n.ds-sheet-tool { flex: none; display: inline-flex; align-items: center; gap: 5px; min-height: 28px; padding: 4px 9px; border: 1px solid var(--ds-line); border-radius: 5px; background: var(--ds-bg); color: var(--ds-text); font: inherit; cursor: pointer; }\n.ds-sheet-tool:hover { background: var(--ds-surface); }\n.ds-sheet-tool[aria-pressed='true'] { color: var(--ds-accent); border-color: var(--ds-accent); background: var(--ds-surface); }\n.ds-sheet-tool:focus-visible, .ds-sheet-tab:focus-visible, .ds-sheet-search:focus-visible { outline: 2px solid var(--ds-accent); outline-offset: 1px; }\n.ds-sheet-search-wrap { position: relative; display: flex; align-items: center; min-width: 110px; max-width: 300px; flex: 1; }\n.ds-sheet-search-icon { position: absolute; left: 9px; color: var(--ds-muted); font-size: 15px; pointer-events: none; line-height: 1; }\n.ds-sheet-search { width: 100%; min-width: 0; height: 28px; padding: 4px 8px 4px 30px; border: 1px solid var(--ds-line); border-radius: 5px; outline: none; background: var(--ds-bg); color: var(--ds-text); font: inherit; }\n.ds-sheet-search::placeholder { color: var(--ds-muted); }\n.ds-sheet-search:disabled { opacity: .6; }\n.ds-sheet-hint { margin-left: auto; white-space: nowrap; color: var(--ds-muted); font-size: 11px; }\n.ds-sheet-inspector { display: flex; align-items: stretch; flex: none; min-height: 30px; border-bottom: 1px solid var(--ds-line); }\n.ds-sheet-address { display: grid; place-items: center; flex: none; width: 69px; padding: 4px; border-right: 1px solid var(--ds-line); color: var(--ds-accent); font-weight: 650; font-variant-numeric: tabular-nums; background: var(--ds-surface); }\n.ds-sheet-value { display: block; min-width: 0; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: 6px 12px; color: var(--ds-text); user-select: text; }\n.ds-sheet-value.is-empty { color: var(--ds-muted); }\n.ds-sheet-grid { flex: 1; min-height: 0; overflow: hidden; }\n.ds-sheet .tabulator { border: 0; height: 100%; background: var(--ds-bg); color: var(--ds-text); font-family: inherit; font-size: 12px; }\n.ds-sheet .tabulator .tabulator-header { border: 0; border-bottom: 1px solid var(--ds-line); background: var(--ds-surface); color: var(--ds-muted); font-weight: 600; }\n.ds-sheet .tabulator .tabulator-header .tabulator-col { border-right: 1px solid var(--ds-line); background: var(--ds-surface); text-align: center; }\n.ds-sheet .tabulator .tabulator-header .tabulator-col .tabulator-col-content { padding: 7px 5px; }\n.ds-sheet .tabulator .tabulator-header .tabulator-col .tabulator-col-title { text-align: center; }\n.ds-sheet .tabulator .tabulator-header .tabulator-col.tabulator-sortable:hover { background: var(--ds-bg); }\n.ds-sheet .tabulator-row, .ds-sheet .tabulator-row:nth-child(even) { background: var(--ds-bg); color: var(--ds-text); min-height: 29px; border-bottom: 1px solid var(--ds-line); }\n.ds-sheet .tabulator-row:hover, .ds-sheet .tabulator-row:nth-child(even):hover { background: var(--ds-surface); }\n.ds-sheet .tabulator-row .tabulator-cell { height: 29px; padding: 6px 8px; border-right: 1px solid var(--ds-line); vertical-align: middle; }\n.ds-sheet .tabulator-row .tabulator-cell:first-child { position: sticky; left: 0; z-index: 2; text-align: right; font-variant-numeric: tabular-nums; color: var(--ds-muted); background: var(--ds-surface); }\n.ds-sheet .tabulator-row .tabulator-cell.ds-selected-cell { box-shadow: inset 0 0 0 2px var(--ds-accent); background: var(--ds-bg); z-index: 3; }\n.ds-sheet .tabulator .tabulator-header-filter { margin-top: 4px; }\n.ds-sheet .tabulator .tabulator-header-filter input { width: 100%; min-width: 0; padding: 3px 5px; border: 1px solid var(--ds-line); border-radius: 4px; color: var(--ds-text); background: var(--ds-bg); font: inherit; }\n.ds-sheet:not(.ds-filtering) .tabulator .tabulator-header-filter { display: none; }\n.ds-sheet .ds-url { color: var(--ds-accent); text-decoration: underline; text-underline-offset: 2px; cursor: pointer; }\n.ds-sheet .ds-url:hover { text-decoration-thickness: 2px; }\n.ds-sheet-footer { display: flex; align-items: stretch; flex: none; min-height: 32px; border-top: 1px solid var(--ds-line); background: var(--ds-surface); }\n.ds-sheet-tabs { display: flex; align-items: stretch; flex: 1; min-width: 0; overflow-x: auto; scrollbar-width: thin; padding-left: 8px; }\n.ds-sheet-tab { position: relative; flex: none; max-width: 170px; min-width: 72px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: 6px 14px; border: 0; border-right: 1px solid var(--ds-line); background: transparent; color: var(--ds-muted); font: inherit; cursor: pointer; }\n.ds-sheet-tab:hover { background: var(--ds-bg); color: var(--ds-text); }\n.ds-sheet-tab[aria-selected='true'] { background: var(--ds-bg); color: var(--ds-accent); font-weight: 650; }\n.ds-sheet-tab[aria-selected='true']::after { content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 2px; background: var(--ds-accent); }\n.ds-sheet-status { flex: none; display: flex; align-items: center; gap: 6px; padding: 0 10px; border-left: 1px solid var(--ds-line); color: var(--ds-muted); white-space: nowrap; font-variant-numeric: tabular-nums; }\n.ds-sheet-message { padding: 16px; overflow: auto; }\n@container ds-viewer (max-width: 440px) {\n .ds-sheet-hint { display: none; }\n .ds-sheet-status { padding: 0 6px; }\n .ds-sheet-tool { padding-left: 7px; padding-right: 7px; }\n}\n@media (max-width: 520px) {\n .ds-sheet-head, .ds-sheet-tools { padding-left: 8px; padding-right: 8px; }\n .ds-sheet-hint { display: none; }\n .ds-sheet-status { padding: 0 6px; }\n .ds-sheet-tool { padding-left: 7px; padding-right: 7px; }\n}\n";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
declare const _default: ".tabulator{background-color:#888;border:1px solid #999;font-size:14px;overflow:hidden;position:relative;text-align:left;-webkit-transform:translateZ(0);-moz-transform:translateZ(0);-ms-transform:translateZ(0);-o-transform:translateZ(0);transform:translateZ(0)}.tabulator[tabulator-layout=fitDataFill] .tabulator-tableholder .tabulator-table{min-width:100%}.tabulator[tabulator-layout=fitDataTable]{display:inline-block}.tabulator.tabulator-block-select,.tabulator.tabulator-ranges .tabulator-cell:not(.tabulator-editing){user-select:none}.tabulator .tabulator-header{background-color:#e6e6e6;border-bottom:1px solid #999;box-sizing:border-box;color:#555;font-weight:700;outline:none;overflow:hidden;position:relative;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-o-user-select:none;white-space:nowrap;width:100%}.tabulator .tabulator-header.tabulator-header-hidden{display:none}.tabulator .tabulator-header .tabulator-header-contents{overflow:hidden;position:relative}.tabulator .tabulator-header .tabulator-header-contents .tabulator-headers{display:inline-block}.tabulator .tabulator-header .tabulator-col{background:#e6e6e6;border-right:1px solid #aaa;box-sizing:border-box;display:inline-flex;flex-direction:column;justify-content:flex-start;overflow:hidden;position:relative;text-align:left;vertical-align:bottom}.tabulator .tabulator-header .tabulator-col.tabulator-moving{background:#cdcdcd;border:1px solid #999;pointer-events:none;position:absolute}.tabulator .tabulator-header .tabulator-col.tabulator-range-highlight{background-color:#d6d6d6;color:#000}.tabulator .tabulator-header .tabulator-col.tabulator-range-selected{background-color:#3876ca;color:#fff}.tabulator .tabulator-header .tabulator-col .tabulator-col-content{box-sizing:border-box;padding:4px;position:relative}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-header-popup-button{padding:0 8px}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-header-popup-button:hover{cursor:pointer;opacity:.6}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-title-holder{position:relative}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-title{box-sizing:border-box;overflow:hidden;text-overflow:ellipsis;vertical-align:bottom;white-space:nowrap;width:100%}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-title.tabulator-col-title-wrap{text-overflow:clip;white-space:normal}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-title .tabulator-title-editor{background:#fff;border:1px solid #999;box-sizing:border-box;padding:1px;width:100%}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-title .tabulator-header-popup-button+.tabulator-title-editor{width:calc(100% - 22px)}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-sorter{align-items:center;bottom:0;display:flex;position:absolute;right:4px;top:0}.tabulator .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-sorter .tabulator-arrow{border-bottom:6px solid #bbb;border-left:6px solid transparent;border-right:6px solid transparent;height:0;width:0}.tabulator .tabulator-header .tabulator-col.tabulator-col-group .tabulator-col-group-cols{border-top:1px solid #aaa;display:flex;margin-right:-1px;overflow:hidden;position:relative}.tabulator .tabulator-header .tabulator-col .tabulator-header-filter{box-sizing:border-box;margin-top:2px;position:relative;text-align:center;width:100%}.tabulator .tabulator-header .tabulator-col .tabulator-header-filter textarea{height:auto!important}.tabulator .tabulator-header .tabulator-col .tabulator-header-filter svg{margin-top:3px}.tabulator .tabulator-header .tabulator-col .tabulator-header-filter input::-ms-clear{height:0;width:0}.tabulator .tabulator-header .tabulator-col.tabulator-sortable .tabulator-col-title{padding-right:25px}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-header .tabulator-col.tabulator-sortable.tabulator-col-sorter-element:hover{background-color:#cdcdcd;cursor:pointer}}.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=none] .tabulator-col-content .tabulator-col-sorter{color:#bbb}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=none] .tabulator-col-content .tabulator-col-sorter.tabulator-col-sorter-element .tabulator-arrow:hover{border-bottom:6px solid #555;cursor:pointer}}.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=none] .tabulator-col-content .tabulator-col-sorter .tabulator-arrow{border-bottom:6px solid #bbb;border-top:none}.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=ascending] .tabulator-col-content .tabulator-col-sorter{color:#666}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=ascending] .tabulator-col-content .tabulator-col-sorter.tabulator-col-sorter-element .tabulator-arrow:hover{border-bottom:6px solid #555;cursor:pointer}}.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=ascending] .tabulator-col-content .tabulator-col-sorter .tabulator-arrow{border-bottom:6px solid #666;border-top:none}.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=descending] .tabulator-col-content .tabulator-col-sorter{color:#666}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=descending] .tabulator-col-content .tabulator-col-sorter.tabulator-col-sorter-element .tabulator-arrow:hover{border-top:6px solid #555;cursor:pointer}}.tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort=descending] .tabulator-col-content .tabulator-col-sorter .tabulator-arrow{border-bottom:none;border-top:6px solid #666;color:#666}.tabulator .tabulator-header .tabulator-col.tabulator-col-vertical .tabulator-col-content .tabulator-col-title{align-items:center;display:flex;justify-content:center;text-orientation:mixed;writing-mode:vertical-rl}.tabulator .tabulator-header .tabulator-col.tabulator-col-vertical.tabulator-col-vertical-flip .tabulator-col-title{transform:rotate(180deg)}.tabulator .tabulator-header .tabulator-col.tabulator-col-vertical.tabulator-sortable .tabulator-col-title{padding-right:0;padding-top:20px}.tabulator .tabulator-header .tabulator-col.tabulator-col-vertical.tabulator-sortable.tabulator-col-vertical-flip .tabulator-col-title{padding-bottom:20px;padding-right:0}.tabulator .tabulator-header .tabulator-col.tabulator-col-vertical.tabulator-sortable .tabulator-col-sorter{bottom:auto;justify-content:center;left:0;right:0;top:4px}.tabulator .tabulator-header .tabulator-frozen{left:0;position:sticky;z-index:11}.tabulator .tabulator-header .tabulator-frozen.tabulator-frozen-left{border-right:2px solid #aaa}.tabulator .tabulator-header .tabulator-frozen.tabulator-frozen-right{border-left:2px solid #aaa}.tabulator .tabulator-header .tabulator-calcs-holder{background:#f3f3f3!important;border-bottom:1px solid #aaa;border-top:1px solid #aaa;box-sizing:border-box;display:inline-block}.tabulator .tabulator-header .tabulator-calcs-holder .tabulator-row{background:#f3f3f3!important}.tabulator .tabulator-header .tabulator-calcs-holder .tabulator-row .tabulator-col-resize-handle{display:none}.tabulator .tabulator-header .tabulator-frozen-rows-holder{display:inline-block}.tabulator .tabulator-header .tabulator-frozen-rows-holder:empty{display:none}.tabulator .tabulator-tableholder{-webkit-overflow-scrolling:touch;overflow:auto;position:relative;white-space:nowrap;width:100%}.tabulator .tabulator-tableholder:focus{outline:none}.tabulator .tabulator-tableholder .tabulator-placeholder{align-items:center;box-sizing:border-box;display:flex;justify-content:center;min-width:100%;width:100%}.tabulator .tabulator-tableholder .tabulator-placeholder[tabulator-render-mode=virtual]{min-height:100%}.tabulator .tabulator-tableholder .tabulator-placeholder .tabulator-placeholder-contents{color:#ccc;display:inline-block;font-size:20px;font-weight:700;padding:10px;text-align:center;white-space:normal}.tabulator .tabulator-tableholder .tabulator-table{background-color:#fff;color:#333;display:inline-block;overflow:visible;position:relative;white-space:nowrap}.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.tabulator-calcs{background:#e2e2e2!important;font-weight:700}.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.tabulator-calcs.tabulator-calcs-top{border-bottom:2px solid #aaa}.tabulator .tabulator-tableholder .tabulator-table .tabulator-row.tabulator-calcs.tabulator-calcs-bottom{border-top:2px solid #aaa}.tabulator .tabulator-tableholder .tabulator-range-overlay{inset:0;pointer-events:none;position:absolute;z-index:11}.tabulator .tabulator-tableholder .tabulator-range-overlay .tabulator-range{border:1px solid #2975dd;box-sizing:border-box;position:absolute}.tabulator .tabulator-tableholder .tabulator-range-overlay .tabulator-range.tabulator-range-fill-preview{border-style:dashed}.tabulator .tabulator-tableholder .tabulator-range-overlay .tabulator-range-cell-active{border:2px solid #2975dd;box-sizing:border-box;position:absolute}.tabulator .tabulator-footer{background-color:#e6e6e6;border-top:1px solid #999;color:#555;font-weight:700;user-select:none;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-o-user-select:none;white-space:nowrap}.tabulator .tabulator-footer .tabulator-footer-contents{align-items:center;display:flex;flex-direction:row;justify-content:space-between;padding:5px 10px}.tabulator .tabulator-footer .tabulator-footer-contents:empty{display:none}.tabulator .tabulator-footer .tabulator-spreadsheet-tabs{margin-top:-5px;overflow-x:auto}.tabulator .tabulator-footer .tabulator-spreadsheet-tabs .tabulator-spreadsheet-tab{border:1px solid #999;border-bottom-left-radius:5px;border-bottom-right-radius:5px;border-top:none;display:inline-block;font-size:.9em;padding:5px}.tabulator .tabulator-footer .tabulator-spreadsheet-tabs .tabulator-spreadsheet-tab:hover{cursor:pointer;opacity:.7}.tabulator .tabulator-footer .tabulator-spreadsheet-tabs .tabulator-spreadsheet-tab.tabulator-spreadsheet-tab-active{background:#fff}.tabulator .tabulator-footer .tabulator-calcs-holder{background:#f3f3f3!important;border-bottom:1px solid #aaa;border-top:1px solid #aaa;box-sizing:border-box;overflow:hidden;text-align:left;width:100%}.tabulator .tabulator-footer .tabulator-calcs-holder .tabulator-row{background:#f3f3f3!important;display:inline-block}.tabulator .tabulator-footer .tabulator-calcs-holder .tabulator-row .tabulator-col-resize-handle{display:none}.tabulator .tabulator-footer .tabulator-calcs-holder:only-child{border-bottom:none;margin-bottom:-5px}.tabulator .tabulator-footer>*+.tabulator-page-counter{margin-left:10px}.tabulator .tabulator-footer .tabulator-page-counter{font-weight:400}.tabulator .tabulator-footer .tabulator-paginator{color:#555;flex:1;font-family:inherit;font-size:inherit;font-weight:inherit;text-align:right}.tabulator .tabulator-footer .tabulator-page-size{border:1px solid #aaa;border-radius:3px;display:inline-block;margin:0 5px;padding:2px 5px}.tabulator .tabulator-footer .tabulator-pages{margin:0 7px}.tabulator .tabulator-footer .tabulator-page{background:hsla(0,0%,100%,.2);border:1px solid #aaa;border-radius:3px;display:inline-block;margin:0 2px;padding:2px 5px}.tabulator .tabulator-footer .tabulator-page.active{color:#d00}.tabulator .tabulator-footer .tabulator-page:disabled{opacity:.5}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-footer .tabulator-page:not(disabled):hover{background:rgba(0,0,0,.2);color:#fff;cursor:pointer}}.tabulator .tabulator-col-resize-handle{display:inline-block;margin-left:-3px;margin-right:-3px;position:relative;vertical-align:middle;width:6px;z-index:11}@media (hover:hover) and (pointer:fine){.tabulator .tabulator-col-resize-handle:hover{cursor:ew-resize}}.tabulator .tabulator-col-resize-handle:last-of-type{margin-right:0;width:3px}.tabulator .tabulator-col-resize-guide{background-color:#999;height:100%;margin-left:-.5px;opacity:.5;position:absolute;top:0;width:4px}.tabulator .tabulator-row-resize-guide{background-color:#999;height:4px;left:0;margin-top:-.5px;opacity:.5;position:absolute;width:100%}.tabulator .tabulator-alert{align-items:center;background:rgba(0,0,0,.4);display:flex;height:100%;left:0;position:absolute;text-align:center;top:0;width:100%;z-index:100}.tabulator .tabulator-alert .tabulator-alert-msg{background:#fff;border-radius:10px;display:inline-block;font-size:16px;font-weight:700;margin:0 auto;padding:10px 20px}.tabulator .tabulator-alert .tabulator-alert-msg.tabulator-alert-state-msg{border:4px solid #333;color:#000}.tabulator .tabulator-alert .tabulator-alert-msg.tabulator-alert-state-error{border:4px solid #d00;color:#590000}.tabulator-row{background-color:#fff;box-sizing:border-box;min-height:22px;position:relative}.tabulator-row.tabulator-row-even{background-color:#efefef}@media (hover:hover) and (pointer:fine){.tabulator-row.tabulator-selectable:hover{background-color:#bbb;cursor:pointer}}.tabulator-row.tabulator-selected{background-color:#9abcea}@media (hover:hover) and (pointer:fine){.tabulator-row.tabulator-selected:hover{background-color:#769bcc;cursor:pointer}}.tabulator-row.tabulator-row-moving{background:#fff;border:1px solid #000}.tabulator-row.tabulator-moving{border-bottom:1px solid #aaa;border-top:1px solid #aaa;pointer-events:none;position:absolute;z-index:15}.tabulator-row.tabulator-range-highlight .tabulator-cell.tabulator-range-row-header{background-color:#d6d6d6;color:#000}.tabulator-row.tabulator-range-highlight.tabulator-range-selected .tabulator-cell.tabulator-range-row-header,.tabulator-row.tabulator-range-selected .tabulator-cell.tabulator-range-row-header{background-color:#3876ca;color:#fff}.tabulator-row .tabulator-row-resize-handle{bottom:0;height:5px;left:0;position:absolute;right:0}.tabulator-row .tabulator-row-resize-handle.prev{bottom:auto;top:0}@media (hover:hover) and (pointer:fine){.tabulator-row .tabulator-row-resize-handle:hover{cursor:ns-resize}}.tabulator-row .tabulator-responsive-collapse{border-bottom:1px solid #aaa;border-top:1px solid #aaa;box-sizing:border-box;padding:5px}.tabulator-row .tabulator-responsive-collapse:empty{display:none}.tabulator-row .tabulator-responsive-collapse table{font-size:14px}.tabulator-row .tabulator-responsive-collapse table tr td{position:relative}.tabulator-row .tabulator-responsive-collapse table tr td:first-of-type{padding-right:10px}.tabulator-row .tabulator-cell{border-right:1px solid #aaa;box-sizing:border-box;display:inline-block;outline:none;overflow:hidden;padding:4px;position:relative;text-overflow:ellipsis;vertical-align:middle;white-space:nowrap}.tabulator-row .tabulator-cell .tabulator-tick{fill:#2dc214}.tabulator-row .tabulator-cell .tabulator-cross{fill:#ce1515}.tabulator-row .tabulator-cell.tabulator-row-header{background:#e6e6e6;border-bottom:1px solid #aaa;border-right:1px solid #999}.tabulator-row .tabulator-cell.tabulator-frozen{background-color:inherit;display:inline-block;left:0;position:sticky;z-index:11}.tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-left{border-right:2px solid #aaa}.tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-right{border-left:2px solid #aaa}.tabulator-row .tabulator-cell.tabulator-editing{border:1px solid #1d68cd;outline:none;padding:0}.tabulator-row .tabulator-cell.tabulator-editing input,.tabulator-row .tabulator-cell.tabulator-editing select{background:transparent;border:1px;outline:none}.tabulator-row .tabulator-cell.tabulator-validation-fail{border:1px solid #d00}.tabulator-row .tabulator-cell.tabulator-validation-fail input,.tabulator-row .tabulator-cell.tabulator-validation-fail select{background:transparent;border:1px;color:#d00}.tabulator-row .tabulator-cell.tabulator-row-handle{align-items:center;display:inline-flex;justify-content:center;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-o-user-select:none}.tabulator-row .tabulator-cell.tabulator-row-handle .tabulator-row-handle-box{width:80%}.tabulator-row .tabulator-cell.tabulator-row-handle .tabulator-row-handle-box .tabulator-row-handle-bar{background:#666;height:3px;margin-top:2px;width:100%}.tabulator-row .tabulator-cell.tabulator-range-selected:not(.tabulator-range-only-cell-selected):not(.tabulator-range-row-header){background-color:#9abcea}.tabulator-row .tabulator-cell .tabulator-data-tree-branch-empty{display:inline-block;width:7px}.tabulator-row .tabulator-cell .tabulator-data-tree-branch{border-bottom:2px solid #aaa;border-bottom-left-radius:1px;border-left:2px solid #aaa;display:inline-block;height:9px;margin-right:5px;margin-top:-9px;vertical-align:middle;width:7px}.tabulator-row .tabulator-cell .tabulator-data-tree-control{align-items:center;background:rgba(0,0,0,.1);border:1px solid #333;border-radius:2px;display:inline-flex;height:11px;justify-content:center;margin-right:5px;overflow:hidden;vertical-align:middle;width:11px}@media (hover:hover) and (pointer:fine){.tabulator-row .tabulator-cell .tabulator-data-tree-control:hover{background:rgba(0,0,0,.2);cursor:pointer}}.tabulator-row .tabulator-cell .tabulator-data-tree-control .tabulator-data-tree-control-collapse{background:transparent;display:inline-block;height:7px;position:relative;width:1px}.tabulator-row .tabulator-cell .tabulator-data-tree-control .tabulator-data-tree-control-collapse:after{background:#333;content:\"\";height:1px;left:-3px;position:absolute;top:3px;width:7px}.tabulator-row .tabulator-cell .tabulator-data-tree-control .tabulator-data-tree-control-expand{background:#333;display:inline-block;height:7px;position:relative;width:1px}.tabulator-row .tabulator-cell .tabulator-data-tree-control .tabulator-data-tree-control-expand:after{background:#333;content:\"\";height:1px;left:-3px;position:absolute;top:3px;width:7px}.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle{align-items:center;background:#666;border-radius:20px;color:#fff;display:inline-flex;font-size:1.1em;font-weight:700;height:15px;justify-content:center;-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;-o-user-select:none;width:15px}@media (hover:hover) and (pointer:fine){.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle:hover{cursor:pointer;opacity:.7}}.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle.open .tabulator-responsive-collapse-toggle-close{display:initial}.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle.open .tabulator-responsive-collapse-toggle-open{display:none}.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle svg{stroke:#fff}.tabulator-row .tabulator-cell .tabulator-responsive-collapse-toggle .tabulator-responsive-collapse-toggle-close{display:none}.tabulator-row .tabulator-cell .tabulator-traffic-light{border-radius:14px;display:inline-block;height:14px;width:14px}.tabulator-row.tabulator-group{background:#ccc;border-bottom:1px solid #999;border-right:1px solid #aaa;border-top:1px solid #999;box-sizing:border-box;font-weight:700;min-width:100%;padding:5px 5px 5px 10px}@media (hover:hover) and (pointer:fine){.tabulator-row.tabulator-group:hover{background-color:rgba(0,0,0,.1);cursor:pointer}}.tabulator-row.tabulator-group.tabulator-group-visible .tabulator-arrow{border-bottom:0;border-left:6px solid transparent;border-right:6px solid transparent;border-top:6px solid #666;margin-right:10px}.tabulator-row.tabulator-group.tabulator-group-level-1{padding-left:30px}.tabulator-row.tabulator-group.tabulator-group-level-2{padding-left:50px}.tabulator-row.tabulator-group.tabulator-group-level-3{padding-left:70px}.tabulator-row.tabulator-group.tabulator-group-level-4{padding-left:90px}.tabulator-row.tabulator-group.tabulator-group-level-5{padding-left:110px}.tabulator-row.tabulator-group .tabulator-group-toggle{display:inline-block}.tabulator-row.tabulator-group .tabulator-arrow{border-bottom:6px solid transparent;border-left:6px solid #666;border-right:0;border-top:6px solid transparent;display:inline-block;height:0;margin-right:16px;vertical-align:middle;width:0}.tabulator-row.tabulator-group span{color:#d00;margin-left:10px}.tabulator-range-fill-handle{background-color:#2975dd;border-radius:999px;bottom:-3px;cursor:crosshair;height:6px;pointer-events:auto;position:absolute;right:-3px;width:6px}.tabulator-toggle{background:#dcdcdc;border:1px solid #ccc;box-sizing:border-box;display:flex;flex-direction:row}.tabulator-toggle.tabulator-toggle-on{background:#1c6cc2}.tabulator-toggle .tabulator-toggle-switch{background:#fff;border:1px solid #ccc;box-sizing:border-box}.tabulator-popup-container{-webkit-overflow-scrolling:touch;background:#fff;border:1px solid #aaa;box-shadow:0 0 5px 0 rgba(0,0,0,.2);box-sizing:border-box;display:inline-block;font-size:14px;overflow-y:auto;position:absolute;z-index:10000}.tabulator-popup{border-radius:3px;padding:5px}.tabulator-tooltip{border-radius:2px;box-shadow:none;font-size:12px;max-width:min(500px,100%);padding:3px 5px;pointer-events:none}.tabulator-menu .tabulator-menu-item{box-sizing:border-box;padding:5px 10px;position:relative;user-select:none}.tabulator-menu .tabulator-menu-item.tabulator-menu-item-disabled{opacity:.5}@media (hover:hover) and (pointer:fine){.tabulator-menu .tabulator-menu-item:not(.tabulator-menu-item-disabled):hover{background:#efefef;cursor:pointer}}.tabulator-menu .tabulator-menu-item.tabulator-menu-item-submenu{padding-right:25px}.tabulator-menu .tabulator-menu-item.tabulator-menu-item-submenu:after{border-color:#aaa;border-style:solid;border-width:1px 1px 0 0;content:\"\";display:inline-block;height:7px;position:absolute;right:10px;top:calc(5px + .4em);transform:rotate(45deg);vertical-align:top;width:7px}.tabulator-menu .tabulator-menu-separator{border-top:1px solid #aaa}.tabulator-edit-list{-webkit-overflow-scrolling:touch;font-size:14px;max-height:200px;overflow-y:auto}.tabulator-edit-list .tabulator-edit-list-item{color:#333;outline:none;padding:4px}.tabulator-edit-list .tabulator-edit-list-item.active{background:#1d68cd;color:#fff}.tabulator-edit-list .tabulator-edit-list-item.active.focused{outline:1px solid hsla(0,0%,100%,.5)}.tabulator-edit-list .tabulator-edit-list-item.focused{outline:1px solid #1d68cd}@media (hover:hover) and (pointer:fine){.tabulator-edit-list .tabulator-edit-list-item:hover{background:#1d68cd;color:#fff;cursor:pointer}}.tabulator-edit-list .tabulator-edit-list-placeholder{color:#333;padding:4px;text-align:center}.tabulator-edit-list .tabulator-edit-list-group{border-bottom:1px solid #aaa;color:#333;font-weight:700;padding:6px 4px 4px}.tabulator-edit-list .tabulator-edit-list-group.tabulator-edit-list-group-level-2,.tabulator-edit-list .tabulator-edit-list-item.tabulator-edit-list-group-level-2{padding-left:12px}.tabulator-edit-list .tabulator-edit-list-group.tabulator-edit-list-group-level-3,.tabulator-edit-list .tabulator-edit-list-item.tabulator-edit-list-group-level-3{padding-left:20px}.tabulator-edit-list .tabulator-edit-list-group.tabulator-edit-list-group-level-4,.tabulator-edit-list .tabulator-edit-list-item.tabulator-edit-list-group-level-4{padding-left:28px}.tabulator-edit-list .tabulator-edit-list-group.tabulator-edit-list-group-level-5,.tabulator-edit-list .tabulator-edit-list-item.tabulator-edit-list-group-level-5{padding-left:36px}.tabulator.tabulator-ltr{direction:ltr}.tabulator.tabulator-rtl{direction:rtl;text-align:initial}.tabulator.tabulator-rtl .tabulator-header .tabulator-col{border-left:1px solid #aaa;border-right:initial;text-align:initial}.tabulator.tabulator-rtl .tabulator-header .tabulator-col.tabulator-col-group .tabulator-col-group-cols{margin-left:-1px;margin-right:0}.tabulator.tabulator-rtl .tabulator-header .tabulator-col.tabulator-sortable .tabulator-col-title{padding-left:25px;padding-right:0}.tabulator.tabulator-rtl .tabulator-header .tabulator-col .tabulator-col-content .tabulator-col-sorter{left:8px;right:auto}.tabulator.tabulator-rtl .tabulator-range-fill-handle{left:-3px;right:auto}.tabulator.tabulator-rtl .tabulator-row .tabulator-cell{border-left:1px solid #aaa;border-right:initial}.tabulator.tabulator-rtl .tabulator-row .tabulator-cell .tabulator-data-tree-branch{border-bottom-left-radius:0;border-bottom-right-radius:1px;border-left:initial;border-right:2px solid #aaa;margin-left:5px;margin-right:0}.tabulator.tabulator-rtl .tabulator-row .tabulator-cell .tabulator-data-tree-control{margin-left:5px;margin-right:0}.tabulator.tabulator-rtl .tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-left{border-left:2px solid #aaa}.tabulator.tabulator-rtl .tabulator-row .tabulator-cell.tabulator-frozen.tabulator-frozen-right{border-right:2px solid #aaa}.tabulator.tabulator-rtl .tabulator-row .tabulator-col-resize-handle:last-of-type{margin-left:0;margin-right:-3px;width:3px}.tabulator.tabulator-rtl .tabulator-footer .tabulator-calcs-holder{text-align:initial}.tabulator-print-fullscreen{bottom:0;left:0;position:absolute;right:0;top:0;z-index:10000}body.tabulator-print-fullscreen-hide>:not(.tabulator-print-fullscreen){display:none!important}.tabulator-print-table{border-collapse:collapse}.tabulator-print-table .tabulator-data-tree-branch{border-bottom:2px solid #aaa;border-bottom-left-radius:1px;border-left:2px solid #aaa;display:inline-block;height:9px;margin-right:5px;margin-top:-9px;vertical-align:middle;width:7px}.tabulator-print-table .tabulator-print-table-group{background:#ccc;border-bottom:1px solid #999;border-right:1px solid #aaa;border-top:1px solid #999;box-sizing:border-box;font-weight:700;min-width:100%;padding:5px 5px 5px 10px}@media (hover:hover) and (pointer:fine){.tabulator-print-table .tabulator-print-table-group:hover{background-color:rgba(0,0,0,.1);cursor:pointer}}.tabulator-print-table .tabulator-print-table-group.tabulator-group-visible .tabulator-arrow{border-bottom:0;border-left:6px solid transparent;border-right:6px solid transparent;border-top:6px solid #666;margin-right:10px}.tabulator-print-table .tabulator-print-table-group.tabulator-group-level-1 td{padding-left:30px!important}.tabulator-print-table .tabulator-print-table-group.tabulator-group-level-2 td{padding-left:50px!important}.tabulator-print-table .tabulator-print-table-group.tabulator-group-level-3 td{padding-left:70px!important}.tabulator-print-table .tabulator-print-table-group.tabulator-group-level-4 td{padding-left:90px!important}.tabulator-print-table .tabulator-print-table-group.tabulator-group-level-5 td{padding-left:110px!important}.tabulator-print-table .tabulator-print-table-group .tabulator-group-toggle{display:inline-block}.tabulator-print-table .tabulator-print-table-group .tabulator-arrow{border-bottom:6px solid transparent;border-left:6px solid #666;border-right:0;border-top:6px solid transparent;display:inline-block;height:0;margin-right:16px;vertical-align:middle;width:0}.tabulator-print-table .tabulator-print-table-group span{color:#d00;margin-left:10px}.tabulator-print-table .tabulator-data-tree-control{align-items:center;background:rgba(0,0,0,.1);border:1px solid #333;border-radius:2px;display:inline-flex;height:11px;justify-content:center;margin-right:5px;overflow:hidden;vertical-align:middle;width:11px}@media (hover:hover) and (pointer:fine){.tabulator-print-table .tabulator-data-tree-control:hover{background:rgba(0,0,0,.2);cursor:pointer}}.tabulator-print-table .tabulator-data-tree-control .tabulator-data-tree-control-collapse{background:transparent;display:inline-block;height:7px;position:relative;width:1px}.tabulator-print-table .tabulator-data-tree-control .tabulator-data-tree-control-collapse:after{background:#333;content:\"\";height:1px;left:-3px;position:absolute;top:3px;width:7px}.tabulator-print-table .tabulator-data-tree-control .tabulator-data-tree-control-expand{background:#333;display:inline-block;height:7px;position:relative;width:1px}.tabulator-print-table .tabulator-data-tree-control .tabulator-data-tree-control-expand:after{background:#333;content:\"\";height:1px;left:-3px;position:absolute;top:3px;width:7px}\n/*# sourceMappingURL=tabulator.min.css.map */";
|
|
2
|
+
export default _default;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type WorkBook } from 'xlsx';
|
|
2
|
+
export declare const MAX_ROWS = 5000;
|
|
3
|
+
export declare const MAX_COLUMNS = 256;
|
|
4
|
+
export interface GridSheet {
|
|
5
|
+
name: string;
|
|
6
|
+
columns: string[];
|
|
7
|
+
rows: Record<string, string | number>[];
|
|
8
|
+
truncated: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function parseWorkbook(bytes: ArrayBuffer | Uint8Array, filename?: string): WorkBook;
|
|
11
|
+
/** Preserve spreadsheet coordinates: first row is data, never inferred headers. */
|
|
12
|
+
export declare function gridSheet(book: WorkBook, name: string): GridSheet;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
export declare const name = "dock-spreadsheet";
|
|
3
|
+
/**
|
|
4
|
+
* Services required before mounting.
|
|
5
|
+
*
|
|
6
|
+
* The declared-authority list these routes fence with used to be injected here
|
|
7
|
+
* as `webRuntime`. DSH renamed that service to `webStartup` in 0.2.1, so the
|
|
8
|
+
* list is resolved lazily through `declaredTrustedHosts` instead: a renamed
|
|
9
|
+
* service can no longer park this plugin, and older hosts still mount.
|
|
10
|
+
*/
|
|
11
|
+
export declare const inject: string[];
|
|
12
|
+
export declare const MAX_FILE_BYTES: number;
|
|
13
|
+
interface Context {
|
|
14
|
+
webServer: {
|
|
15
|
+
register(options: {
|
|
16
|
+
kind: 'prefix';
|
|
17
|
+
path: string;
|
|
18
|
+
handler: (req: IncomingMessage, res: ServerResponse) => Promise<void>;
|
|
19
|
+
}): () => void;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Read a service without declaring a hard dependency on it.
|
|
23
|
+
*
|
|
24
|
+
* Used for the optional `webStartup` / `webRuntime` / `connection` services:
|
|
25
|
+
* a host that provides none of them keeps the loopback-only fence instead of
|
|
26
|
+
* failing to mount.
|
|
27
|
+
*/
|
|
28
|
+
get?(name: string): unknown;
|
|
29
|
+
effect(fn: () => void | (() => void), label?: string): void;
|
|
30
|
+
}
|
|
31
|
+
export declare class ReadError extends Error {
|
|
32
|
+
readonly status: number;
|
|
33
|
+
readonly code: string;
|
|
34
|
+
constructor(status: number, code: string, message: string);
|
|
35
|
+
}
|
|
36
|
+
export declare function readSpreadsheet(path: string): Promise<Buffer>;
|
|
37
|
+
export declare function apply(ctx: Context): void;
|
|
38
|
+
export {};
|