@jini-ai/ui 0.4.6 → 0.4.8
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/README.md +33 -0
- package/dist/features/admin-widgets/components/InfoTip.d.ts +23 -2
- package/dist/features/admin-widgets/components/InfoTip.hooks.d.ts +22 -2
- package/dist/features/admin-widgets/components/InfoTip.hooks.js +75 -28
- package/dist/features/admin-widgets/components/InfoTip.js +12 -4
- package/dist/features/admin-widgets/components/InfoTip.rules.d.ts +72 -0
- package/dist/features/admin-widgets/components/InfoTip.rules.js +68 -0
- package/dist/features/admin-widgets/components/SuggestInput/SuggestInput.d.ts +36 -0
- package/dist/features/admin-widgets/components/SuggestInput/SuggestInput.hooks.d.ts +58 -0
- package/dist/features/admin-widgets/components/SuggestInput/SuggestInput.hooks.js +66 -0
- package/dist/features/admin-widgets/components/SuggestInput/SuggestInput.js +24 -0
- package/dist/features/admin-widgets/components/useDebouncedHtmlPreview.hooks.d.ts +23 -0
- package/dist/features/admin-widgets/components/useDebouncedHtmlPreview.hooks.js +28 -0
- package/dist/features/admin-widgets/index.d.ts +9 -0
- package/dist/features/admin-widgets/index.js +5 -0
- package/dist/features/execution/constants.js +11 -10
- package/dist/features/execution/http-adapter.d.ts +3 -1
- package/dist/features/execution/http-adapter.js +9 -4
- package/dist/features/execution/index.d.ts +1 -1
- package/dist/features/execution/index.js +1 -1
- package/dist/features/execution/react/components/ByokProviderForm.d.ts +18 -1
- package/dist/features/execution/react/components/ByokProviderForm.js +14 -16
- package/dist/features/execution/react/components/LocalCliAgentCard.d.ts +5 -1
- package/dist/features/execution/react/components/LocalCliAgentCard.js +12 -87
- package/dist/features/execution/react/components/LocalCliAgentList.d.ts +4 -1
- package/dist/features/execution/react/components/LocalCliAgentList.js +13 -4
- package/dist/features/execution/react/components/LocalCliAgentSettings.d.ts +20 -0
- package/dist/features/execution/react/components/LocalCliAgentSettings.js +20 -0
- package/dist/features/execution/react/hooks/use-byok-field-validation.hooks.d.ts +24 -0
- package/dist/features/execution/react/hooks/use-byok-field-validation.hooks.js +27 -0
- package/dist/features/execution/react/hooks/useExecutionTab.js +10 -1
- package/dist/features/execution/react/hooks/useLocalCliAgentCard.d.ts +48 -0
- package/dist/features/execution/react/hooks/useLocalCliAgentCard.js +110 -0
- package/dist/features/execution/react/hooks/useLocalCliAgentList.d.ts +19 -0
- package/dist/features/execution/react/hooks/useLocalCliAgentList.js +13 -0
- package/dist/features/execution/rules.d.ts +13 -0
- package/dist/features/execution/rules.js +14 -0
- package/dist/features/execution/types.d.ts +2 -0
- package/dist/features/html-editor/canvas-embed-placeholders.d.ts +13 -1
- package/dist/features/html-editor/canvas-embed-placeholders.js +61 -12
- package/dist/features/html-editor/dom.d.ts +5 -0
- package/dist/features/html-editor/dom.js +10 -0
- package/dist/features/html-editor/editor-blocks.d.ts +71 -0
- package/dist/features/html-editor/editor-blocks.js +185 -0
- package/dist/features/html-editor/editor-serialization.d.ts +24 -0
- package/dist/features/html-editor/editor-serialization.js +110 -0
- package/dist/features/html-editor/index.d.ts +5 -0
- package/dist/features/html-editor/index.js +3 -0
- package/dist/features/html-editor/protected-elements.d.ts +64 -0
- package/dist/features/html-editor/protected-elements.js +189 -0
- package/dist/features/html-editor/react/components/InteractiveHtmlEditor.d.ts +6 -4
- package/dist/features/html-editor/react/components/InteractiveHtmlEditor.js +3 -3
- package/dist/features/html-editor/react/hooks/useInteractiveHtmlEditor.d.ts +9 -1
- package/dist/features/html-editor/react/hooks/useInteractiveHtmlEditor.js +107 -196
- package/dist/features/html-editor/source-splice.d.ts +23 -0
- package/dist/features/html-editor/source-splice.js +45 -0
- package/dist/features/i18n/dictionaries/settings-dialog.ar.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.bn.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.de.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.en.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.es.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.fa.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.fr.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.hi.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.hu.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.id.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.it.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.ja.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.ko.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.pl.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.pt-BR.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.ru.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.th.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.tr.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.uk.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.ur.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.zh-CN.js +4 -0
- package/dist/features/i18n/dictionaries/settings-dialog.zh-TW.js +4 -0
- package/dist/features/interactive-ui/interactive-ui.css +1 -1
- package/dist/features/interactive-ui/providers/native/data-table.js +8 -1
- package/dist/features/interactive-ui/providers/native/data-table.manifest.d.ts +3 -5
- package/dist/features/interactive-ui/providers/native/data-table.manifest.js +5 -7
- package/dist/features/interactive-ui/providers/recharts/bar-chart.js +8 -3
- package/dist/features/interactive-ui/providers/recharts/chart-config.d.ts +40 -0
- package/dist/features/interactive-ui/providers/recharts/chart-config.js +32 -0
- package/dist/features/interactive-ui/providers/recharts/chart-tokens.d.ts +18 -12
- package/dist/features/interactive-ui/providers/recharts/chart-tokens.js +6 -11
- package/dist/features/interactive-ui/providers/recharts/chart.d.ts +31 -0
- package/dist/features/interactive-ui/providers/recharts/chart.js +34 -0
- package/dist/features/interactive-ui/providers/recharts/line-chart.js +10 -3
- package/dist/features/interactive-ui/providers/recharts/pie-chart.js +8 -3
- package/dist/features/interactive-ui/providers/shadcn/button.d.ts +1 -1
- package/dist/features/interactive-ui/providers/shadcn/data-table.d.ts +5 -1
- package/dist/features/interactive-ui/providers/shadcn/data-table.js +26 -2
- package/dist/features/interactive-ui/providers/shadcn/data-table.manifest.d.ts +8 -1
- package/dist/features/interactive-ui/providers/shadcn/data-table.manifest.js +6 -3
- package/dist/features/interactive-ui/table-data.d.ts +25 -0
- package/dist/features/interactive-ui/table-data.js +36 -0
- package/dist/features/mcp-ui/escape.js +2 -6
- package/dist/features/mcp-ui/sandbox-proxy-link-forwarder.d.ts +26 -0
- package/dist/features/mcp-ui/sandbox-proxy-link-forwarder.js +40 -0
- package/dist/features/mcp-ui/sandbox-proxy.d.ts +5 -1
- package/dist/features/mcp-ui/sandbox-proxy.js +11 -0
- package/dist/features/mcp-ui/surfaces/bridge.js +9 -3
- package/dist/features/mcp-ui/surfaces/document.d.ts +11 -4
- package/dist/features/mcp-ui/surfaces/document.js +25 -3
- package/dist/features/mcp-ui/surfaces/outcome.js +1 -1
- package/dist/features/media-providers/constants.d.ts +0 -7
- package/dist/features/media-providers/constants.js +7 -15
- package/dist/features/media-providers/react/components/MediaProvidersTab.js +3 -2
- package/dist/features/media-providers/react/hooks/useMediaProvidersTab.js +6 -3
- package/dist/features/memory/dependencies.d.ts +2 -0
- package/dist/features/memory/dependencies.js +9 -25
- package/dist/features/memory/index.d.ts +1 -0
- package/dist/features/memory/index.js +1 -0
- package/dist/features/memory/react/MemoryPorts.d.ts +17 -0
- package/dist/features/memory/react/MemoryPorts.js +15 -0
- package/dist/features/memory/react/hooks/useMemoryConfig.hooks.d.ts +2 -2
- package/dist/features/memory/react/hooks/useMemoryConfig.hooks.js +7 -7
- package/dist/features/memory/react/hooks/useMemoryConnectors.hooks.d.ts +2 -3
- package/dist/features/memory/react/hooks/useMemoryConnectors.hooks.js +4 -5
- package/dist/features/memory/react/hooks/useMemoryEntries.hooks.d.ts +2 -3
- package/dist/features/memory/react/hooks/useMemoryEntries.hooks.js +4 -5
- package/dist/features/memory/react/hooks/useMemoryExtractions.hooks.js +2 -2
- package/dist/features/panel-kit/fetch-query/adapter.react.d.ts +2 -2
- package/dist/features/panel-kit/fetch-query/adapter.react.js +5 -3
- package/dist/features/panel-kit/fetch-query/adapter.tanstack.d.ts +11 -0
- package/dist/features/panel-kit/fetch-query/adapter.tanstack.js +212 -0
- package/dist/features/panel-kit/fetch-query/binding.react.d.ts +17 -0
- package/dist/features/panel-kit/fetch-query/binding.react.js +24 -0
- package/dist/features/panel-kit/fetch-query/environment.browser.d.ts +1 -1
- package/dist/features/panel-kit/fetch-query/index.d.ts +6 -3
- package/dist/features/panel-kit/fetch-query/index.js +2 -1
- package/dist/features/panel-kit/fetch-query/tanstack.d.ts +8 -0
- package/dist/features/panel-kit/fetch-query/tanstack.js +4 -0
- package/dist/features/panel-kit/helpers/standing-draft-recovery-message.d.ts +3 -1
- package/dist/features/panel-kit/helpers/standing-draft-recovery-message.js +9 -2
- package/dist/features/settings/dialog/styles/settings-dialog.css +169 -21
- package/dist/features/tabbed-dialog/styles/tabbed-dialog.css +91 -0
- package/dist/react/components/RemixIcon.d.ts +1 -41
- package/dist/react/components/RemixIcon.js +3 -176
- package/dist/react/components/RemixIcon.stylesheet.d.ts +84 -0
- package/dist/react/components/RemixIcon.stylesheet.js +208 -0
- package/dist/react/components/remixicon-font-recovery.d.ts +55 -0
- package/dist/react/components/remixicon-font-recovery.js +110 -0
- package/dist/styles/admin.css +8 -2
- package/package.json +25 -8
- package/dist/features/panel-kit/fetch-query/cache.d.ts +0 -123
- package/dist/features/panel-kit/fetch-query/cache.js +0 -231
- package/dist/features/panel-kit/fetch-query/types.d.ts +0 -91
- package/dist/features/panel-kit/fetch-query/types.js +0 -2
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef } from 'react';
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
2
|
import grapesjs, {} from 'grapesjs';
|
|
3
3
|
import { applyCanvasContentWrapper } from '../../canvas-content-wrapper.js';
|
|
4
4
|
import { applyCanvasEmbedPlaceholders } from '../../canvas-embed-placeholders.js';
|
|
5
5
|
import { buildCanvasStyleConfig, CANVAS_STYLES_PENDING_CLASS } from '../../canvas-style.js';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { serializeEditorSource } from '../../editor-serialization.js';
|
|
7
|
+
import { prepareEditorSource } from '../../editor-blocks.js';
|
|
8
|
+
import { assertPreparedSourceModel, assertProtectedSourceMetadata, EditorSourceError, PROTECTED_ELEMENT_TYPE, protectedCanvasDescriber, protectTextAncestors, registerProtectedElementType } from '../../protected-elements.js';
|
|
9
|
+
import { pathKey } from '../../source-splice.js';
|
|
8
10
|
/**
|
|
9
11
|
* @file `InteractiveHtmlEditor`'s GrapesJS lifecycle: construct the editor once per mount against
|
|
10
12
|
* the container ref, wire content-change notifications out, and tear the editor down on unmount.
|
|
@@ -22,87 +24,52 @@ import { indexElementSourceRanges, pathKey, spliceElementInner } from '../../sou
|
|
|
22
24
|
* fresh document only mounts this component while it's active and remounts with fresh `html` — see
|
|
23
25
|
* `@jini-ai/admin/react`'s `InteractiveHtmlEditor` adapter and its caller in the host product's `PageEditor.tsx`.
|
|
24
26
|
*
|
|
25
|
-
* **This module has zero knowledge of
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* embed-placeholder convention) belongs to the caller, not this primitive.
|
|
27
|
+
* **This module has zero knowledge of host embed conventions.** The shared source detector owns
|
|
28
|
+
* native/runtime blocks; the caller owns its embed predicate and descriptions. Both go through
|
|
29
|
+
* `../../protected-elements.ts`'s single raw-source type and the existing canvas card renderer.
|
|
29
30
|
*
|
|
30
31
|
* **`canvasStyling.contentWrapper` is applied on `load`, against the live canvas — never folded into
|
|
31
32
|
* `components` above.** See `../../canvas-content-wrapper.ts`'s own file header for why: GrapesJS's
|
|
32
33
|
* exported HTML/CSS are built from its component model, which `components` seeds and this wrapper
|
|
33
34
|
* never touches, so it cannot leak into a save no matter how the canvas is edited afterward.
|
|
34
35
|
*/
|
|
35
|
-
const PROTECTED_ELEMENT_TYPE = 'protected-element';
|
|
36
36
|
/** Bold/italic/link only. GrapesJS's other built-in RTE actions (underline, strikethrough, wrap) are
|
|
37
37
|
* omitted, not merely unconfigured, by naming exactly these three rather than leaving the default
|
|
38
38
|
* action list in place. */
|
|
39
39
|
const RTE_ACTIONS = ['bold', 'italic', 'link'];
|
|
40
|
-
|
|
41
|
-
* Registers the `protected-element` component type so any parsed element `isProtectedElement`
|
|
42
|
-
* recognizes becomes one atomic block: its content can never be edited (excluded from RTE
|
|
43
|
-
* content-editing, including as a nested island inside a `text` component), dropped into, or styled,
|
|
44
|
-
* but the element itself is selectable and can be deliberately moved, copied or deleted like any other
|
|
45
|
-
* block. Selectable matters for safety: a non-selectable element hands a click to its nearest
|
|
46
|
-
* selectable ancestor, so clicking an embed selected the block around it and Delete then removed that
|
|
47
|
-
* block together with the embed. Pair it with `applyCanvasEmbedPlaceholders`, which makes the marker
|
|
48
|
-
* the clickable surface of its card. Passed as a `plugins` entry so it registers before
|
|
49
|
-
* `editor.setComponents` parses the initial `html` (see `initInteractiveHtmlEditor` below).
|
|
50
|
-
*
|
|
51
|
-
* @complexity O(1) — one type registration; the per-element check GrapesJS then runs during parsing
|
|
52
|
-
* is O(1) per element (whatever `isProtectedElement` costs), not owned by this function.
|
|
53
|
-
* @overallScore 100
|
|
54
|
-
*/
|
|
55
|
-
function registerProtectedElementType(editor, isProtectedElement) {
|
|
56
|
-
editor.Components.addType(PROTECTED_ELEMENT_TYPE, {
|
|
57
|
-
isComponent: (el) => (isProtectedElement(el) ? { type: PROTECTED_ELEMENT_TYPE } : undefined),
|
|
58
|
-
model: {
|
|
59
|
-
defaults: {
|
|
60
|
-
editable: false,
|
|
61
|
-
droppable: false,
|
|
62
|
-
stylable: false,
|
|
63
|
-
layerable: false,
|
|
64
|
-
badgable: false,
|
|
65
|
-
selectable: true,
|
|
66
|
-
hoverable: true,
|
|
67
|
-
highlightable: true,
|
|
68
|
-
removable: true,
|
|
69
|
-
draggable: true,
|
|
70
|
-
copyable: true,
|
|
71
|
-
},
|
|
72
|
-
},
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
function initInteractiveHtmlEditor(container, html, isProtectedElement, canvasStyling) {
|
|
40
|
+
function initInteractiveHtmlEditor(container, sourcePlan, canvasStyling, options) {
|
|
76
41
|
return grapesjs.init({
|
|
77
42
|
container,
|
|
78
43
|
height: '100%',
|
|
79
44
|
fromElement: false,
|
|
80
|
-
components: html,
|
|
45
|
+
components: sourcePlan.html,
|
|
81
46
|
storageManager: { type: 'none' },
|
|
82
47
|
panels: { defaults: [] },
|
|
83
48
|
blockManager: { blocks: [] },
|
|
84
49
|
richTextEditor: { actions: RTE_ACTIONS },
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
// the other half (the splice path does not depend on this at all, since it never re-serializes
|
|
90
|
-
// untouched elements from the component model in the first place).
|
|
50
|
+
// Both default to `true`: avoidInlineStyle moves styles into ID rules; forceClass converts
|
|
51
|
+
// them to .cNNNN rules even with avoidInlineStyle disabled, including on untouched elements.
|
|
52
|
+
// Keep authored styles inline for structural saves (Bug C in this repo's `pages-redo` plan).
|
|
53
|
+
// serializeCleaned's JSON CSS export is the other half; the splice path already keeps bytes.
|
|
91
54
|
avoidInlineStyle: false,
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
55
|
+
forceClass: false,
|
|
56
|
+
canvas: buildCanvasStyleConfig({ ...canvasStyling, css: [canvasStyling.css, sourcePlan.canvasCss].filter(Boolean).join('\n') }),
|
|
57
|
+
// Structural export can only preserve whitespace gaps the parser retained in the model.
|
|
58
|
+
parser: { optionsHtml: { allowScripts: false, keepEmptyTextNodes: true } },
|
|
59
|
+
plugins: [(editor) => registerProtectedElementType({ editor, sourcePlan }, {
|
|
60
|
+
onRequestEditHtml: options.onRequestEditHtml, editHtmlLabel: options.translate?.('Edit HTML'),
|
|
61
|
+
beforeRequestEditHtml: () => disableActiveTextEditing(editor), onSerializationError: options.onSerializationError,
|
|
62
|
+
})],
|
|
96
63
|
});
|
|
97
64
|
}
|
|
98
65
|
/** Mirrors `source-splice.ts`'s own `UNINDEXED_TAGS` filter, but for GrapesJS's live component tree
|
|
99
66
|
* rather than a parsed HTML string: `textnode` and `comment` components never consume a path slot,
|
|
100
|
-
* matching how `indexElementSourceRanges` numbers the original string.
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
* them without this hook's help. */
|
|
67
|
+
* matching how `indexElementSourceRanges` numbers the original string. Raw style/script atoms
|
|
68
|
+
* now stay in the tree, but their original source tag still excludes them from path slots; before
|
|
69
|
+
* source preservation GrapesJS moved styles into CssComposer and dropped scripts outright. */
|
|
104
70
|
function isPathableComponent(component) {
|
|
105
|
-
return !component.is('textnode') && !component.is('comment')
|
|
71
|
+
return !component.is('textnode') && !component.is('comment')
|
|
72
|
+
&& !['style', 'script'].includes(component.get?.('sourceTagName'));
|
|
106
73
|
}
|
|
107
74
|
/**
|
|
108
75
|
* `component`'s element-only child path from the editor's wrapper root, in the exact numbering
|
|
@@ -164,94 +131,6 @@ function isInsideTextComponent(component) {
|
|
|
164
131
|
}
|
|
165
132
|
return false;
|
|
166
133
|
}
|
|
167
|
-
/** Dev-only, so a fallback is never silent — see `serializeWithSplice` for each trigger this names. */
|
|
168
|
-
function warnFallback(reason) {
|
|
169
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
170
|
-
console.warn(`[useInteractiveHtmlEditor] falling back to full re-serialization: ${reason}`);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Fallback whole-document serialization for when the lossless splice (see `serializeWithSplice`)
|
|
175
|
-
* cannot be trusted. This is the "cleaned" formula from this repo's `pages-redo` plan ("Bug C"), and
|
|
176
|
-
* differs from the whole-document serialization this hook used before the splice existed:
|
|
177
|
-
* `avoidProtected: true` on `getCss()` drops GrapesJS's own `* { box-sizing: border-box; } body
|
|
178
|
-
* {margin: 0;}` block, which GrapesJS otherwise prepends even to a page with no CSS of its own; and
|
|
179
|
-
* `getWrapper().getInnerHTML()` — the wrapper's INNER content — omits the `<body>` tag `editor.
|
|
180
|
-
* getHtml()` serializes the wrapper component itself as. Combined with `avoidInlineStyle: false` on
|
|
181
|
-
* the editor's own config (see `initInteractiveHtmlEditor`), inline `style="…"` attributes also
|
|
182
|
-
* survive here instead of being rewritten into generated `#iXXXX` CSS rules. Still not a byte-
|
|
183
|
-
* identical round trip — this is a full re-serialization from GrapesJS's component model, which
|
|
184
|
-
* cannot recover the original string's entities or authored formatting — just the least-lossy shape
|
|
185
|
-
* available when a true splice is not possible.
|
|
186
|
-
*
|
|
187
|
-
* `keepUnusedStyles: true` carries over unchanged from the serialization this replaces: real
|
|
188
|
-
* documents commonly carry `@media` blocks and `::before`/`::after` rules that don't correspond to
|
|
189
|
-
* any single "matched" component in the tree, which GrapesJS's default CSS export can otherwise drop.
|
|
190
|
-
*/
|
|
191
|
-
function serializeCleaned(editor) {
|
|
192
|
-
const css = editor.getCss({ avoidProtected: true, keepUnusedStyles: true });
|
|
193
|
-
const wrapper = editor.getWrapper();
|
|
194
|
-
// `wrapper` is undefined only before/without a successful init, which cannot be true once this
|
|
195
|
-
// hook's `editor` has fired `update` — defensive only, mirroring `serializeCleaned`'s own doc.
|
|
196
|
-
const html = wrapper ? wrapper.getInnerHTML() : editor.getHtml();
|
|
197
|
-
return css ? `<style>${prettifyCss(css)}</style>${html}` : html;
|
|
198
|
-
}
|
|
199
|
-
/**
|
|
200
|
-
* Builds this edit cycle's exported HTML. The default path splices every accumulated dirty text edit
|
|
201
|
-
* (see `recordDirtyTextEdit`) back into the ORIGINAL source string via `spliceElementInner` (see
|
|
202
|
-
* `source-splice.ts`), so every other byte — entities, attribute quoting, an untouched embed marker,
|
|
203
|
-
* unrelated formatting — passes through unchanged instead of being rebuilt from GrapesJS's component
|
|
204
|
-
* model. Falls back to `serializeCleaned` (and warns via `warnFallback`, so it is never silent)
|
|
205
|
-
* whenever that guarantee cannot be trusted:
|
|
206
|
-
* - `structuralChange` — a block was added or removed since mount (a block move, clone, or delete;
|
|
207
|
-
* a move fires a remove then an add). Its value names the first such event, for the warning. The
|
|
208
|
-
* child churn inside a text component that every RTE close causes does not count (see
|
|
209
|
-
* `isInsideTextComponent`); that is a text edit, which the splice covers. Every dirty path's offset was computed against the
|
|
210
|
-
* pristine `original` string; once the LIVE tree's shape has diverged from it, those offsets (and
|
|
211
|
-
* even the paths themselves) can silently address the wrong element instead of failing loudly, so
|
|
212
|
-
* this is checked first and unconditionally, regardless of what is or isn't in `dirty`. There is no
|
|
213
|
-
* path back to `false` once this flips — a later purely-textual edit still cannot trust offsets
|
|
214
|
-
* computed before the structural change, so the whole rest of the mount stays on the cleaned path.
|
|
215
|
-
* - `dirty` is empty — `update` fired (a real content/style/attribute mutation happened) but no
|
|
216
|
-
* text-component edit was ever recorded for it. Nothing addressable means nothing spliceable.
|
|
217
|
-
* - any individual `spliceElementInner` call returns `null` — that path's element is no longer where
|
|
218
|
-
* it was, or its tag no longer matches what the live component believes.
|
|
219
|
-
*
|
|
220
|
-
* Dirty edits are applied in descending original-offset order (highest first): a splice at a later
|
|
221
|
-
* offset never shifts the byte positions of anything before it, so replaying strictly back-to-front
|
|
222
|
-
* lets every subsequent `spliceElementInner` call re-locate its own (still-untouched-until-its-turn)
|
|
223
|
-
* range correctly without this function tracking running offset deltas itself.
|
|
224
|
-
*
|
|
225
|
-
* @complexity O(n·k) where n = size of `original` (each of up to k dirty splices re-indexes the
|
|
226
|
-
* current string internally) and k = number of dirty edits accumulated this mount — small in
|
|
227
|
-
* practice (an operator edits a handful of elements per session), never a hot loop here.
|
|
228
|
-
*/
|
|
229
|
-
function serializeWithSplice(editor, original, dirty, structuralChange) {
|
|
230
|
-
if (structuralChange) {
|
|
231
|
-
warnFallback(`a block was added, moved, copied or deleted since mount (first: ${structuralChange})`);
|
|
232
|
-
return serializeCleaned(editor);
|
|
233
|
-
}
|
|
234
|
-
if (dirty.size === 0) {
|
|
235
|
-
warnFallback('no text edit was recorded for this change');
|
|
236
|
-
return serializeCleaned(editor);
|
|
237
|
-
}
|
|
238
|
-
const originalIndex = indexElementSourceRanges(original);
|
|
239
|
-
const ordered = [...dirty.values()].sort((a, b) => {
|
|
240
|
-
const aStart = originalIndex.get(pathKey(a.path))?.start ?? -1;
|
|
241
|
-
const bStart = originalIndex.get(pathKey(b.path))?.start ?? -1;
|
|
242
|
-
return bStart - aStart;
|
|
243
|
-
});
|
|
244
|
-
let result = original;
|
|
245
|
-
for (const edit of ordered) {
|
|
246
|
-
const spliced = spliceElementInner(result, edit.path, edit.tagName, edit.component.getInnerHTML());
|
|
247
|
-
if (spliced === null) {
|
|
248
|
-
warnFallback(`element at path "${pathKey(edit.path)}" could not be spliced (removed, or its tag drifted)`);
|
|
249
|
-
return serializeCleaned(editor);
|
|
250
|
-
}
|
|
251
|
-
result = spliced;
|
|
252
|
-
}
|
|
253
|
-
return result;
|
|
254
|
-
}
|
|
255
134
|
/**
|
|
256
135
|
* Ends whatever RTE session is currently open, so its pending edit syncs into the component model
|
|
257
136
|
* before `flush` reads it — GrapesJS only writes an RTE session's edited text into the model when
|
|
@@ -297,7 +176,7 @@ const STYLESHEET_SETTLE_TIMEOUT_MS = 3000;
|
|
|
297
176
|
* @complexity O(n) in the number of `<link rel="stylesheet">` elements found — one listener pair per
|
|
298
177
|
* link, no polling.
|
|
299
178
|
*/
|
|
300
|
-
function revealCanvasWhenStylesheetsSettle(editor, body) {
|
|
179
|
+
function revealCanvasWhenStylesheetsSettle(editor, body, onSettled) {
|
|
301
180
|
body.classList.add(CANVAS_STYLES_PENDING_CLASS);
|
|
302
181
|
const canvasDocument = editor.Canvas.getDocument();
|
|
303
182
|
// `getDocument()` returning null is not reachable once `load` has fired on a real editor — defensive
|
|
@@ -317,6 +196,8 @@ function revealCanvasWhenStylesheetsSettle(editor, body) {
|
|
|
317
196
|
link.removeEventListener('error', handleSettle);
|
|
318
197
|
}
|
|
319
198
|
pending.clear();
|
|
199
|
+
if (links.length > 0)
|
|
200
|
+
onSettled();
|
|
320
201
|
body.classList.remove(CANVAS_STYLES_PENDING_CLASS);
|
|
321
202
|
};
|
|
322
203
|
function handleSettle(event) {
|
|
@@ -335,44 +216,9 @@ function revealCanvasWhenStylesheetsSettle(editor, body) {
|
|
|
335
216
|
timeoutId = setTimeout(reveal, STYLESHEET_SETTLE_TIMEOUT_MS);
|
|
336
217
|
return reveal;
|
|
337
218
|
}
|
|
338
|
-
|
|
339
|
-
* Owns one GrapesJS `Editor` instance for the lifetime of the calling component's mount. `onChange`
|
|
340
|
-
* fires with the editor's current serialized content (see `serializeWithSplice`) on every content
|
|
341
|
-
* mutation (GrapesJS's `update` event — components, styles, or attributes changing); it is not
|
|
342
|
-
* debounced, so a caller replacing an existing uncontrolled `<textarea>` (which already pushes a full
|
|
343
|
-
* value on every keystroke) sees matching behavior.
|
|
344
|
-
*
|
|
345
|
-
* **Mounts into a dedicated child node, not the wrapper div `containerRef` itself, and removes that
|
|
346
|
-
* child node (not just its content) on cleanup.** Defensive, for React StrictMode's dev-time
|
|
347
|
-
* double-invoke (mount -> cleanup -> mount again, synchronously): if a destroyed instance's canvas
|
|
348
|
-
* were still mid-render when cleanup ran, clearing the container's *current* content at that moment
|
|
349
|
-
* cannot remove DOM that does not exist yet, and a later async write from the discarded instance
|
|
350
|
-
* would land after the fact. Detaching the whole per-instance node sidesteps that class of ordering
|
|
351
|
-
* problem entirely — whatever a destroyed instance's async work later does, it mutates a node no
|
|
352
|
-
* longer connected to `document`, so it can never become visible or queryable in the live page no
|
|
353
|
-
* matter when it runs. (Investigated live via Playwright while chasing an unrelated symptom — two
|
|
354
|
-
* canvas iframes coexisting after one mount — which turned out to be a separate, harmless GrapesJS
|
|
355
|
-
* quirk unrelated to StrictMode: `Canvas`'s `FramesView` always constructs one `FrameView` that never
|
|
356
|
-
* proceeds past construction to `render()`, alongside the real one; it keeps Backbone's default
|
|
357
|
-
* `class="frame"` forever, inert and unrendered. `.gjs-frame` — the class `FrameView.render()`
|
|
358
|
-
* assigns — is what callers should select on, never a bare `iframe` under the wrapper.)
|
|
359
|
-
*
|
|
360
|
-
* **`flush()` exists because GrapesJS's RTE only syncs on `disableEditing`, and `update` is
|
|
361
|
-
* double-debounced and never fires after `off()`.** A host that calls `onChange` on every keystroke
|
|
362
|
-
* (like an uncontrolled `<textarea>`) still misses the operator's last edit if they click Save or
|
|
363
|
-
* navigate away while an RTE session is still open: GrapesJS writes that session's text into the
|
|
364
|
-
* component model only when it closes, and by the time a host's save handler runs, this hook has
|
|
365
|
-
* already stopped listening (or the debounce hasn't fired yet). `flush` closes any open RTE session,
|
|
366
|
-
* computes and emits the current content the same way `handleUpdate` does, and hands the result back
|
|
367
|
-
* directly — see its own doc below for exactly what it returns and when.
|
|
368
|
-
*
|
|
369
|
-
* @complexity O(1) setup/teardown. Steady-state cost is GrapesJS's own serialization per edit,
|
|
370
|
-
* proportional to document size — not something this hook controls or should hide a note about
|
|
371
|
-
* beyond that.
|
|
372
|
-
* @overallScore 100
|
|
373
|
-
*/
|
|
374
|
-
export function useInteractiveHtmlEditor(html, onChange, isProtectedElement, canvasStyling = {}, describeEmbedPlaceholder) {
|
|
219
|
+
export function useInteractiveHtmlEditor(html, onChange, isProtectedElement, canvasStyling = {}, describeEmbedPlaceholder, options = {}) {
|
|
375
220
|
const wrapperRef = useRef(null);
|
|
221
|
+
const [sourceError, setSourceError] = useState();
|
|
376
222
|
const onChangeRef = useRef(onChange);
|
|
377
223
|
onChangeRef.current = onChange;
|
|
378
224
|
// Delegated to by the stable `flush` callback returned below. Reset to a no-op on cleanup, before
|
|
@@ -382,10 +228,48 @@ export function useInteractiveHtmlEditor(html, onChange, isProtectedElement, can
|
|
|
382
228
|
const wrapper = wrapperRef.current;
|
|
383
229
|
if (!wrapper)
|
|
384
230
|
return;
|
|
231
|
+
const sourcePlan = prepareEditorSource({ html }, { isProtectedElement, describeHostElement: describeEmbedPlaceholder, translate: options.translate });
|
|
232
|
+
let integrityFailure;
|
|
233
|
+
const reportSourceError = (error) => {
|
|
234
|
+
integrityFailure = error;
|
|
235
|
+
setSourceError(options.translate?.(error.message) ?? error.message);
|
|
236
|
+
options.onSerializationError?.(error);
|
|
237
|
+
};
|
|
238
|
+
if (!sourcePlan.ok) {
|
|
239
|
+
reportSourceError(new EditorSourceError({ message: sourcePlan.problem }, {}));
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
385
242
|
const mountEl = document.createElement('div');
|
|
386
243
|
mountEl.style.height = '100%';
|
|
387
244
|
wrapper.appendChild(mountEl);
|
|
388
|
-
|
|
245
|
+
let editor;
|
|
246
|
+
try {
|
|
247
|
+
editor = initInteractiveHtmlEditor(mountEl, sourcePlan, canvasStyling, { ...options, onSerializationError: reportSourceError });
|
|
248
|
+
}
|
|
249
|
+
catch {
|
|
250
|
+
reportSourceError(new EditorSourceError());
|
|
251
|
+
mountEl.remove();
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
const decorate = () => {
|
|
255
|
+
protectTextAncestors({ editor }, {});
|
|
256
|
+
const describe = sourcePlan.instances.length ? protectedCanvasDescriber({ editor }, {}) : describeEmbedPlaceholder;
|
|
257
|
+
if (describe)
|
|
258
|
+
applyCanvasEmbedPlaceholders(editor.Canvas.getBody(), describe);
|
|
259
|
+
};
|
|
260
|
+
const serialize = () => {
|
|
261
|
+
try {
|
|
262
|
+
const next = serializeEditorSource({ editor, html, edits: dirty, sourcePlan }, { structuralChange });
|
|
263
|
+
if (next.includes(sourcePlan.markerAttribute))
|
|
264
|
+
throw new EditorSourceError();
|
|
265
|
+
return next;
|
|
266
|
+
}
|
|
267
|
+
catch (error) {
|
|
268
|
+
const failure = error instanceof EditorSourceError ? error : new EditorSourceError();
|
|
269
|
+
reportSourceError(failure);
|
|
270
|
+
throw failure;
|
|
271
|
+
}
|
|
272
|
+
};
|
|
389
273
|
// Splice bookkeeping — see `serializeWithSplice`'s own doc for how these two are used. Plain
|
|
390
274
|
// closure state scoped to this one mount-only effect run (like `editor`/`mountEl` above), not
|
|
391
275
|
// `useRef`: nothing outside this effect ever reads them.
|
|
@@ -398,22 +282,26 @@ export function useInteractiveHtmlEditor(html, onChange, isProtectedElement, can
|
|
|
398
282
|
// Set inside `handleLoad`, once the canvas body exists — see `revealCanvasWhenStylesheetsSettle`.
|
|
399
283
|
// `undefined` until then (or if `load` never fires before unmount), so cleanup below guards it.
|
|
400
284
|
let cancelStylesheetWait;
|
|
285
|
+
let canvasWindow;
|
|
401
286
|
// Embed placeholder cards live only in the canvas DOM, so a structural edit (delete, move, clone,
|
|
402
287
|
// undo) can leave one stale. Repaired on the tick after the edit, once GrapesJS has rendered it;
|
|
403
288
|
// see `applyCanvasEmbedPlaceholders`. Only after `load`: before it, adds are the initial parse.
|
|
404
289
|
let loaded = false;
|
|
405
290
|
let cardRepair;
|
|
406
291
|
const scheduleCardRepair = () => {
|
|
407
|
-
if (!loaded ||
|
|
292
|
+
if (!loaded || cardRepair !== undefined)
|
|
408
293
|
return;
|
|
409
294
|
cardRepair = setTimeout(() => {
|
|
410
295
|
cardRepair = undefined;
|
|
411
|
-
|
|
296
|
+
decorate();
|
|
412
297
|
}, 0);
|
|
413
298
|
};
|
|
414
299
|
const handleUpdate = () => {
|
|
415
300
|
changed = true;
|
|
416
|
-
|
|
301
|
+
try {
|
|
302
|
+
onChangeRef.current(serialize());
|
|
303
|
+
}
|
|
304
|
+
catch { /* Source error is already visible; retain the last intact working copy. */ }
|
|
417
305
|
};
|
|
418
306
|
// `component:update:content` fires with the changed component itself as its argument (GrapesJS's
|
|
419
307
|
// general `component:update:{propertyName}` pattern); `rte:disable` fires with `(view, rte)` —
|
|
@@ -429,7 +317,7 @@ export function useInteractiveHtmlEditor(html, onChange, isProtectedElement, can
|
|
|
429
317
|
// for the rest of this mount; see its own doc for why this never resets.
|
|
430
318
|
const handleStructuralChange = (event) => (component) => {
|
|
431
319
|
scheduleCardRepair();
|
|
432
|
-
if (structuralChange || (component && isInsideTextComponent(component)))
|
|
320
|
+
if (structuralChange || (component && !component.is(PROTECTED_ELEMENT_TYPE) && isInsideTextComponent(component)))
|
|
433
321
|
return;
|
|
434
322
|
structuralChange = `${event} <${component?.tagName?.toLowerCase() || 'unknown'}>`;
|
|
435
323
|
};
|
|
@@ -447,9 +335,19 @@ export function useInteractiveHtmlEditor(html, onChange, isProtectedElement, can
|
|
|
447
335
|
// structural change happened either.
|
|
448
336
|
flushRef.current = async () => {
|
|
449
337
|
await disableActiveTextEditing(editor);
|
|
338
|
+
if (integrityFailure)
|
|
339
|
+
throw integrityFailure;
|
|
340
|
+
try {
|
|
341
|
+
assertProtectedSourceMetadata({ editor }, {});
|
|
342
|
+
}
|
|
343
|
+
catch {
|
|
344
|
+
const failure = new EditorSourceError();
|
|
345
|
+
reportSourceError(failure);
|
|
346
|
+
throw failure;
|
|
347
|
+
}
|
|
450
348
|
if (!changed && dirty.size === 0 && !structuralChange)
|
|
451
349
|
return undefined;
|
|
452
|
-
const next =
|
|
350
|
+
const next = serialize();
|
|
453
351
|
onChangeRef.current(next);
|
|
454
352
|
return next;
|
|
455
353
|
};
|
|
@@ -461,14 +359,26 @@ export function useInteractiveHtmlEditor(html, onChange, isProtectedElement, can
|
|
|
461
359
|
// Applied AFTER the content wrapper so a placeholder can sit correctly however deep the wrapper
|
|
462
360
|
// chain nests its marker, though nothing about either function actually depends on this order.
|
|
463
361
|
const handleLoad = () => {
|
|
362
|
+
if (sourcePlan.instances.length) {
|
|
363
|
+
try {
|
|
364
|
+
assertPreparedSourceModel({ editor, sourcePlan }, {});
|
|
365
|
+
}
|
|
366
|
+
catch {
|
|
367
|
+
reportSourceError(new EditorSourceError());
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
464
371
|
const body = editor.Canvas.getBody();
|
|
465
372
|
applyCanvasContentWrapper(body, canvasStyling.contentWrapper);
|
|
466
|
-
|
|
467
|
-
applyCanvasEmbedPlaceholders(body, describeEmbedPlaceholder);
|
|
373
|
+
decorate();
|
|
468
374
|
// Runs LAST, after both decorations above — see `revealCanvasWhenStylesheetsSettle`'s own doc
|
|
469
375
|
// for why that order matters even in the zero-stylesheet case.
|
|
470
|
-
cancelStylesheetWait = revealCanvasWhenStylesheetsSettle(editor, body);
|
|
376
|
+
cancelStylesheetWait = revealCanvasWhenStylesheetsSettle(editor, body, decorate);
|
|
471
377
|
loaded = true;
|
|
378
|
+
// An iframe window's native resize event already covers canvas-width changes. Reuse it
|
|
379
|
+
// instead of maintaining a second ResizeObserver lifecycle for the same view geometry.
|
|
380
|
+
canvasWindow = editor.Canvas.getDocument()?.defaultView;
|
|
381
|
+
canvasWindow?.addEventListener('resize', scheduleCardRepair);
|
|
472
382
|
};
|
|
473
383
|
editor.on('load', handleLoad);
|
|
474
384
|
return () => {
|
|
@@ -482,6 +392,7 @@ export function useInteractiveHtmlEditor(html, onChange, isProtectedElement, can
|
|
|
482
392
|
editor.off('component:remove', handleRemove);
|
|
483
393
|
editor.off('load', handleLoad);
|
|
484
394
|
cancelStylesheetWait?.();
|
|
395
|
+
canvasWindow?.removeEventListener('resize', scheduleCardRepair);
|
|
485
396
|
clearTimeout(cardRepair);
|
|
486
397
|
editor.destroy();
|
|
487
398
|
mountEl.remove();
|
|
@@ -491,6 +402,6 @@ export function useInteractiveHtmlEditor(html, onChange, isProtectedElement, can
|
|
|
491
402
|
// Stable across renders (the effect above mutates `flushRef.current`, not this callback's identity)
|
|
492
403
|
// so a caller can safely pass `flush` into a `useCallback`/`useImperativeHandle` dependency array.
|
|
493
404
|
const flush = useCallback(() => flushRef.current(), []);
|
|
494
|
-
return { containerRef: wrapperRef, flush };
|
|
405
|
+
return { containerRef: wrapperRef, flush, sourceError };
|
|
495
406
|
}
|
|
496
407
|
//# sourceMappingURL=useInteractiveHtmlEditor.js.map
|
|
@@ -53,4 +53,27 @@ export declare function indexElementSourceRanges(html: string): Map<string, Elem
|
|
|
53
53
|
* @overallScore 100
|
|
54
54
|
*/
|
|
55
55
|
export declare function spliceElementInner(html: string, path: ElementPath, expectedTagName: string, newInner: string): string | null;
|
|
56
|
+
/** Verified offsets are JS string indexes (UTF-16), suitable for slice and textarea selection. */
|
|
57
|
+
export interface ElementOuterRange {
|
|
58
|
+
readonly outerStart: number;
|
|
59
|
+
readonly outerEnd: number;
|
|
60
|
+
readonly openingEnd: number;
|
|
61
|
+
}
|
|
62
|
+
/** Source tree shared by block detection and preparation; raw atoms never consume a splice slot. */
|
|
63
|
+
export interface SourceElement {
|
|
64
|
+
readonly tagName: string;
|
|
65
|
+
readonly attributes: Readonly<Record<string, string>>;
|
|
66
|
+
readonly path: ElementPath | null;
|
|
67
|
+
readonly outerRange: ElementOuterRange | null;
|
|
68
|
+
readonly sourceSupported: boolean;
|
|
69
|
+
readonly children: readonly SourceElement[];
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Extends the inner-splice index without changing its published range shape. Missing end tags,
|
|
73
|
+
* implicit nodes and reordered/foster-parented children cannot establish an opaque source owner.
|
|
74
|
+
* Validation is linear in source nodes; ranges are checked against their immediate source parent.
|
|
75
|
+
*/
|
|
76
|
+
export declare function indexSourceElements({ html }: {
|
|
77
|
+
html: string;
|
|
78
|
+
}, _options?: object): SourceElement[];
|
|
56
79
|
//# sourceMappingURL=source-splice.d.ts.map
|
|
@@ -86,4 +86,49 @@ export function spliceElementInner(html, path, expectedTagName, newInner) {
|
|
|
86
86
|
return null;
|
|
87
87
|
return html.slice(0, range.start) + newInner + html.slice(range.end);
|
|
88
88
|
}
|
|
89
|
+
const VOID_TAGS = new Set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr']);
|
|
90
|
+
/**
|
|
91
|
+
* Extends the inner-splice index without changing its published range shape. Missing end tags,
|
|
92
|
+
* implicit nodes and reordered/foster-parented children cannot establish an opaque source owner.
|
|
93
|
+
* Validation is linear in source nodes; ranges are checked against their immediate source parent.
|
|
94
|
+
*/
|
|
95
|
+
export function indexSourceElements({ html }, _options = {}) {
|
|
96
|
+
const fragment = parseFragment(html, { sourceCodeLocationInfo: true });
|
|
97
|
+
const walk = (nodes, parentPath, parentStart, parentEnd) => {
|
|
98
|
+
let slot = 0;
|
|
99
|
+
const elements = nodes.filter(isElement).map(node => {
|
|
100
|
+
const atom = UNINDEXED_TAGS.has(node.tagName);
|
|
101
|
+
const path = atom ? null : [...parentPath, slot++];
|
|
102
|
+
const loc = node.sourceCodeLocation;
|
|
103
|
+
const start = loc?.startTag;
|
|
104
|
+
const end = loc?.endTag;
|
|
105
|
+
const voidTag = VOID_TAGS.has(node.tagName) || (node.namespaceURI !== 'http://www.w3.org/1999/xhtml'
|
|
106
|
+
&& !!start && html.slice(start.startOffset, start.endOffset).endsWith('/>'));
|
|
107
|
+
const outerEnd = voidTag ? start?.endOffset : end?.endOffset;
|
|
108
|
+
const boundsValid = !!start && outerEnd !== undefined && start.startOffset >= parentStart && outerEnd <= parentEnd;
|
|
109
|
+
const children = walk(node.childNodes, path ?? parentPath, start?.endOffset ?? parentStart, end?.startOffset ?? parentEnd);
|
|
110
|
+
// Template content is a separate fragment; it has no ordinary component path/shell here.
|
|
111
|
+
const valid = boundsValid && node.tagName !== 'template';
|
|
112
|
+
return {
|
|
113
|
+
tagName: node.tagName,
|
|
114
|
+
attributes: Object.fromEntries(node.attrs.map(attr => [attr.name, attr.value])),
|
|
115
|
+
path,
|
|
116
|
+
outerRange: valid ? { outerStart: start.startOffset, outerEnd: outerEnd, openingEnd: start.endOffset } : null,
|
|
117
|
+
sourceSupported: valid && children.every(child => child.sourceSupported),
|
|
118
|
+
children,
|
|
119
|
+
};
|
|
120
|
+
});
|
|
121
|
+
// Foster parenting can move a valid-looking node ahead of a source-earlier table. Neither
|
|
122
|
+
// sibling can be trusted as a model-addressable source owner in that recovered ordering.
|
|
123
|
+
for (let i = 1; i < elements.length; i++) {
|
|
124
|
+
const previous = elements[i - 1];
|
|
125
|
+
const current = elements[i];
|
|
126
|
+
if (previous.outerRange && current.outerRange && previous.outerRange.outerEnd > current.outerRange.outerStart) {
|
|
127
|
+
previous.sourceSupported = current.sourceSupported = false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return elements;
|
|
131
|
+
};
|
|
132
|
+
return walk(fragment.childNodes, [], 0, html.length);
|
|
133
|
+
}
|
|
89
134
|
//# sourceMappingURL=source-splice.js.map
|
|
@@ -7,6 +7,7 @@ export const SETTINGS_DIALOG_AR = {
|
|
|
7
7
|
'About': 'حول',
|
|
8
8
|
'Accent color': 'لون التمييز',
|
|
9
9
|
'Account': 'الحساب',
|
|
10
|
+
'Advanced': 'متقدّم',
|
|
10
11
|
'Advanced: proxy & custom paths': 'متقدم: الوكيل والمسارات المخصصة',
|
|
11
12
|
'Agent check failed': 'فشل التحقق من الوكيل',
|
|
12
13
|
'Agent is ready.': 'الوكيل جاهز.',
|
|
@@ -131,6 +132,9 @@ export const SETTINGS_DIALOG_AR = {
|
|
|
131
132
|
'Not available for this daemon.': 'غير متاح لهذه الخدمة الخلفية.',
|
|
132
133
|
'Not configured': 'غير مهيّأ',
|
|
133
134
|
'Not installed': 'غير مثبَّت',
|
|
135
|
+
'Show {count} not installed': 'إظهار {count} غير مثبَّتة',
|
|
136
|
+
'Hide not installed': 'إخفاء غير المثبَّتة',
|
|
137
|
+
'Show not-installed CLIs': 'إظهار واجهات CLI غير المثبَّتة',
|
|
134
138
|
'Not sharing': 'لا تتم المشاركة',
|
|
135
139
|
'Nothing is shared unless you opt in.': 'لا تتم مشاركة أي شيء ما لم تفعّل ذلك بنفسك.',
|
|
136
140
|
'Off': 'إيقاف',
|
|
@@ -7,6 +7,7 @@ export const SETTINGS_DIALOG_BN = {
|
|
|
7
7
|
'About': 'সম্পর্কে',
|
|
8
8
|
'Accent color': 'হাইলাইট রং',
|
|
9
9
|
'Account': 'অ্যাকাউন্ট',
|
|
10
|
+
'Advanced': 'উন্নত',
|
|
10
11
|
'Advanced: proxy & custom paths': 'উন্নত: প্রক্সি ও কাস্টম পাথ',
|
|
11
12
|
'Agent check failed': 'এজেন্ট পরীক্ষা ব্যর্থ হয়েছে',
|
|
12
13
|
'Agent is ready.': 'এজেন্ট প্রস্তুত।',
|
|
@@ -131,6 +132,9 @@ export const SETTINGS_DIALOG_BN = {
|
|
|
131
132
|
'Not available for this daemon.': 'এই ডিমনের জন্য উপলব্ধ নয়।',
|
|
132
133
|
'Not configured': 'কনফিগার করা হয়নি',
|
|
133
134
|
'Not installed': 'ইনস্টল করা নেই',
|
|
135
|
+
'Show {count} not installed': '{count}টি ইনস্টল না করা দেখান',
|
|
136
|
+
'Hide not installed': 'ইনস্টল না করা লুকান',
|
|
137
|
+
'Show not-installed CLIs': 'ইনস্টল না করা CLI দেখান',
|
|
134
138
|
'Not sharing': 'শেয়ার করা হচ্ছে না',
|
|
135
139
|
'Nothing is shared unless you opt in.': 'আপনি সম্মতি না দেওয়া পর্যন্ত কিছুই শেয়ার করা হয় না।',
|
|
136
140
|
'Off': 'বন্ধ',
|
|
@@ -7,6 +7,7 @@ export const SETTINGS_DIALOG_DE = {
|
|
|
7
7
|
'About': 'Info',
|
|
8
8
|
'Accent color': 'Akzentfarbe',
|
|
9
9
|
'Account': 'Konto',
|
|
10
|
+
'Advanced': 'Erweitert',
|
|
10
11
|
'Advanced: proxy & custom paths': 'Erweitert: Proxy und benutzerdefinierte Pfade',
|
|
11
12
|
'Agent check failed': 'Agentenprüfung fehlgeschlagen',
|
|
12
13
|
'Agent is ready.': 'Agent ist bereit.',
|
|
@@ -131,6 +132,9 @@ export const SETTINGS_DIALOG_DE = {
|
|
|
131
132
|
'Not available for this daemon.': 'Für diesen Daemon nicht verfügbar.',
|
|
132
133
|
'Not configured': 'Nicht konfiguriert',
|
|
133
134
|
'Not installed': 'Nicht installiert',
|
|
135
|
+
'Show {count} not installed': '{count} nicht installierte anzeigen',
|
|
136
|
+
'Hide not installed': 'Nicht installierte ausblenden',
|
|
137
|
+
'Show not-installed CLIs': 'Nicht installierte CLIs anzeigen',
|
|
134
138
|
'Not sharing': 'Wird nicht geteilt',
|
|
135
139
|
'Nothing is shared unless you opt in.': 'Es wird nichts geteilt, außer du stimmst ausdrücklich zu.',
|
|
136
140
|
'Off': 'Aus',
|
|
@@ -7,6 +7,7 @@ export const SETTINGS_DIALOG_EN = {
|
|
|
7
7
|
'About': 'About',
|
|
8
8
|
'Accent color': 'Accent color',
|
|
9
9
|
'Account': 'Account',
|
|
10
|
+
'Advanced': 'Advanced',
|
|
10
11
|
'Advanced: proxy & custom paths': 'Advanced: proxy & custom paths',
|
|
11
12
|
'Agent check failed': 'Agent check failed',
|
|
12
13
|
'Agent is ready.': 'Agent is ready.',
|
|
@@ -131,6 +132,9 @@ export const SETTINGS_DIALOG_EN = {
|
|
|
131
132
|
'Not available for this daemon.': 'Not available for this daemon.',
|
|
132
133
|
'Not configured': 'Not configured',
|
|
133
134
|
'Not installed': 'Not installed',
|
|
135
|
+
'Show {count} not installed': 'Show {count} not installed',
|
|
136
|
+
'Hide not installed': 'Hide not installed',
|
|
137
|
+
'Show not-installed CLIs': 'Show not-installed CLIs',
|
|
134
138
|
'Not sharing': 'Not sharing',
|
|
135
139
|
'Nothing is shared unless you opt in.': 'Nothing is shared unless you opt in.',
|
|
136
140
|
'Off': 'Off',
|
|
@@ -7,6 +7,7 @@ export const SETTINGS_DIALOG_ES = {
|
|
|
7
7
|
'About': 'Acerca de',
|
|
8
8
|
'Accent color': 'Color de acento',
|
|
9
9
|
'Account': 'Cuenta',
|
|
10
|
+
'Advanced': 'Avanzado',
|
|
10
11
|
'Advanced: proxy & custom paths': 'Avanzado: proxy y rutas personalizadas',
|
|
11
12
|
'Agent check failed': 'Falló la comprobación del agente',
|
|
12
13
|
'Agent is ready.': 'El agente está listo.',
|
|
@@ -131,6 +132,9 @@ export const SETTINGS_DIALOG_ES = {
|
|
|
131
132
|
'Not available for this daemon.': 'No disponible para este daemon.',
|
|
132
133
|
'Not configured': 'No configurado',
|
|
133
134
|
'Not installed': 'No instalado',
|
|
135
|
+
'Show {count} not installed': 'Mostrar {count} no instalados',
|
|
136
|
+
'Hide not installed': 'Ocultar no instalados',
|
|
137
|
+
'Show not-installed CLIs': 'Mostrar CLIs no instalados',
|
|
134
138
|
'Not sharing': 'No se comparte',
|
|
135
139
|
'Nothing is shared unless you opt in.': 'No se comparte nada a menos que lo actives.',
|
|
136
140
|
'Off': 'Desactivado',
|
|
@@ -7,6 +7,7 @@ export const SETTINGS_DIALOG_FA = {
|
|
|
7
7
|
'About': 'درباره',
|
|
8
8
|
'Accent color': 'رنگ تأکیدی',
|
|
9
9
|
'Account': 'حساب کاربری',
|
|
10
|
+
'Advanced': 'پیشرفته',
|
|
10
11
|
'Advanced: proxy & custom paths': 'پیشرفته: پراکسی و مسیرهای سفارشی',
|
|
11
12
|
'Agent check failed': 'بررسی عامل ناموفق بود',
|
|
12
13
|
'Agent is ready.': 'عامل آماده است.',
|
|
@@ -131,6 +132,9 @@ export const SETTINGS_DIALOG_FA = {
|
|
|
131
132
|
'Not available for this daemon.': 'برای این دیمون در دسترس نیست.',
|
|
132
133
|
'Not configured': 'پیکربندینشده',
|
|
133
134
|
'Not installed': 'نصبنشده',
|
|
135
|
+
'Show {count} not installed': 'نمایش {count} مورد نصبنشده',
|
|
136
|
+
'Hide not installed': 'پنهان کردن موارد نصبنشده',
|
|
137
|
+
'Show not-installed CLIs': 'نمایش CLIهای نصبنشده',
|
|
134
138
|
'Not sharing': 'اشتراکگذاری نمیشود',
|
|
135
139
|
'Nothing is shared unless you opt in.': 'تا زمانی که خودتان فعال نکنید، چیزی به اشتراک گذاشته نمیشود.',
|
|
136
140
|
'Off': 'خاموش',
|