@formicoidea/labre-core 0.46.1 → 0.47.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/dist/_pkgs/affine-block-bookmark/bookmark-block.js +5 -2
- package/dist/_pkgs/affine-block-data-view/columns/index.d.ts +1 -1
- package/dist/_pkgs/affine-block-database/data-source.d.ts +1 -1
- package/dist/_pkgs/affine-block-database/properties/converts.d.ts +4 -4
- package/dist/_pkgs/affine-block-database/properties/index.d.ts +1 -1
- package/dist/_pkgs/affine-block-database/properties/link/cell-renderer.js +2 -2
- package/dist/_pkgs/affine-block-embed/embed-figma-block/embed-figma-block.js +3 -1
- package/dist/_pkgs/affine-block-embed/embed-github-block/embed-github-block.js +3 -1
- package/dist/_pkgs/affine-block-embed/embed-loom-block/embed-loom-block.js +3 -1
- package/dist/_pkgs/affine-block-embed/embed-youtube-block/embed-youtube-block.js +3 -1
- package/dist/_pkgs/affine-block-frame/commands.js +41 -1
- package/dist/_pkgs/affine-block-frame/frame-manager.d.ts +1 -1
- package/dist/_pkgs/affine-block-frame/view.js +7 -2
- package/dist/_pkgs/affine-block-note/components/edgeless-page-block-title.d.ts +7 -0
- package/dist/_pkgs/affine-block-note/components/edgeless-page-block-title.js +15 -1
- package/dist/_pkgs/affine-block-surface/extensions/canvas-grid/commands.js +20 -6
- package/dist/_pkgs/affine-block-surface/extensions/export-manager/commands.d.ts +2 -0
- package/dist/_pkgs/affine-block-surface/extensions/export-manager/commands.js +79 -0
- package/dist/_pkgs/affine-block-surface/extensions/export-manager/export-manager.d.ts +18 -1
- package/dist/_pkgs/affine-block-surface/extensions/export-manager/export-manager.js +53 -30
- package/dist/_pkgs/affine-block-surface/extensions/export-manager/index.d.ts +1 -0
- package/dist/_pkgs/affine-block-surface/extensions/export-manager/index.js +1 -0
- package/dist/_pkgs/affine-block-surface/extensions/reading-toolbar.js +12 -12
- package/dist/_pkgs/affine-block-surface/view.js +11 -2
- package/dist/_pkgs/affine-components/highlight-dropdown-menu/highlight-duotone-icon.js +8 -8
- package/dist/_pkgs/affine-components/highlight-dropdown-menu/text-duotone-icon.js +11 -11
- package/dist/_pkgs/affine-components/link-preview/link.js +3 -3
- package/dist/_pkgs/affine-fragment-doc-title/doc-title.d.ts +8 -0
- package/dist/_pkgs/affine-fragment-doc-title/doc-title.js +65 -4
- package/dist/_pkgs/affine-fragment-doc-title/effects.d.ts +2 -0
- package/dist/_pkgs/affine-fragment-doc-title/effects.js +2 -0
- package/dist/_pkgs/affine-fragment-doc-title/index.d.ts +2 -0
- package/dist/_pkgs/affine-fragment-doc-title/index.js +2 -0
- package/dist/_pkgs/affine-fragment-doc-title/title-band/format.d.ts +69 -0
- package/dist/_pkgs/affine-fragment-doc-title/title-band/format.js +182 -0
- package/dist/_pkgs/affine-fragment-doc-title/title-band/title-band.d.ts +62 -0
- package/dist/_pkgs/affine-fragment-doc-title/title-band/title-band.js +649 -0
- package/dist/_pkgs/affine-fragment-doc-title/translations.d.ts +21 -0
- package/dist/_pkgs/affine-fragment-doc-title/translations.js +58 -0
- package/dist/_pkgs/affine-inline-footnote/footnote-node/footnote-node.js +3 -0
- package/dist/_pkgs/affine-inline-link/adapters/markdown/markdown-inline.js +5 -0
- package/dist/_pkgs/affine-inline-link/link-node/affine-link.js +6 -3
- package/dist/_pkgs/affine-shared/services/index.d.ts +2 -0
- package/dist/_pkgs/affine-shared/services/index.js +2 -0
- package/dist/_pkgs/affine-shared/services/properties-panel-service.d.ts +50 -0
- package/dist/_pkgs/affine-shared/services/properties-panel-service.js +17 -0
- package/dist/_pkgs/affine-shared/services/title-band-service.d.ts +124 -0
- package/dist/_pkgs/affine-shared/services/title-band-service.js +17 -0
- package/dist/_pkgs/affine-shared/utils/url.d.ts +15 -0
- package/dist/_pkgs/affine-shared/utils/url.js +30 -0
- package/dist/_pkgs/affine-widget-edgeless-selected-rect/edgeless-element-link.js +2 -2
- package/dist/_pkgs/affine-widget-edgeless-zoom-toolbar/index.js +26 -26
- package/dist/_pkgs/affine-widget-keyboard-toolbar/icons.js +98 -98
- package/dist/_pkgs/affine-widget-page-dragging-area/index.js +10 -10
- package/dist/_pkgs/affine-widget-viewport-overlay/index.js +13 -13
- package/dist/_pkgs/data-view/core/utils/menu-title.js +17 -17
- package/dist/_pkgs/data-view/property-presets/converts.d.ts +2 -2
- package/dist/_pkgs/data-view/property-presets/index.d.ts +1 -1
- package/dist/_pkgs/data-view/property-presets/number/cell-renderer.d.ts +1 -1
- package/dist/_pkgs/data-view/property-presets/number/define.d.ts +1 -1
- package/dist/_pkgs/data-view/property-presets/number/types.d.ts +2 -2
- package/dist/_pkgs/data-view/property-presets/pure-index.d.ts +2 -1
- package/dist/_pkgs/data-view/property-presets/pure-index.js +4 -0
- package/dist/_pkgs/data-view/view-presets/table/pc-virtual/group/top/group-title.js +18 -18
- package/dist/_pkgs/std/extension/command-registry.d.ts +50 -1
- package/dist/_pkgs/std/extension/command-registry.js +26 -7
- package/dist/commands.js +2 -1
- package/dist/host-panels.d.ts +83 -2
- package/dist/host-panels.js +82 -1
- package/package.json +1 -1
- package/dist/_pkgs/affine-block-surface/extensions/export-manager/file-exporter.d.ts +0 -26
- package/dist/_pkgs/affine-block-surface/extensions/export-manager/file-exporter.js +0 -73
package/dist/host-panels.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type FramePreview } from './_pkgs/affine-fragment-frame-panel/index.js';
|
|
2
2
|
import type { FrameBlockModel } from './_pkgs/affine-model/index.js';
|
|
3
|
-
import type
|
|
3
|
+
import { type BlockStdScope, type CommandFile } from './_pkgs/std/index.js';
|
|
4
4
|
/**
|
|
5
5
|
* `@labre/affine/host-panels` — the verbs a host's own panel calls (ADR 0034
|
|
6
6
|
* §4, docs/integrate/08-host-panels.md).
|
|
@@ -40,14 +40,23 @@ import type { BlockStdScope } from './_pkgs/std/index.js';
|
|
|
40
40
|
* cannot find here is a gap in this module, not a reason to import a widget
|
|
41
41
|
* package.
|
|
42
42
|
*
|
|
43
|
+
* **The view actions** (amendment of 2026-10-13) are the same idea for a
|
|
44
|
+
* host's MENU rather than its panel: `getViewActions` projects the commands
|
|
45
|
+
* on the `'view-menu'` surface — the grid, the PNG and PDF export, Present —
|
|
46
|
+
* into rows of data (label, icon key, check, disabled), and `runViewAction`
|
|
47
|
+
* runs one and hands back the file an export produced. The host draws the
|
|
48
|
+
* rows (Labre's header ⋯, section "This view"); nothing here renders.
|
|
49
|
+
*
|
|
43
50
|
* ponytail: `frameList` is a plain read, not a signal of the frame list; a
|
|
44
51
|
* host follows reorders through `std.store.slots.blockUpdated` (08 says how).
|
|
45
52
|
* A signal of the frame list is the upgrade path if two hosts ask.
|
|
46
53
|
*/
|
|
47
|
-
export { type AnyCommandDescriptor, type CommandInvocation, type CommandOwner, type CommandSurface, getCommandIcon, getCommandsForSurface, getRegisteredCommands, runCommand, } from './_pkgs/std/index.js';
|
|
54
|
+
export { type AnyCommandDescriptor, type CommandFile, type CommandInvocation, type CommandOwner, type CommandSurface, getCommandIcon, getCommandsForSurface, getRegisteredCommands, runCommand, runCommandAsync, } from './_pkgs/std/index.js';
|
|
48
55
|
export { armArtefact } from './_pkgs/affine-widget-edgeless-toolbar/index.js';
|
|
49
56
|
export { type SelectionPaneNode, selectionPaneTree, } from './_pkgs/affine-block-surface/index.js';
|
|
50
57
|
export { frameCommands, type ReorderFramesParams, reorderFramesParams, } from './_pkgs/affine-block-frame/index.js';
|
|
58
|
+
export { formatPropertyValue, isPropertyValueEmpty, } from './_pkgs/affine-fragment-doc-title/index.js';
|
|
59
|
+
export type { PropertiesPanelRequest, PropertiesPanelService, TitleBandNotShownPage, TitleBandProperty, TitleBandService, TitleBandSheetState, } from './_pkgs/affine-shared/services/index.js';
|
|
51
60
|
/**
|
|
52
61
|
* The document's frames in presentation order, first slide first — the order
|
|
53
62
|
* `canvas.frame.reorder` writes and the presentation walks. Works in page mode
|
|
@@ -88,3 +97,75 @@ export declare function createFramePreview(std: BlockStdScope, frame: FrameBlock
|
|
|
88
97
|
height?: number;
|
|
89
98
|
fillScreen?: boolean;
|
|
90
99
|
}): FramePreview;
|
|
100
|
+
/**
|
|
101
|
+
* One entry of the current view's own actions — what a host draws in the
|
|
102
|
+
* "This view" section of its menu (ADR 0034, amendment of 2026-10-13). Data
|
|
103
|
+
* only: the host owns the menu, the row, the spinner and the toast; the
|
|
104
|
+
* library owns what the entry does.
|
|
105
|
+
*/
|
|
106
|
+
export interface ViewAction {
|
|
107
|
+
/**
|
|
108
|
+
* The command id, stable across versions (`canvas.grid.toggle`): the key a
|
|
109
|
+
* host maps its own icon, test id or placement to, and what it passes back
|
|
110
|
+
* to {@link runViewAction}.
|
|
111
|
+
*/
|
|
112
|
+
id: string;
|
|
113
|
+
/**
|
|
114
|
+
* The group the entry belongs to, from the command's `category`:
|
|
115
|
+
* `'display'` (the grid), `'export'` (PNG, PDF), `'present'`. A host draws
|
|
116
|
+
* a separator between groups, or places a group elsewhere (Labre keeps
|
|
117
|
+
* "Present" in its header). Open: a later minor may add a group.
|
|
118
|
+
*/
|
|
119
|
+
section: string;
|
|
120
|
+
/** Resolved through the host's `TranslationExtension`, else English. */
|
|
121
|
+
label: string;
|
|
122
|
+
/** The `com.labre.*` key behind {@link label}, for a host that translates itself. */
|
|
123
|
+
labelKey: string;
|
|
124
|
+
/** Stable asset key; `getCommandIcon(std, iconKey)` is the library's icon. */
|
|
125
|
+
iconKey?: string;
|
|
126
|
+
/** Present only on a toggle: the state a check mark shows. */
|
|
127
|
+
checked?: boolean;
|
|
128
|
+
/**
|
|
129
|
+
* Listed but refused now (a write on a read-only document): draw it
|
|
130
|
+
* disabled. An entry that does not apply at all is not listed.
|
|
131
|
+
*/
|
|
132
|
+
disabled: boolean;
|
|
133
|
+
}
|
|
134
|
+
/** What {@link runViewAction} hands back. */
|
|
135
|
+
export interface ViewActionResult {
|
|
136
|
+
/**
|
|
137
|
+
* The file an export produced — the host downloads it, shares it, uploads
|
|
138
|
+
* it. Absent for an action that makes none, and for an export that found
|
|
139
|
+
* nothing to draw.
|
|
140
|
+
*/
|
|
141
|
+
file?: CommandFile;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* The actions proper to the view the editor shows now, in their declared
|
|
145
|
+
* order: the commands registered on the `'view-menu'` surface whose `scope`
|
|
146
|
+
* is this mode (or `'global'`) and whose `when` holds.
|
|
147
|
+
*
|
|
148
|
+
* `mode` is the view the host shows; omitted, it is read from
|
|
149
|
+
* `DocModeProvider`, and a provider that does not know (the library default
|
|
150
|
+
* answers `null`) leaves the choice to registration — the canvas commands are
|
|
151
|
+
* registered on the edgeless scope only. Flags apply by construction: a
|
|
152
|
+
* `frame: false` assembly registers no "Present", so it is not listed.
|
|
153
|
+
*
|
|
154
|
+
* Reactive when read inside an `effect` or a `computed`: `checked`, `when`
|
|
155
|
+
* and the read-only refusal read signals, so the list follows the grid, a
|
|
156
|
+
* toggle, a peer's save or a document turning read-only. Read it again after a
|
|
157
|
+
* mode switch: the `std` is rebuilt.
|
|
158
|
+
*/
|
|
159
|
+
export declare function getViewActions(std: BlockStdScope, mode?: 'page' | 'edgeless'): ViewAction[];
|
|
160
|
+
/**
|
|
161
|
+
* Run one entry of {@link getViewActions} by its id, through `runCommand`'s
|
|
162
|
+
* bottleneck (usage and telemetry recorded once), and wait for it.
|
|
163
|
+
*
|
|
164
|
+
* Resolves when the action is done — after the export has rendered — with the
|
|
165
|
+
* file it produced, if any: the library renders, the host decides where the
|
|
166
|
+
* bytes go, and nothing is downloaded behind its back. Rejects when the action
|
|
167
|
+
* fails, so the host can say so. An id that is not a view action here, an
|
|
168
|
+
* entry that no longer applies or a refused one runs nothing and resolves
|
|
169
|
+
* with no file.
|
|
170
|
+
*/
|
|
171
|
+
export declare function runViewAction(std: BlockStdScope, id: string): Promise<ViewActionResult>;
|
package/dist/host-panels.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { EdgelessFrameManagerIdentifier } from './_pkgs/affine-block-frame/index.js';
|
|
2
2
|
import { AFFINE_FRAME_PREVIEW, } from './_pkgs/affine-fragment-frame-panel/index.js';
|
|
3
|
+
import { DocModeProvider, translateKey } from './_pkgs/affine-shared/services/index.js';
|
|
4
|
+
import { getRegisteredCommands, isCommandAvailable, runCommandAsync, } from './_pkgs/std/index.js';
|
|
3
5
|
import { GfxControllerIdentifier } from './_pkgs/std/gfx/index.js';
|
|
4
6
|
/**
|
|
5
7
|
* `@labre/affine/host-panels` — the verbs a host's own panel calls (ADR 0034
|
|
@@ -40,14 +42,32 @@ import { GfxControllerIdentifier } from './_pkgs/std/gfx/index.js';
|
|
|
40
42
|
* cannot find here is a gap in this module, not a reason to import a widget
|
|
41
43
|
* package.
|
|
42
44
|
*
|
|
45
|
+
* **The view actions** (amendment of 2026-10-13) are the same idea for a
|
|
46
|
+
* host's MENU rather than its panel: `getViewActions` projects the commands
|
|
47
|
+
* on the `'view-menu'` surface — the grid, the PNG and PDF export, Present —
|
|
48
|
+
* into rows of data (label, icon key, check, disabled), and `runViewAction`
|
|
49
|
+
* runs one and hands back the file an export produced. The host draws the
|
|
50
|
+
* rows (Labre's header ⋯, section "This view"); nothing here renders.
|
|
51
|
+
*
|
|
43
52
|
* ponytail: `frameList` is a plain read, not a signal of the frame list; a
|
|
44
53
|
* host follows reorders through `std.store.slots.blockUpdated` (08 says how).
|
|
45
54
|
* A signal of the frame list is the upgrade path if two hosts ask.
|
|
46
55
|
*/
|
|
47
|
-
export { getCommandIcon, getCommandsForSurface, getRegisteredCommands, runCommand, } from './_pkgs/std/index.js';
|
|
56
|
+
export { getCommandIcon, getCommandsForSurface, getRegisteredCommands, runCommand, runCommandAsync, } from './_pkgs/std/index.js';
|
|
48
57
|
export { armArtefact } from './_pkgs/affine-widget-edgeless-toolbar/index.js';
|
|
49
58
|
export { selectionPaneTree, } from './_pkgs/affine-block-surface/index.js';
|
|
50
59
|
export { frameCommands, reorderFramesParams, } from './_pkgs/affine-block-frame/index.js';
|
|
60
|
+
/*
|
|
61
|
+
* The properties panel (ADR 0035 §9b and §11). The editor asks the host to
|
|
62
|
+
* show it through `PropertiesPanelExtension` and feeds the title band through
|
|
63
|
+
* `TitleBandExtension` (both registered from `@labre/affine/shared/services`,
|
|
64
|
+
* docs/integrate/04-host-seams.md); a host's own panel reads and writes the
|
|
65
|
+
* same sheet it hands the band, from its own store. What it takes from here
|
|
66
|
+
* is the band's vocabulary, so the panel and the chips read the same: the
|
|
67
|
+
* types of a sheet and of a panel request, and the text a chip draws for a
|
|
68
|
+
* value.
|
|
69
|
+
*/
|
|
70
|
+
export { formatPropertyValue, isPropertyValueEmpty, } from './_pkgs/affine-fragment-doc-title/index.js';
|
|
51
71
|
/**
|
|
52
72
|
* The document's frames in presentation order, first slide first — the order
|
|
53
73
|
* `canvas.frame.reorder` writes and the presentation walks. Works in page mode
|
|
@@ -109,3 +129,64 @@ export function createFramePreview(std, frame, options = {}) {
|
|
|
109
129
|
preview.fillScreen = options.fillScreen;
|
|
110
130
|
return preview;
|
|
111
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* The actions proper to the view the editor shows now, in their declared
|
|
134
|
+
* order: the commands registered on the `'view-menu'` surface whose `scope`
|
|
135
|
+
* is this mode (or `'global'`) and whose `when` holds.
|
|
136
|
+
*
|
|
137
|
+
* `mode` is the view the host shows; omitted, it is read from
|
|
138
|
+
* `DocModeProvider`, and a provider that does not know (the library default
|
|
139
|
+
* answers `null`) leaves the choice to registration — the canvas commands are
|
|
140
|
+
* registered on the edgeless scope only. Flags apply by construction: a
|
|
141
|
+
* `frame: false` assembly registers no "Present", so it is not listed.
|
|
142
|
+
*
|
|
143
|
+
* Reactive when read inside an `effect` or a `computed`: `checked`, `when`
|
|
144
|
+
* and the read-only refusal read signals, so the list follows the grid, a
|
|
145
|
+
* toggle, a peer's save or a document turning read-only. Read it again after a
|
|
146
|
+
* mode switch: the `std` is rebuilt.
|
|
147
|
+
*/
|
|
148
|
+
export function getViewActions(std, mode) {
|
|
149
|
+
const view = mode ?? std.getOptional(DocModeProvider)?.getEditorMode();
|
|
150
|
+
return getRegisteredCommands(std)
|
|
151
|
+
.filter(command => command.surfaces.includes('view-menu'))
|
|
152
|
+
.filter(command => command.scope === 'global' || !view || command.scope === view)
|
|
153
|
+
.filter(command => !command.when || command.when(std))
|
|
154
|
+
.sort((a, b) => (a.order ?? 0) - (b.order ?? 0))
|
|
155
|
+
.map(command => ({
|
|
156
|
+
id: command.id,
|
|
157
|
+
section: command.category ?? 'display',
|
|
158
|
+
label: translateKey(std, command.labelKey, command.labelFallback),
|
|
159
|
+
labelKey: command.labelKey,
|
|
160
|
+
...(command.iconKey ? { iconKey: command.iconKey } : {}),
|
|
161
|
+
...(command.checked ? { checked: command.checked(std) } : {}),
|
|
162
|
+
disabled: !isCommandAvailable(std, command),
|
|
163
|
+
}));
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Run one entry of {@link getViewActions} by its id, through `runCommand`'s
|
|
167
|
+
* bottleneck (usage and telemetry recorded once), and wait for it.
|
|
168
|
+
*
|
|
169
|
+
* Resolves when the action is done — after the export has rendered — with the
|
|
170
|
+
* file it produced, if any: the library renders, the host decides where the
|
|
171
|
+
* bytes go, and nothing is downloaded behind its back. Rejects when the action
|
|
172
|
+
* fails, so the host can say so. An id that is not a view action here, an
|
|
173
|
+
* entry that no longer applies or a refused one runs nothing and resolves
|
|
174
|
+
* with no file.
|
|
175
|
+
*/
|
|
176
|
+
export async function runViewAction(std, id) {
|
|
177
|
+
const command = getRegisteredCommands(std).find(candidate => candidate.id === id && candidate.surfaces.includes('view-menu'));
|
|
178
|
+
if (!command)
|
|
179
|
+
return {};
|
|
180
|
+
if (command.when && !command.when(std))
|
|
181
|
+
return {};
|
|
182
|
+
if (!isCommandAvailable(std, command))
|
|
183
|
+
return {};
|
|
184
|
+
const result = {};
|
|
185
|
+
const params = {
|
|
186
|
+
deliver: file => {
|
|
187
|
+
result.file = file;
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
await runCommandAsync(std, command, { surface: 'view-menu', source: 'context-menu' }, params);
|
|
191
|
+
return result;
|
|
192
|
+
}
|
package/package.json
CHANGED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/** Tools for exporting files to device. For example, via browser download. */
|
|
2
|
-
export declare const FileExporter: {
|
|
3
|
-
/**
|
|
4
|
-
* Create a download for the user's browser.
|
|
5
|
-
*
|
|
6
|
-
* @param filename
|
|
7
|
-
* @param text
|
|
8
|
-
* @param mimeType like `"text/plain"`, `"text/html"`, `"application/javascript"`, etc. See {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types mdn docs List of MIME types}.
|
|
9
|
-
*
|
|
10
|
-
* @remarks
|
|
11
|
-
* Only accepts data in utf-8 encoding (html files, javascript source, text files, etc).
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* const todoMDText = `# Todo items
|
|
15
|
-
* [ ] Item 1
|
|
16
|
-
* [ ] Item 2
|
|
17
|
-
* `
|
|
18
|
-
* FileExporter.exportFile("Todo list.md", todoMDText, "text/plain")
|
|
19
|
-
*
|
|
20
|
-
* @example
|
|
21
|
-
* const stateJsonContent = JSON.stringify({ a: 1, b: 2, c: 3 })
|
|
22
|
-
* FileExporter.exportFile("state.json", jsonContent, "application/json")
|
|
23
|
-
*/
|
|
24
|
-
exportFile(filename: string, dataURL: string): void;
|
|
25
|
-
exportPng(docTitle: string | undefined, dataURL: string): void;
|
|
26
|
-
};
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/* oxlint-disable no-control-regex */
|
|
2
|
-
// Context: Lean towards breaking out any localizable content into constants so it's
|
|
3
|
-
// easier to track content we may need to localize in the future. (i18n)
|
|
4
|
-
const UNTITLED_PAGE_NAME = 'Untitled';
|
|
5
|
-
/** Tools for exporting files to device. For example, via browser download. */
|
|
6
|
-
export const FileExporter = {
|
|
7
|
-
/**
|
|
8
|
-
* Create a download for the user's browser.
|
|
9
|
-
*
|
|
10
|
-
* @param filename
|
|
11
|
-
* @param text
|
|
12
|
-
* @param mimeType like `"text/plain"`, `"text/html"`, `"application/javascript"`, etc. See {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types mdn docs List of MIME types}.
|
|
13
|
-
*
|
|
14
|
-
* @remarks
|
|
15
|
-
* Only accepts data in utf-8 encoding (html files, javascript source, text files, etc).
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* const todoMDText = `# Todo items
|
|
19
|
-
* [ ] Item 1
|
|
20
|
-
* [ ] Item 2
|
|
21
|
-
* `
|
|
22
|
-
* FileExporter.exportFile("Todo list.md", todoMDText, "text/plain")
|
|
23
|
-
*
|
|
24
|
-
* @example
|
|
25
|
-
* const stateJsonContent = JSON.stringify({ a: 1, b: 2, c: 3 })
|
|
26
|
-
* FileExporter.exportFile("state.json", jsonContent, "application/json")
|
|
27
|
-
*/
|
|
28
|
-
exportFile(filename, dataURL) {
|
|
29
|
-
const element = document.createElement('a');
|
|
30
|
-
element.setAttribute('href', dataURL);
|
|
31
|
-
const safeFilename = getSafeFileName(filename);
|
|
32
|
-
element.setAttribute('download', safeFilename);
|
|
33
|
-
element.style.display = 'none';
|
|
34
|
-
document.body.append(element);
|
|
35
|
-
element.click();
|
|
36
|
-
element.remove();
|
|
37
|
-
},
|
|
38
|
-
exportPng(docTitle, dataURL) {
|
|
39
|
-
const title = docTitle?.trim() || UNTITLED_PAGE_NAME;
|
|
40
|
-
FileExporter.exportFile(title + '.png', dataURL);
|
|
41
|
-
},
|
|
42
|
-
};
|
|
43
|
-
function getSafeFileName(string) {
|
|
44
|
-
const replacement = ' ';
|
|
45
|
-
const filenameReservedRegex = /[<>:"/\\|?*\u0000-\u001F]/g;
|
|
46
|
-
const windowsReservedNameRegex = /^(con|prn|aux|nul|com\d|lpt\d)$/i;
|
|
47
|
-
const reControlChars = /[\u0000-\u001F\u0080-\u009F]/g;
|
|
48
|
-
const reTrailingPeriods = /\.+$/;
|
|
49
|
-
const allowedLength = 50;
|
|
50
|
-
function trimRepeated(string, target) {
|
|
51
|
-
const escapeStringRegexp = target
|
|
52
|
-
.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&')
|
|
53
|
-
.replace(/-/g, '\\x2d');
|
|
54
|
-
const regex = new RegExp(`(?:${escapeStringRegexp}){2,}`, 'g');
|
|
55
|
-
return string.replace(regex, target);
|
|
56
|
-
}
|
|
57
|
-
string = string
|
|
58
|
-
.normalize('NFD')
|
|
59
|
-
.replace(filenameReservedRegex, replacement)
|
|
60
|
-
.replace(reControlChars, replacement)
|
|
61
|
-
.replace(reTrailingPeriods, '');
|
|
62
|
-
string = trimRepeated(string, replacement);
|
|
63
|
-
string = windowsReservedNameRegex.test(string)
|
|
64
|
-
? string + replacement
|
|
65
|
-
: string;
|
|
66
|
-
const extIndex = string.lastIndexOf('.');
|
|
67
|
-
const filename = string.slice(0, extIndex).trim();
|
|
68
|
-
const extension = string.slice(extIndex);
|
|
69
|
-
string =
|
|
70
|
-
filename.slice(0, Math.max(1, allowedLength - extension.length)) +
|
|
71
|
-
extension;
|
|
72
|
-
return string;
|
|
73
|
-
}
|