@brftech/filex-app-ui 0.48.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.md +50 -0
- package/dist/filex-app-ui.iife.js +2 -0
- package/dist/filex-app-ui.iife.js.map +1 -0
- package/dist/filex-app-ui.js +258 -0
- package/dist/filex-app-ui.js.map +1 -0
- package/dist/index.d.ts +336 -0
- package/package.json +56 -0
- package/src/index.ts +384 -0
- package/src/protocol.ts +320 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
/** The bridge generation both sides speak. */
|
|
2
|
+
export declare const BRIDGE_VERSION = 1;
|
|
3
|
+
|
|
4
|
+
export declare interface BridgeError {
|
|
5
|
+
code: ErrorCode;
|
|
6
|
+
message?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export declare interface ConfirmParams {
|
|
10
|
+
title?: string;
|
|
11
|
+
text: string;
|
|
12
|
+
confirm?: string;
|
|
13
|
+
cancel?: string;
|
|
14
|
+
danger?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The handshake. Resolves once filex answered with its port and the session;
|
|
19
|
+
* rejects outside filex or when filex did not answer in time. Calling it again
|
|
20
|
+
* answers the same connection.
|
|
21
|
+
*/
|
|
22
|
+
export declare function connect(opts?: ConnectOptions): Promise<FilexApp>;
|
|
23
|
+
|
|
24
|
+
export declare interface ConnectOptions {
|
|
25
|
+
/** How long to wait for filex (default 10 s). */
|
|
26
|
+
timeoutMs?: number;
|
|
27
|
+
/**
|
|
28
|
+
* Put filex's colours (`--fe-*`), `lang`, `dir` and `data-theme` on
|
|
29
|
+
* `<html>`, and keep them current (default true).
|
|
30
|
+
*/
|
|
31
|
+
applyTheme?: boolean;
|
|
32
|
+
/** Ctrl/Cmd+S inside the interface runs the save handler (default true). */
|
|
33
|
+
saveShortcut?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** `file.saveAs`: a NEW file, somewhere the person picks in filex's own dialog. */
|
|
37
|
+
/**
|
|
38
|
+
* `ui.download`: a file for the person's own disk. filex does it (a sandboxed
|
|
39
|
+
* frame cannot download), with the app's `ui:download` grant, on a gesture in
|
|
40
|
+
* the frame or the person's yes, never over LIMITS.maxDownloadBytes.
|
|
41
|
+
*/
|
|
42
|
+
export declare interface DownloadParams {
|
|
43
|
+
/** The file name offered — a name, not a path. */
|
|
44
|
+
name: string;
|
|
45
|
+
/** A transferred ReadableStream<Uint8Array>, an ArrayBuffer or a string. */
|
|
46
|
+
data: ReadableStream<Uint8Array> | ArrayBuffer | string;
|
|
47
|
+
mime?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export declare interface DownloadResult {
|
|
51
|
+
saved: true;
|
|
52
|
+
size: number;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** `engine.call`: the app's own module, `ui_call` export. */
|
|
56
|
+
export declare interface EngineCallParams {
|
|
57
|
+
method: string;
|
|
58
|
+
params?: unknown;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Why a call was refused. */
|
|
62
|
+
export declare type ErrorCode =
|
|
63
|
+
/** The app was not granted what the call needs (files:write for a save…). */
|
|
64
|
+
'not_granted'
|
|
65
|
+
/** No such file (an index past the opened files), no such key. */
|
|
66
|
+
| 'not_found'
|
|
67
|
+
/** The file cannot be written: a read-only storage, a view-only opening. */
|
|
68
|
+
| 'read_only'
|
|
69
|
+
/** The call's parameters are wrong. */
|
|
70
|
+
| 'invalid'
|
|
71
|
+
/** Over a limit (LIMITS). */
|
|
72
|
+
| 'too_large'
|
|
73
|
+
/** The server could not be reached or refused; `message` says what. */
|
|
74
|
+
| 'failed'
|
|
75
|
+
/** The person said no (a confirm, a save-as picker closed). */
|
|
76
|
+
| 'cancelled'
|
|
77
|
+
/** The host does not offer this here (no engine, no save handler). */
|
|
78
|
+
| 'unavailable' | 'unknown_method';
|
|
79
|
+
|
|
80
|
+
export declare interface EventMessage {
|
|
81
|
+
event: HostEvent;
|
|
82
|
+
data?: unknown;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** One file the interface was opened with. Never a storage path. */
|
|
86
|
+
export declare interface FileInfo {
|
|
87
|
+
index: number;
|
|
88
|
+
name: string;
|
|
89
|
+
/** Lower-case, no dot. */
|
|
90
|
+
ext: string;
|
|
91
|
+
size: number;
|
|
92
|
+
mime: string;
|
|
93
|
+
/** It cannot be saved over: read-only storage, view-only opening, or the
|
|
94
|
+
* app holds no `files:write`. */
|
|
95
|
+
readOnly: boolean;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export declare interface FilexApp {
|
|
99
|
+
readonly session: Session;
|
|
100
|
+
/** The file at `index` (default the first) the interface was opened with. */
|
|
101
|
+
open(index?: number): Promise<OpenedFile>;
|
|
102
|
+
/** Save new content over an opened file. */
|
|
103
|
+
save(data: SaveData, opts?: {
|
|
104
|
+
index?: number;
|
|
105
|
+
mime?: string;
|
|
106
|
+
}): Promise<SaveResult>;
|
|
107
|
+
/** Save a NEW file; filex asks the person where (its own folder picker). */
|
|
108
|
+
saveAs(name: string, data: SaveData, mime?: string): Promise<SaveAsResult>;
|
|
109
|
+
/** Unsaved changes, or not: filex asks before the person leaves them. */
|
|
110
|
+
dirty(on: boolean): void;
|
|
111
|
+
title(text: string): void;
|
|
112
|
+
toast(text: string, tone?: ToastParams['tone']): void;
|
|
113
|
+
confirm(opts: ConfirmParams | string): Promise<boolean>;
|
|
114
|
+
/** Ask filex to close the interface (it asks first when there are changes). */
|
|
115
|
+
close(): void;
|
|
116
|
+
/** Put text on the clipboard (filex does it, the same way in every browser). */
|
|
117
|
+
copy(text: string): Promise<void>;
|
|
118
|
+
/**
|
|
119
|
+
* Hand the person a file for their own disk (the app needs `ui.download`
|
|
120
|
+
* in its manifest). Call it from a click or a key press: without one,
|
|
121
|
+
* filex asks the person first. `cancelled` when they say no.
|
|
122
|
+
*/
|
|
123
|
+
download(name: string, data: SaveData, mime?: string): Promise<DownloadResult>;
|
|
124
|
+
/** Call the app's own module (`ui_call` export) with the opened files. */
|
|
125
|
+
call<T = unknown>(method: string, params?: unknown): Promise<T>;
|
|
126
|
+
/** Queue one of the app's actions on the opened files; answers the op. */
|
|
127
|
+
submit(action: string, params?: Record<string, unknown>): Promise<{
|
|
128
|
+
op: unknown;
|
|
129
|
+
}>;
|
|
130
|
+
/** This person's small store for this app (JSON values, 8 KiB each). */
|
|
131
|
+
state: {
|
|
132
|
+
get<T = unknown>(key: string): Promise<T | undefined>;
|
|
133
|
+
set(key: string, value: unknown): Promise<void>;
|
|
134
|
+
};
|
|
135
|
+
/** Listen to filex. Returns the unsubscribe. */
|
|
136
|
+
on(event: HostEvent, handler: (data: unknown) => void): () => void;
|
|
137
|
+
/**
|
|
138
|
+
* What to save when filex asks (its Save button, a draft's "Save to disk",
|
|
139
|
+
* Ctrl+S): return the document and the SDK saves it over the opened file;
|
|
140
|
+
* return nothing if the handler saved by itself.
|
|
141
|
+
*/
|
|
142
|
+
onSave(handler: () => SaveData | void | Promise<SaveData | void>): () => void;
|
|
143
|
+
/** Raw call, for a method this SDK has no helper for. */
|
|
144
|
+
request<T = unknown>(method: Method, params?: unknown, transfer?: Transferable[]): Promise<T>;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** A call filex refused, with the reason as a code. */
|
|
148
|
+
export declare class FilexError extends Error {
|
|
149
|
+
readonly code: ErrorCode;
|
|
150
|
+
constructor(err: BridgeError);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** The app's first message, to `window.parent`. */
|
|
154
|
+
export declare const HELLO = "filex:hello";
|
|
155
|
+
|
|
156
|
+
export declare interface HelloMessage {
|
|
157
|
+
type: typeof HELLO;
|
|
158
|
+
v: number;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** What the host tells the app, unasked. */
|
|
162
|
+
export declare type HostEvent =
|
|
163
|
+
/** The interface's colours changed (light/dark, palette). `data: Theme`. */
|
|
164
|
+
'theme'
|
|
165
|
+
/** The reader's language changed. `data: {locale, dir}`. */
|
|
166
|
+
| 'locale'
|
|
167
|
+
/** The open file changed underneath (another tab, another person). */
|
|
168
|
+
| 'file.changed'
|
|
169
|
+
/** The person closed the frame's surroundings; last chance to say so. */
|
|
170
|
+
| 'close.request'
|
|
171
|
+
/** An administrator installed a new version of this app: reload to use it. */
|
|
172
|
+
| 'app.updated';
|
|
173
|
+
|
|
174
|
+
/** What the host may ask the app (and wait for). */
|
|
175
|
+
export declare type HostRequest =
|
|
176
|
+
/** Save now (the host's Save button, a draft's "Save to disk"): the app
|
|
177
|
+
* hands its document to `file.save` and answers when it is written. */
|
|
178
|
+
'save';
|
|
179
|
+
|
|
180
|
+
export declare interface HostRequestMessage {
|
|
181
|
+
hid: number;
|
|
182
|
+
request: HostRequest;
|
|
183
|
+
params?: unknown;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export declare interface HostResponseMessage {
|
|
187
|
+
hid: number;
|
|
188
|
+
result?: unknown;
|
|
189
|
+
error?: BridgeError;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export declare function isHello(d: unknown): d is HelloMessage;
|
|
193
|
+
|
|
194
|
+
export declare function isKnownMethod(m: string): m is Method;
|
|
195
|
+
|
|
196
|
+
export declare function isPortMessage(d: unknown): d is PortMessage;
|
|
197
|
+
|
|
198
|
+
export declare function isRequest(d: unknown): d is RequestMessage;
|
|
199
|
+
|
|
200
|
+
/** `job.submit`: queue one of the app's actions on the opened files. */
|
|
201
|
+
export declare interface JobSubmitParams {
|
|
202
|
+
action: string;
|
|
203
|
+
params?: Record<string, unknown>;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export declare const LIMITS: {
|
|
207
|
+
/** `file.read({as: 'text'})` refuses a file larger than this; read a stream. */
|
|
208
|
+
readonly maxTextBytes: number;
|
|
209
|
+
/** One `state.set` value, as JSON. */
|
|
210
|
+
readonly maxStateBytes: number;
|
|
211
|
+
/** A `ui.toast` / `ui.title` text, in characters. */
|
|
212
|
+
readonly maxLineChars: 300;
|
|
213
|
+
/** How long the SDK waits for the host's port. */
|
|
214
|
+
readonly connectTimeoutMs: 10000;
|
|
215
|
+
/** The largest file `ui.download` hands the person (streamed or not). */
|
|
216
|
+
readonly maxDownloadBytes: number;
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
/** What the app may ask the host. Anything else is `unknown_method`. */
|
|
220
|
+
export declare type Method = 'session.get' | 'file.read' | 'file.save' | 'file.saveAs' | 'ui.dirty' | 'ui.title' | 'ui.toast' | 'ui.confirm' | 'ui.close' | 'clipboard.write' | 'ui.download' | 'engine.call' | 'job.submit' | 'state.get' | 'state.set';
|
|
221
|
+
|
|
222
|
+
export declare const METHODS: readonly Method[];
|
|
223
|
+
|
|
224
|
+
/** One opened file, read the way the app needs it. */
|
|
225
|
+
export declare interface OpenedFile extends FileInfo {
|
|
226
|
+
text(): Promise<string>;
|
|
227
|
+
bytes(): Promise<ArrayBuffer>;
|
|
228
|
+
stream(): Promise<ReadableStream<Uint8Array>>;
|
|
229
|
+
/** Save new content over this file (a new version, or the draft it is). */
|
|
230
|
+
save(data: SaveData, mime?: string): Promise<SaveResult>;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** The host's answer, carrying the port. */
|
|
234
|
+
export declare const PORT = "filex:port";
|
|
235
|
+
|
|
236
|
+
export declare interface PortMessage {
|
|
237
|
+
type: typeof PORT;
|
|
238
|
+
v: number;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** `file.read` params. */
|
|
242
|
+
export declare interface ReadParams {
|
|
243
|
+
index?: number;
|
|
244
|
+
/** `stream` (a transferred ReadableStream<Uint8Array>), `bytes` (an
|
|
245
|
+
* ArrayBuffer), or `text` (UTF-8, at most LIMITS.maxTextBytes). */
|
|
246
|
+
as?: 'stream' | 'bytes' | 'text';
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export declare interface ReadResult {
|
|
250
|
+
name: string;
|
|
251
|
+
size: number;
|
|
252
|
+
mime: string;
|
|
253
|
+
stream?: ReadableStream<Uint8Array>;
|
|
254
|
+
bytes?: ArrayBuffer;
|
|
255
|
+
text?: string;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export declare interface RequestMessage {
|
|
259
|
+
id: number;
|
|
260
|
+
method: Method;
|
|
261
|
+
params?: unknown;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export declare interface ResponseMessage {
|
|
265
|
+
id: number;
|
|
266
|
+
result?: unknown;
|
|
267
|
+
error?: BridgeError;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export declare interface SaveAsParams {
|
|
271
|
+
name: string;
|
|
272
|
+
data: ReadableStream<Uint8Array> | ArrayBuffer | string;
|
|
273
|
+
mime?: string;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export declare interface SaveAsResult {
|
|
277
|
+
saved: true;
|
|
278
|
+
name: string;
|
|
279
|
+
size: number;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/** Content the app hands to a save. */
|
|
283
|
+
export declare type SaveData = string | Blob | ArrayBuffer | ArrayBufferView | ReadableStream<Uint8Array>;
|
|
284
|
+
|
|
285
|
+
/** `file.save` params: the new content of the opened file. */
|
|
286
|
+
export declare interface SaveParams {
|
|
287
|
+
index?: number;
|
|
288
|
+
/** A transferred ReadableStream<Uint8Array>, an ArrayBuffer or a string. */
|
|
289
|
+
data: ReadableStream<Uint8Array> | ArrayBuffer | string;
|
|
290
|
+
mime?: string;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export declare interface SaveResult {
|
|
294
|
+
saved: true;
|
|
295
|
+
size: number;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/** What `session.get` answers — what the app knows about where it runs. */
|
|
299
|
+
export declare interface Session {
|
|
300
|
+
v: number;
|
|
301
|
+
app: {
|
|
302
|
+
name: string;
|
|
303
|
+
version: string;
|
|
304
|
+
};
|
|
305
|
+
view: {
|
|
306
|
+
id: string;
|
|
307
|
+
placement: 'modal' | 'page' | 'inspector' | 'home' | 'viewer';
|
|
308
|
+
};
|
|
309
|
+
/** The reader's language tag (`tr`, `pt-br`) and its direction. */
|
|
310
|
+
locale: string;
|
|
311
|
+
dir: 'ltr' | 'rtl';
|
|
312
|
+
theme: Theme;
|
|
313
|
+
/** The person's display name; never their e-mail, never a token. */
|
|
314
|
+
user: {
|
|
315
|
+
name: string;
|
|
316
|
+
};
|
|
317
|
+
files: FileInfo[];
|
|
318
|
+
/** The permissions this app was granted — so it can hide what it may not do. */
|
|
319
|
+
grants: string[];
|
|
320
|
+
/** The administrator's non-secret settings for this app (grant `settings`). */
|
|
321
|
+
settings?: Record<string, string>;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/** The interface's look: `mode` and filex's own `--fe-*` custom properties. */
|
|
325
|
+
export declare interface Theme {
|
|
326
|
+
mode: 'light' | 'dark';
|
|
327
|
+
/** `--fe-bg`, `--fe-text`, `--fe-accent`… — the SDK sets them on `<html>`. */
|
|
328
|
+
tokens: Record<string, string>;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export declare interface ToastParams {
|
|
332
|
+
text: string;
|
|
333
|
+
tone?: 'info' | 'success' | 'warning' | 'error';
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export { }
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@brftech/filex-app-ui",
|
|
3
|
+
"version": "0.48.1",
|
|
4
|
+
"description": "filex app interface SDK: the bridge between an app's own sandboxed interface and filex (open, save, dirty, call the app's module)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/filex-app-ui.js",
|
|
7
|
+
"module": "./dist/filex-app-ui.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/filex-app-ui.js"
|
|
13
|
+
},
|
|
14
|
+
"./protocol": {
|
|
15
|
+
"types": "./src/protocol.ts",
|
|
16
|
+
"import": "./src/protocol.ts"
|
|
17
|
+
},
|
|
18
|
+
"./iife": "./dist/filex-app-ui.iife.js"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"src",
|
|
23
|
+
"README.md"
|
|
24
|
+
],
|
|
25
|
+
"sideEffects": false,
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@types/node": "^22.7.0",
|
|
28
|
+
"rimraf": "^6.0.1",
|
|
29
|
+
"typescript": "^5.6.0",
|
|
30
|
+
"vite": "^5.4.10",
|
|
31
|
+
"vite-plugin-dts": "^4.3.0"
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"filex",
|
|
38
|
+
"file-manager",
|
|
39
|
+
"plugin",
|
|
40
|
+
"sdk",
|
|
41
|
+
"brftech"
|
|
42
|
+
],
|
|
43
|
+
"author": "BRF Tech <hello@brf.sh>",
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "git+https://github.com/brf-tech/filex.git",
|
|
48
|
+
"directory": "packages/app-ui"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "tsc --noEmit && vite build",
|
|
52
|
+
"build:no-dts": "vite build",
|
|
53
|
+
"typecheck": "tsc --noEmit",
|
|
54
|
+
"clean": "rimraf dist"
|
|
55
|
+
}
|
|
56
|
+
}
|