@jini-ai/ui 0.4.4 → 0.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.md +20 -0
- package/dist/core/theme/default.d.ts +4 -0
- package/dist/core/theme/default.js +11 -0
- package/dist/core/theme/stylesheet.d.ts +8 -0
- package/dist/core/theme/stylesheet.js +14 -0
- package/dist/core/theme/types.d.ts +39 -0
- package/dist/core/theme/types.js +2 -0
- package/dist/core/theme/validation.d.ts +15 -0
- package/dist/core/theme/validation.js +55 -0
- package/dist/features/admin-widgets/components/Select/Select.d.ts +11 -3
- package/dist/features/admin-widgets/components/Select/Select.hooks.d.ts +8 -13
- package/dist/features/admin-widgets/components/Select/Select.hooks.js +37 -215
- package/dist/features/admin-widgets/components/Select/Select.js +4 -3
- package/dist/features/appearance/constants.js +1 -1
- package/dist/features/connectors/rules.d.ts +1 -6
- package/dist/features/connectors/types.d.ts +1 -6
- package/dist/features/connectors/types.js +1 -6
- package/dist/features/execution/http-adapter.d.ts +111 -0
- package/dist/features/execution/http-adapter.js +160 -0
- package/dist/features/execution/index.d.ts +4 -0
- package/dist/features/execution/index.js +2 -0
- package/dist/features/execution/react/hooks/use-execution-credential.hooks.d.ts +232 -0
- package/dist/features/execution/react/hooks/use-execution-credential.hooks.js +217 -0
- package/dist/features/html-editor/canvas-style.d.ts +1 -1
- package/dist/features/html-editor/canvas-style.js +2 -2
- package/dist/features/i18n/dictionaries/settings-dialog.ar.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.bn.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.de.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.en.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.es.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.fa.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.fr.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.hi.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.hu.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.id.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.it.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.ja.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.ko.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.pl.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.pt-BR.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.ru.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.th.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.tr.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.uk.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.ur.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.zh-CN.js +2 -0
- package/dist/features/i18n/dictionaries/settings-dialog.zh-TW.js +2 -0
- package/dist/features/i18n/dictionary-translator.d.ts +5 -2
- package/dist/features/i18n/dictionary-translator.js +1 -1
- package/dist/features/i18n/index.d.ts +1 -1
- package/dist/features/i18n/index.js +1 -1
- package/dist/features/i18n/template-i18n.d.ts +11 -0
- package/dist/features/i18n/template-i18n.js +12 -0
- package/dist/features/interactive-ui/providers/recharts/chart-tokens.d.ts +1 -1
- package/dist/features/interactive-ui/providers/recharts/chart-tokens.js +1 -1
- package/dist/features/lexical-rich-text-editor/deserialize.d.ts +0 -5
- package/dist/features/lexical-rich-text-editor/deserialize.js +0 -5
- package/dist/features/lexical-rich-text-editor/mention-node.d.ts +5 -17
- package/dist/features/lexical-rich-text-editor/mention-node.js +5 -17
- package/dist/features/lexical-rich-text-editor/mention-parser.d.ts +2 -4
- package/dist/features/lexical-rich-text-editor/serialize.d.ts +0 -4
- package/dist/features/lexical-rich-text-editor/serialize.js +0 -4
- package/dist/features/lexical-rich-text-editor/types.d.ts +3 -13
- package/dist/features/lexical-rich-text-editor/types.js +3 -13
- package/dist/features/mcp-ui/early-message-buffer.d.ts +1 -2
- package/dist/features/mcp-ui/early-message-buffer.js +1 -2
- package/dist/features/mcp-ui/escape.d.ts +4 -0
- package/dist/features/mcp-ui/escape.js +2 -9
- package/dist/features/mcp-ui/index.d.ts +1 -0
- package/dist/features/mcp-ui/index.js +1 -0
- package/dist/features/mcp-ui/protocol.d.ts +2 -5
- package/dist/features/mcp-ui/protocol.js +2 -5
- package/dist/features/mcp-ui/resource.d.ts +47 -7
- package/dist/features/mcp-ui/resource.js +5 -8
- package/dist/features/mcp-ui/secret-card.d.ts +115 -0
- package/dist/features/mcp-ui/secret-card.js +137 -0
- package/dist/features/mcp-ui/surfaces/confirmation.js +5 -0
- package/dist/features/mcp-ui/surfaces/fields.d.ts +5 -2
- package/dist/features/mcp-ui/surfaces/fields.js +3 -1
- package/dist/features/mcp-ui/surfaces/form.d.ts +4 -1
- package/dist/features/mcp-ui/surfaces/form.js +65 -3
- package/dist/features/mcp-ui/surfaces/outcome.d.ts +2 -0
- package/dist/features/mcp-ui/surfaces/text-input.d.ts +14 -6
- package/dist/features/mcp-ui/surfaces/text-input.js +16 -2
- package/dist/features/mcp-ui/surfaces/tokens.js +1 -1
- package/dist/features/memory/react/components/MemoryConnectedPanel.d.ts +1 -1
- package/dist/features/memory/react/components/MemoryConnectedPanel.js +0 -1
- package/dist/features/panel-kit/helpers/format-timestamp.d.ts +5 -3
- package/dist/features/panel-kit/helpers/format-timestamp.js +13 -4
- package/dist/features/panel-kit/helpers/permissions.js +2 -0
- package/dist/features/panel-kit/helpers/resolve-active-tab-id.js +2 -0
- package/dist/features/panel-kit/helpers/standing-draft-local-backup.d.ts +51 -0
- package/dist/features/panel-kit/helpers/standing-draft-local-backup.js +77 -0
- package/dist/features/panel-kit/helpers/standing-draft-recovery-message.d.ts +8 -0
- package/dist/features/panel-kit/helpers/standing-draft-recovery-message.js +10 -0
- package/dist/features/panel-kit/hooks/use-async-action.hooks.d.ts +1 -0
- package/dist/features/panel-kit/hooks/use-dirty-guard.hooks.d.ts +1 -0
- package/dist/features/panel-kit/hooks/use-dirty-guard.hooks.js +1 -0
- package/dist/features/panel-kit/hooks/use-serial-writes.hooks.d.ts +8 -0
- package/dist/features/panel-kit/hooks/use-serial-writes.hooks.js +3 -0
- package/dist/features/panel-kit/hooks/use-settings-slice.hooks.d.ts +211 -0
- package/dist/features/panel-kit/hooks/use-settings-slice.hooks.js +382 -0
- package/dist/features/panel-kit/hooks/use-standing-draft-autosave.hooks.d.ts +179 -0
- package/dist/features/panel-kit/hooks/use-standing-draft-autosave.hooks.js +300 -0
- package/dist/features/panel-kit/index.d.ts +7 -1
- package/dist/features/panel-kit/index.js +5 -1
- package/dist/features/resource-dashboard/types.d.ts +8 -41
- package/dist/features/resource-dashboard/types.js +3 -26
- package/dist/features/tab-strip/constants.d.ts +3 -4
- package/dist/features/tab-strip/constants.js +3 -4
- package/dist/features/tab-strip/dependencies.d.ts +1 -6
- package/dist/features/tab-strip/dependencies.js +1 -6
- package/dist/features/tab-strip/react/components/TabBar.d.ts +33 -1
- package/dist/features/tab-strip/react/components/TabBar.hooks.d.ts +57 -5
- package/dist/features/tab-strip/react/components/TabBar.hooks.js +63 -5
- package/dist/features/tab-strip/react/components/TabBar.js +12 -1
- package/dist/features/tab-strip/rules.d.ts +4 -17
- package/dist/features/tab-strip/rules.js +4 -17
- package/dist/features/tab-strip/types.d.ts +2 -13
- package/dist/icon-name.d.ts +1 -1
- package/dist/icon-name.js +1 -0
- package/dist/index.d.ts +42 -417
- package/dist/index.js +42 -430
- package/dist/react/components/CustomSelect.d.ts +6 -2
- package/dist/react/components/CustomSelect.js +20 -91
- package/dist/react/components/Icon.d.ts +6 -0
- package/dist/react/components/Icon.js +12 -0
- package/dist/react/components/select-popover.d.ts +106 -0
- package/dist/react/components/select-popover.js +210 -0
- package/dist/renderers/sandbox-scripts.d.ts +29 -0
- package/dist/renderers/sandbox-scripts.js +127 -0
- package/dist/renderers/sandboxed-document.js +3 -86
- package/dist/renderers/srcdoc/build.d.ts +10 -17
- package/dist/renderers/srcdoc/build.js +31 -234
- package/dist/styles/variables.css +2 -2
- package/dist/theme/apply.d.ts +1 -1
- package/dist/theme/apply.js +2 -2
- package/dist/theme/color-scheme.d.ts +1 -1
- package/dist/theme/default.d.ts +1 -3
- package/dist/theme/default.js +2 -10
- package/dist/theme/index.d.ts +3 -3
- package/dist/theme/index.js +2 -2
- package/dist/theme/stylesheet.d.ts +1 -7
- package/dist/theme/stylesheet.js +2 -13
- package/dist/theme/types.d.ts +1 -38
- package/dist/theme/types.js +2 -1
- package/dist/theme/validation.d.ts +1 -14
- package/dist/theme/validation.js +2 -54
- package/dist/utils/endpoint-policy.d.ts +21 -39
- package/dist/utils/endpoint-policy.js +19 -107
- package/dist/utils/file-system-errors.d.ts +1 -2
- package/dist/utils/file-system-errors.js +1 -2
- package/dist/utils/ime-composing.d.ts +0 -3
- package/dist/utils/ime-composing.js +0 -3
- package/dist/utils/platform.d.ts +1 -4
- package/dist/utils/platform.js +1 -4
- package/dist/utils/smooth-scroll-to-top.d.ts +1 -3
- package/dist/utils/smooth-scroll-to-top.js +3 -13
- package/dist/utils/timezone.js +1 -5
- package/dist/utils/uuid.d.ts +0 -3
- package/dist/utils/uuid.js +0 -3
- package/dist/utils/visual-stability.d.ts +1 -3
- package/dist/utils/visual-stability.js +1 -3
- package/package.json +74 -15
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Script that shims `localStorage`/`sessionStorage` with an in-memory store
|
|
3
|
+
* when the real Storage API throws, and intercepts `<a href>` clicks so
|
|
4
|
+
* hash-only links scroll within the document (instead of attempting a
|
|
5
|
+
* sandboxed top-level navigation) and `target="_blank"` links open via
|
|
6
|
+
* `window.open` behind an `http:`/`https:`/`mailto:` scheme allow-list
|
|
7
|
+
* (instead of being silently blocked by the iframe sandbox).
|
|
8
|
+
*/
|
|
9
|
+
// Sandboxed iframes (`sandbox="allow-scripts"`, deliberately without
|
|
10
|
+
// `allow-same-origin` — see `react/components/SrcDocSandbox.tsx`) raise a
|
|
11
|
+
// SecurityError on first `localStorage`/`sessionStorage` access. Many
|
|
12
|
+
// freeform-generated artifacts call `localStorage.getItem(...)` at the top
|
|
13
|
+
// of an IIFE with no try/catch — when it throws, the whole script aborts
|
|
14
|
+
// and the artifact renders blank. Installing a same-origin in-memory shim
|
|
15
|
+
// before any user script runs lets those artifacts degrade gracefully
|
|
16
|
+
// (position/state just doesn't persist across reloads) instead of crashing.
|
|
17
|
+
// The same script also intercepts same-document anchor navigation and
|
|
18
|
+
// `target="_blank"` links so they behave sanely inside a sandboxed iframe
|
|
19
|
+
// (`allow-popups`/`allow-popups-to-escape-sandbox` still required on the
|
|
20
|
+
// iframe itself for the latter to actually open).
|
|
21
|
+
export function buildStorageScript({ format }, _optional = {}) {
|
|
22
|
+
const modern = format === 'srcdoc';
|
|
23
|
+
return `<script data-jini-sandbox-shim>(function(){
|
|
24
|
+
function makeStore(){
|
|
25
|
+
var data = {};
|
|
26
|
+
var api = {
|
|
27
|
+
getItem: function(k){ return Object.prototype.hasOwnProperty.call(data, k) ? data[k] : null; },
|
|
28
|
+
setItem: function(k, v){ data[k] = String(v); },
|
|
29
|
+
removeItem: function(k){ delete data[k]; },
|
|
30
|
+
clear: function(){ data = {}; },
|
|
31
|
+
key: function(i){ return Object.keys(data)[i] || null; }
|
|
32
|
+
};
|
|
33
|
+
Object.defineProperty(api, 'length', { get: function(){ return Object.keys(data).length; } });
|
|
34
|
+
return api;
|
|
35
|
+
}
|
|
36
|
+
function tryShim(name){
|
|
37
|
+
var works = false;
|
|
38
|
+
try { works = !!window[name] && typeof window[name].getItem === 'function'; void window[name].length; }
|
|
39
|
+
catch (_) { works = false; }
|
|
40
|
+
if (works) return;
|
|
41
|
+
try { Object.defineProperty(window, name, { configurable: true, value: makeStore() }); }
|
|
42
|
+
catch (_) { try { window[name] = makeStore(); } catch (__) {} }
|
|
43
|
+
}
|
|
44
|
+
tryShim('localStorage');
|
|
45
|
+
tryShim('sessionStorage');
|
|
46
|
+
document.addEventListener('click', ${modern ? '(e) => {' : 'function(e){'}
|
|
47
|
+
if (!e.target || !(e.target instanceof Element)) return;
|
|
48
|
+
var link = e.target.closest('a[href]');
|
|
49
|
+
if (!link) return;
|
|
50
|
+
var href = link.getAttribute('href');
|
|
51
|
+
if (href === null) return;
|
|
52
|
+
var isAnchor = ${modern ? "href.startsWith('#')" : "href.indexOf('#') === 0"} || href === '';
|
|
53
|
+
if (isAnchor) {
|
|
54
|
+
e.preventDefault();
|
|
55
|
+
if (href === '' || href === '#') {
|
|
56
|
+
window.scrollTo({ top: 0 });
|
|
57
|
+
history.replaceState(null, '', ' ');
|
|
58
|
+
} else {
|
|
59
|
+
var targetId = href.slice(1);
|
|
60
|
+
var target = targetId ? document.getElementById(targetId) : null;
|
|
61
|
+
if (target) {
|
|
62
|
+
target.scrollIntoView();
|
|
63
|
+
location.hash === href && history.replaceState(null, '', ' ');
|
|
64
|
+
location.hash = href;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
} else if (link.getAttribute('target') === '_blank') {
|
|
68
|
+
e.preventDefault();
|
|
69
|
+
${modern ? 'let' : 'var'} safe = false;
|
|
70
|
+
try {
|
|
71
|
+
var url = new URL(href, location.href);
|
|
72
|
+
${modern ? `safe =
|
|
73
|
+
url.protocol === 'http:' ||
|
|
74
|
+
url.protocol === 'https:' ||
|
|
75
|
+
url.protocol === 'mailto:';` : "safe = url.protocol === 'http:' || url.protocol === 'https:' || url.protocol === 'mailto:';"}
|
|
76
|
+
} catch (_) {}
|
|
77
|
+
safe && window.open(href, '_blank', 'noopener,noreferrer');
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
})();</script>`;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Script that suppresses `focus()` calls (on `window` or any element) that
|
|
84
|
+
* aren't the direct result of a real pointer/keyboard event within the
|
|
85
|
+
* last second, so embedded content can't steal keyboard focus away from
|
|
86
|
+
* the host page around it.
|
|
87
|
+
*/
|
|
88
|
+
export function buildFocusScript({ format }, _optional = {}) {
|
|
89
|
+
const preview = format === 'srcdoc';
|
|
90
|
+
return `<script ${preview ? 'data-jini-preview-focus-guard' : 'data-jini-focus-guard'}>(function(){
|
|
91
|
+
var lastTrustedInputAt = 0;
|
|
92
|
+
function userActivated(){
|
|
93
|
+
return Date.now() - lastTrustedInputAt < 1000;
|
|
94
|
+
}
|
|
95
|
+
function markTrustedInput(event){
|
|
96
|
+
if (event && event.isTrusted) lastTrustedInputAt = Date.now();
|
|
97
|
+
}
|
|
98
|
+
${preview ? ` document.addEventListener('pointerdown', function(event){
|
|
99
|
+
markTrustedInput(event);
|
|
100
|
+
}, true);
|
|
101
|
+
document.addEventListener('keydown', function(event){
|
|
102
|
+
markTrustedInput(event);
|
|
103
|
+
}, true);` : ` document.addEventListener('pointerdown', markTrustedInput, true);
|
|
104
|
+
document.addEventListener('keydown', markTrustedInput, true);`}
|
|
105
|
+
try {
|
|
106
|
+
var nativeWindowFocus = window.focus && window.focus.bind(window);
|
|
107
|
+
Object.defineProperty(window, 'focus', {
|
|
108
|
+
configurable: true,
|
|
109
|
+
writable: true,
|
|
110
|
+
value: function(){
|
|
111
|
+
if (userActivated() && nativeWindowFocus) return nativeWindowFocus();
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
} catch (_) {}
|
|
115
|
+
try {
|
|
116
|
+
var nativeElementFocus = HTMLElement.prototype.focus;
|
|
117
|
+
Object.defineProperty(HTMLElement.prototype, 'focus', {
|
|
118
|
+
configurable: true,
|
|
119
|
+
writable: true,
|
|
120
|
+
value: function(options){
|
|
121
|
+
if (userActivated()) return nativeElementFocus.call(this, options);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
} catch (_) {}
|
|
125
|
+
})();</script>`;
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=sandbox-scripts.js.map
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { buildFocusScript, buildStorageScript } from './sandbox-scripts.js';
|
|
1
2
|
import { escapeHtmlAttribute, injectAfterHeadOpen, injectBeforeHeadEnd } from './html-utils.js';
|
|
2
3
|
/** Whether `html` already looks like a full document (starts with `<!doctype>` or `<html>`), vs. a bare fragment. */
|
|
3
4
|
export function isFullHtmlDocument(html) {
|
|
@@ -35,61 +36,7 @@ export function injectBaseHref(doc, baseHref) {
|
|
|
35
36
|
* (instead of being silently blocked by the iframe sandbox).
|
|
36
37
|
*/
|
|
37
38
|
export function buildStorageShimScript() {
|
|
38
|
-
return
|
|
39
|
-
function makeStore(){
|
|
40
|
-
var data = {};
|
|
41
|
-
var api = {
|
|
42
|
-
getItem: function(k){ return Object.prototype.hasOwnProperty.call(data, k) ? data[k] : null; },
|
|
43
|
-
setItem: function(k, v){ data[k] = String(v); },
|
|
44
|
-
removeItem: function(k){ delete data[k]; },
|
|
45
|
-
clear: function(){ data = {}; },
|
|
46
|
-
key: function(i){ return Object.keys(data)[i] || null; }
|
|
47
|
-
};
|
|
48
|
-
Object.defineProperty(api, 'length', { get: function(){ return Object.keys(data).length; } });
|
|
49
|
-
return api;
|
|
50
|
-
}
|
|
51
|
-
function tryShim(name){
|
|
52
|
-
var works = false;
|
|
53
|
-
try { works = !!window[name] && typeof window[name].getItem === 'function'; void window[name].length; }
|
|
54
|
-
catch (_) { works = false; }
|
|
55
|
-
if (works) return;
|
|
56
|
-
try { Object.defineProperty(window, name, { configurable: true, value: makeStore() }); }
|
|
57
|
-
catch (_) { try { window[name] = makeStore(); } catch (__) {} }
|
|
58
|
-
}
|
|
59
|
-
tryShim('localStorage');
|
|
60
|
-
tryShim('sessionStorage');
|
|
61
|
-
document.addEventListener('click', function(e){
|
|
62
|
-
if (!e.target || !(e.target instanceof Element)) return;
|
|
63
|
-
var link = e.target.closest('a[href]');
|
|
64
|
-
if (!link) return;
|
|
65
|
-
var href = link.getAttribute('href');
|
|
66
|
-
if (href === null) return;
|
|
67
|
-
var isAnchor = href.indexOf('#') === 0 || href === '';
|
|
68
|
-
if (isAnchor) {
|
|
69
|
-
e.preventDefault();
|
|
70
|
-
if (href === '' || href === '#') {
|
|
71
|
-
window.scrollTo({ top: 0 });
|
|
72
|
-
history.replaceState(null, '', ' ');
|
|
73
|
-
} else {
|
|
74
|
-
var targetId = href.slice(1);
|
|
75
|
-
var target = targetId ? document.getElementById(targetId) : null;
|
|
76
|
-
if (target) {
|
|
77
|
-
target.scrollIntoView();
|
|
78
|
-
location.hash === href && history.replaceState(null, '', ' ');
|
|
79
|
-
location.hash = href;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
} else if (link.getAttribute('target') === '_blank') {
|
|
83
|
-
e.preventDefault();
|
|
84
|
-
var safe = false;
|
|
85
|
-
try {
|
|
86
|
-
var url = new URL(href, location.href);
|
|
87
|
-
safe = url.protocol === 'http:' || url.protocol === 'https:' || url.protocol === 'mailto:';
|
|
88
|
-
} catch (_) {}
|
|
89
|
-
safe && window.open(href, '_blank', 'noopener,noreferrer');
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
})();</script>`;
|
|
39
|
+
return buildStorageScript({ format: 'sandbox' });
|
|
93
40
|
}
|
|
94
41
|
/**
|
|
95
42
|
* Script that suppresses `focus()` calls (on `window` or any element) that
|
|
@@ -98,37 +45,7 @@ export function buildStorageShimScript() {
|
|
|
98
45
|
* the host page around it.
|
|
99
46
|
*/
|
|
100
47
|
export function buildFocusGuardScript() {
|
|
101
|
-
return
|
|
102
|
-
var lastTrustedInputAt = 0;
|
|
103
|
-
function userActivated(){
|
|
104
|
-
return Date.now() - lastTrustedInputAt < 1000;
|
|
105
|
-
}
|
|
106
|
-
function markTrustedInput(event){
|
|
107
|
-
if (event && event.isTrusted) lastTrustedInputAt = Date.now();
|
|
108
|
-
}
|
|
109
|
-
document.addEventListener('pointerdown', markTrustedInput, true);
|
|
110
|
-
document.addEventListener('keydown', markTrustedInput, true);
|
|
111
|
-
try {
|
|
112
|
-
var nativeWindowFocus = window.focus && window.focus.bind(window);
|
|
113
|
-
Object.defineProperty(window, 'focus', {
|
|
114
|
-
configurable: true,
|
|
115
|
-
writable: true,
|
|
116
|
-
value: function(){
|
|
117
|
-
if (userActivated() && nativeWindowFocus) return nativeWindowFocus();
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
} catch (_) {}
|
|
121
|
-
try {
|
|
122
|
-
var nativeElementFocus = HTMLElement.prototype.focus;
|
|
123
|
-
Object.defineProperty(HTMLElement.prototype, 'focus', {
|
|
124
|
-
configurable: true,
|
|
125
|
-
writable: true,
|
|
126
|
-
value: function(options){
|
|
127
|
-
if (userActivated()) return nativeElementFocus.call(this, options);
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
} catch (_) {}
|
|
131
|
-
})();</script>`;
|
|
48
|
+
return buildFocusScript({ format: 'sandbox' });
|
|
132
49
|
}
|
|
133
50
|
/**
|
|
134
51
|
* Build a sandboxed-iframe-ready HTML document from arbitrary artifact
|
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Wrap artifact HTML for a sandboxed iframe (`srcDoc`).
|
|
3
|
-
*
|
|
4
|
-
* Origin: `apps/web/src/runtime/srcdoc.ts` in the origin project (2,689
|
|
5
|
-
* lines). Ported the generic sandbox mechanics only — document wrapping,
|
|
6
|
-
* title sanitization, the same-origin storage shim, the anti-focus-steal
|
|
7
|
-
* guard, and the lazy-transport-shell perf optimization. NOT ported: every
|
|
8
|
-
* postMessage bridge (deck navigation, comment/inspect element-selection,
|
|
9
|
-
* CSS tweaks palette, manual-edit overlay, snapshot/export-capture) — those
|
|
10
|
-
* are that product's own UI protocols. They become `SrcDocBridge` plugins a
|
|
11
|
-
* host registers via `bridges` (see `bridge.ts`), not built-ins. See
|
|
12
|
-
* `archived provenance ledger` for the full file-by-file breakdown.
|
|
13
|
-
*/
|
|
14
1
|
import { type SrcDocBridge } from './bridge.js';
|
|
15
2
|
export interface BuildSrcDocOptions {
|
|
16
3
|
/** `<base href>` for the document, so relative asset/script URLs resolve as if served from this path. */
|
|
@@ -63,10 +50,16 @@ export declare function sanitizePreviewTitle(text: string): string;
|
|
|
63
50
|
/**
|
|
64
51
|
* Rewrite the `<title>` element in an HTML string so its text content is
|
|
65
52
|
* filename-safe (see {@link sanitizePreviewTitle}). Only the real `<title>`
|
|
66
|
-
* in the `<head>`
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
53
|
+
* in the parsed `<head>` is changed — comments, scripts, styles, templates,
|
|
54
|
+
* and body titles cannot supply it. parse5 owns HTML recovery and single-pass
|
|
55
|
+
* entity decoding, identically in Node and the browser. Source offsets let us
|
|
56
|
+
* splice only title content; serializing the document would change unrelated
|
|
57
|
+
* formatting. A title without an explicit closing tag is left untouched.
|
|
58
|
+
*
|
|
59
|
+
* @param html Original document source; never mutated or re-serialized.
|
|
60
|
+
* @returns Source with the first head title's decoded, filename-safe text.
|
|
61
|
+
* @complexity O(n) time and space in source length for parsing and splicing.
|
|
62
|
+
* @example sanitizeTitleInDoc('<title>Q3: Report</title>') // '<title>Q3- Report</title>'
|
|
70
63
|
*/
|
|
71
64
|
export declare function sanitizeTitleInDoc(html: string): string;
|
|
72
65
|
/** Splice `payload` right after the opening `<head …>` tag, prepending it when no `<head>` exists. */
|
|
@@ -11,6 +11,10 @@
|
|
|
11
11
|
* host registers via `bridges` (see `bridge.ts`), not built-ins. See
|
|
12
12
|
* `archived provenance ledger` for the full file-by-file breakdown.
|
|
13
13
|
*/
|
|
14
|
+
import { defaultTreeAdapter, parse } from 'parse5';
|
|
15
|
+
import { escapeHtmlAttribute } from '../html-utils.js';
|
|
16
|
+
import { injectBaseHref, wrapFragmentAsDocument } from '../sandboxed-document.js';
|
|
17
|
+
import { buildFocusScript, buildStorageScript } from '../sandbox-scripts.js';
|
|
14
18
|
import { applySrcDocBridges } from './bridge.js';
|
|
15
19
|
/**
|
|
16
20
|
* Default Content-Security-Policy applied to every srcDoc document unless
|
|
@@ -24,7 +28,7 @@ import { applySrcDocBridges } from './bridge.js';
|
|
|
24
28
|
*/
|
|
25
29
|
export const DEFAULT_SRC_DOC_CSP = "default-src 'none'; script-src 'unsafe-inline' 'unsafe-eval' blob: data:; style-src 'unsafe-inline'; img-src * data: blob:; font-src * data:; media-src * data: blob:; connect-src *; frame-src *; child-src *; object-src 'none'; base-uri 'none'; form-action 'none';";
|
|
26
30
|
function injectContentSecurityPolicy(doc, csp) {
|
|
27
|
-
const tag = `<meta http-equiv="Content-Security-Policy" content="${
|
|
31
|
+
const tag = `<meta http-equiv="Content-Security-Policy" content="${escapeHtmlAttribute(csp)}">`;
|
|
28
32
|
return injectAfterHeadOpen(doc, tag);
|
|
29
33
|
}
|
|
30
34
|
/**
|
|
@@ -44,106 +48,35 @@ export function sanitizePreviewTitle(text) {
|
|
|
44
48
|
result = result.replace(/[:#%&*{}\\<>?/+|"]+/g, '-');
|
|
45
49
|
return result.trim();
|
|
46
50
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
egrave: 'è', eacute: 'é', ecirc: 'ê', euml: 'ë',
|
|
51
|
-
igrave: 'ì', iacute: 'í', icirc: 'î', iuml: 'ï',
|
|
52
|
-
eth: 'ð', ntilde: 'ñ',
|
|
53
|
-
ograve: 'ò', oacute: 'ó', ocirc: 'ô', otilde: 'õ', ouml: 'ö', oslash: 'ø',
|
|
54
|
-
ugrave: 'ù', uacute: 'ú', ucirc: 'û', uuml: 'ü',
|
|
55
|
-
yacute: 'ý', thorn: 'þ', yuml: 'ÿ',
|
|
56
|
-
Agrave: 'À', Aacute: 'Á', Acirc: 'Â', Atilde: 'Ã', Auml: 'Ä', Aring: 'Å',
|
|
57
|
-
AElig: 'Æ', Ccedil: 'Ç',
|
|
58
|
-
Egrave: 'È', Eacute: 'É', Ecirc: 'Ê', Euml: 'Ë',
|
|
59
|
-
Igrave: 'Ì', Iacute: 'Í', Icirc: 'Î', Iuml: 'Ï',
|
|
60
|
-
ETH: 'Ð', Ntilde: 'Ñ',
|
|
61
|
-
Ograve: 'Ò', Oacute: 'Ó', Ocirc: 'Ô', Otilde: 'Õ', Ouml: 'Ö', Oslash: 'Ø',
|
|
62
|
-
Ugrave: 'Ù', Uacute: 'Ú', Ucirc: 'Û', Uuml: 'Ü',
|
|
63
|
-
Yacute: 'Ý', THORN: 'Þ',
|
|
64
|
-
ndash: '–', mdash: '—', lsquo: '‘', rsquo: '’',
|
|
65
|
-
ldquo: '“', rdquo: '”', hellip: '…', trade: '™', reg: '®',
|
|
66
|
-
copy: '©', deg: '°', euro: '€', pound: '£', yen: '¥',
|
|
67
|
-
};
|
|
68
|
-
function safeFromCodePoint(cp) {
|
|
69
|
-
if (cp < 0 || cp > 0x10ffff)
|
|
70
|
-
return '�';
|
|
71
|
-
return String.fromCodePoint(cp);
|
|
72
|
-
}
|
|
73
|
-
function decodeHtmlEntitiesForTitle(encoded) {
|
|
74
|
-
return encoded
|
|
75
|
-
.replace(/&([A-Za-z]+);/g, (match, name) => NAMED_ENTITY_MAP[name] ?? match)
|
|
76
|
-
.replace(/&/gi, '&')
|
|
77
|
-
.replace(/</gi, '<')
|
|
78
|
-
.replace(/>/gi, '>')
|
|
79
|
-
.replace(/"/gi, '"')
|
|
80
|
-
.replace(/'/gi, "'")
|
|
81
|
-
.replace(/&#(\d+);/g, (_, n) => safeFromCodePoint(Number(n)))
|
|
82
|
-
.replace(/&#x([0-9a-f]+);/gi, (_, h) => safeFromCodePoint(parseInt(h, 16)));
|
|
83
|
-
}
|
|
84
|
-
function findRealTitleOffset(html, searchLimit) {
|
|
85
|
-
let i = 0;
|
|
86
|
-
const limit = Math.min(html.length, searchLimit);
|
|
87
|
-
while (i < limit) {
|
|
88
|
-
if (html.charCodeAt(i) === 60 /* < */ && html.slice(i, i + 4) === '<!--') {
|
|
89
|
-
const end = html.indexOf('-->', i + 4);
|
|
90
|
-
if (end < 0)
|
|
91
|
-
return -1;
|
|
92
|
-
i = end + 3;
|
|
93
|
-
continue;
|
|
94
|
-
}
|
|
95
|
-
if (html.charCodeAt(i) === 60 /* < */) {
|
|
96
|
-
const tagMatch = /^<(script|style)\b/i.exec(html.slice(i, i + 20));
|
|
97
|
-
if (tagMatch) {
|
|
98
|
-
const closingTag = `</${tagMatch[1]}`;
|
|
99
|
-
const end = html.toLowerCase().indexOf(closingTag.toLowerCase(), i + tagMatch[0].length);
|
|
100
|
-
if (end < 0)
|
|
101
|
-
return -1;
|
|
102
|
-
const closeEnd = html.indexOf('>', end);
|
|
103
|
-
i = closeEnd >= 0 ? closeEnd + 1 : end + closingTag.length;
|
|
104
|
-
continue;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
if (html.charCodeAt(i) === 60 /* < */) {
|
|
108
|
-
if (/^<title[\s>]/i.test(html.slice(i, i + 8))) {
|
|
109
|
-
return i;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
i++;
|
|
113
|
-
}
|
|
114
|
-
return -1;
|
|
51
|
+
/** Finds a direct element child; template contents must not supply the document title. */
|
|
52
|
+
function findElementChild({ parent, tagName }) {
|
|
53
|
+
return parent?.childNodes.find((node) => defaultTreeAdapter.isElementNode(node) && node.tagName === tagName);
|
|
115
54
|
}
|
|
116
55
|
/**
|
|
117
56
|
* Rewrite the `<title>` element in an HTML string so its text content is
|
|
118
57
|
* filename-safe (see {@link sanitizePreviewTitle}). Only the real `<title>`
|
|
119
|
-
* in the `<head>`
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
58
|
+
* in the parsed `<head>` is changed — comments, scripts, styles, templates,
|
|
59
|
+
* and body titles cannot supply it. parse5 owns HTML recovery and single-pass
|
|
60
|
+
* entity decoding, identically in Node and the browser. Source offsets let us
|
|
61
|
+
* splice only title content; serializing the document would change unrelated
|
|
62
|
+
* formatting. A title without an explicit closing tag is left untouched.
|
|
63
|
+
*
|
|
64
|
+
* @param html Original document source; never mutated or re-serialized.
|
|
65
|
+
* @returns Source with the first head title's decoded, filename-safe text.
|
|
66
|
+
* @complexity O(n) time and space in source length for parsing and splicing.
|
|
67
|
+
* @example sanitizeTitleInDoc('<title>Q3: Report</title>') // '<title>Q3- Report</title>'
|
|
123
68
|
*/
|
|
124
69
|
export function sanitizeTitleInDoc(html) {
|
|
125
|
-
const
|
|
126
|
-
const
|
|
127
|
-
const
|
|
128
|
-
const
|
|
129
|
-
const
|
|
130
|
-
if (
|
|
70
|
+
const document = parse(html, { sourceCodeLocationInfo: true });
|
|
71
|
+
const root = findElementChild({ parent: document, tagName: 'html' });
|
|
72
|
+
const head = findElementChild({ parent: root, tagName: 'head' });
|
|
73
|
+
const title = findElementChild({ parent: head, tagName: 'title' });
|
|
74
|
+
const location = title?.sourceCodeLocation;
|
|
75
|
+
if (!title || !location?.startTag || !location.endTag)
|
|
131
76
|
return html;
|
|
132
|
-
const
|
|
133
|
-
if (openTagEnd < 0)
|
|
134
|
-
return html;
|
|
135
|
-
const closingTagStart = html.toLowerCase().indexOf('</title>', openTagEnd + 1);
|
|
136
|
-
if (closingTagStart < 0)
|
|
137
|
-
return html;
|
|
138
|
-
const closingTagEnd = html.indexOf('>', closingTagStart);
|
|
139
|
-
if (closingTagEnd < 0)
|
|
140
|
-
return html;
|
|
141
|
-
const openTag = html.slice(titleStart, openTagEnd + 1);
|
|
142
|
-
const rawContent = html.slice(openTagEnd + 1, closingTagStart);
|
|
143
|
-
const closeTag = html.slice(closingTagStart, closingTagEnd + 1);
|
|
144
|
-
const decoded = decodeHtmlEntitiesForTitle(rawContent);
|
|
77
|
+
const decoded = title.childNodes.filter(defaultTreeAdapter.isTextNode).map((node) => node.value).join('');
|
|
145
78
|
const safe = sanitizePreviewTitle(decoded);
|
|
146
|
-
return html.slice(0,
|
|
79
|
+
return html.slice(0, location.startTag.endOffset) + safe + html.slice(location.endTag.startOffset);
|
|
147
80
|
}
|
|
148
81
|
function serializeHtmlDocument(doc) {
|
|
149
82
|
const doctype = doc.doctype ? '<!doctype html>\n' : '';
|
|
@@ -205,133 +138,8 @@ export function injectBeforeBodyEnd(doc, payload) {
|
|
|
205
138
|
}
|
|
206
139
|
return doc + payload;
|
|
207
140
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
function injectBaseHref(doc, baseHref) {
|
|
212
|
-
const safeHref = escapeAttr(baseHref);
|
|
213
|
-
const tag = `<base href="${safeHref}">`;
|
|
214
|
-
if (/<head[^>]*>/i.test(doc)) {
|
|
215
|
-
return doc.replace(/<head[^>]*>/i, (m) => `${m}${tag}`);
|
|
216
|
-
}
|
|
217
|
-
if (/<html[^>]*>/i.test(doc)) {
|
|
218
|
-
return doc.replace(/<html[^>]*>/i, (m) => `${m}<head>${tag}</head>`);
|
|
219
|
-
}
|
|
220
|
-
return tag + doc;
|
|
221
|
-
}
|
|
222
|
-
// Sandboxed iframes (`sandbox="allow-scripts"`, deliberately without
|
|
223
|
-
// `allow-same-origin` — see `react/components/SrcDocSandbox.tsx`) raise a
|
|
224
|
-
// SecurityError on first `localStorage`/`sessionStorage` access. Many
|
|
225
|
-
// freeform-generated artifacts call `localStorage.getItem(...)` at the top
|
|
226
|
-
// of an IIFE with no try/catch — when it throws, the whole script aborts
|
|
227
|
-
// and the artifact renders blank. Installing a same-origin in-memory shim
|
|
228
|
-
// before any user script runs lets those artifacts degrade gracefully
|
|
229
|
-
// (position/state just doesn't persist across reloads) instead of crashing.
|
|
230
|
-
// The same script also intercepts same-document anchor navigation and
|
|
231
|
-
// `target="_blank"` links so they behave sanely inside a sandboxed iframe
|
|
232
|
-
// (`allow-popups`/`allow-popups-to-escape-sandbox` still required on the
|
|
233
|
-
// iframe itself for the latter to actually open).
|
|
234
|
-
function injectSandboxShim(doc) {
|
|
235
|
-
const shim = `<script data-jini-sandbox-shim>(function(){
|
|
236
|
-
function makeStore(){
|
|
237
|
-
var data = {};
|
|
238
|
-
var api = {
|
|
239
|
-
getItem: function(k){ return Object.prototype.hasOwnProperty.call(data, k) ? data[k] : null; },
|
|
240
|
-
setItem: function(k, v){ data[k] = String(v); },
|
|
241
|
-
removeItem: function(k){ delete data[k]; },
|
|
242
|
-
clear: function(){ data = {}; },
|
|
243
|
-
key: function(i){ return Object.keys(data)[i] || null; }
|
|
244
|
-
};
|
|
245
|
-
Object.defineProperty(api, 'length', { get: function(){ return Object.keys(data).length; } });
|
|
246
|
-
return api;
|
|
247
|
-
}
|
|
248
|
-
function tryShim(name){
|
|
249
|
-
var works = false;
|
|
250
|
-
try { works = !!window[name] && typeof window[name].getItem === 'function'; void window[name].length; }
|
|
251
|
-
catch (_) { works = false; }
|
|
252
|
-
if (works) return;
|
|
253
|
-
try { Object.defineProperty(window, name, { configurable: true, value: makeStore() }); }
|
|
254
|
-
catch (_) { try { window[name] = makeStore(); } catch (__) {} }
|
|
255
|
-
}
|
|
256
|
-
tryShim('localStorage');
|
|
257
|
-
tryShim('sessionStorage');
|
|
258
|
-
document.addEventListener('click', (e) => {
|
|
259
|
-
if (!e.target || !(e.target instanceof Element)) return;
|
|
260
|
-
var link = e.target.closest('a[href]');
|
|
261
|
-
if (!link) return;
|
|
262
|
-
var href = link.getAttribute('href');
|
|
263
|
-
if (href === null) return;
|
|
264
|
-
var isAnchor = href.startsWith('#') || href === '';
|
|
265
|
-
if (isAnchor) {
|
|
266
|
-
e.preventDefault();
|
|
267
|
-
if (href === '' || href === '#') {
|
|
268
|
-
window.scrollTo({ top: 0 });
|
|
269
|
-
history.replaceState(null, '', ' ');
|
|
270
|
-
} else {
|
|
271
|
-
var targetId = href.slice(1);
|
|
272
|
-
var target = targetId ? document.getElementById(targetId) : null;
|
|
273
|
-
if (target) {
|
|
274
|
-
target.scrollIntoView();
|
|
275
|
-
location.hash === href && history.replaceState(null, '', ' ');
|
|
276
|
-
location.hash = href;
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
} else if (link.getAttribute('target') === '_blank') {
|
|
280
|
-
e.preventDefault();
|
|
281
|
-
let safe = false;
|
|
282
|
-
try {
|
|
283
|
-
var url = new URL(href, location.href);
|
|
284
|
-
safe =
|
|
285
|
-
url.protocol === 'http:' ||
|
|
286
|
-
url.protocol === 'https:' ||
|
|
287
|
-
url.protocol === 'mailto:';
|
|
288
|
-
} catch (_) {}
|
|
289
|
-
safe && window.open(href, '_blank', 'noopener,noreferrer');
|
|
290
|
-
}
|
|
291
|
-
});
|
|
292
|
-
})();</script>`;
|
|
293
|
-
if (/<head[^>]*>/i.test(doc))
|
|
294
|
-
return doc.replace(/<head[^>]*>/i, (m) => `${m}${shim}`);
|
|
295
|
-
if (/<body[^>]*>/i.test(doc))
|
|
296
|
-
return doc.replace(/<body[^>]*>/i, (m) => `${m}${shim}`);
|
|
297
|
-
return shim + doc;
|
|
298
|
-
}
|
|
299
|
-
function injectPreviewFocusGuard(doc) {
|
|
300
|
-
const script = `<script data-jini-preview-focus-guard>(function(){
|
|
301
|
-
var lastTrustedInputAt = 0;
|
|
302
|
-
function userActivated(){
|
|
303
|
-
return Date.now() - lastTrustedInputAt < 1000;
|
|
304
|
-
}
|
|
305
|
-
function markTrustedInput(event){
|
|
306
|
-
if (event && event.isTrusted) lastTrustedInputAt = Date.now();
|
|
307
|
-
}
|
|
308
|
-
document.addEventListener('pointerdown', function(event){
|
|
309
|
-
markTrustedInput(event);
|
|
310
|
-
}, true);
|
|
311
|
-
document.addEventListener('keydown', function(event){
|
|
312
|
-
markTrustedInput(event);
|
|
313
|
-
}, true);
|
|
314
|
-
try {
|
|
315
|
-
var nativeWindowFocus = window.focus && window.focus.bind(window);
|
|
316
|
-
Object.defineProperty(window, 'focus', {
|
|
317
|
-
configurable: true,
|
|
318
|
-
writable: true,
|
|
319
|
-
value: function(){
|
|
320
|
-
if (userActivated() && nativeWindowFocus) return nativeWindowFocus();
|
|
321
|
-
}
|
|
322
|
-
});
|
|
323
|
-
} catch (_) {}
|
|
324
|
-
try {
|
|
325
|
-
var nativeElementFocus = HTMLElement.prototype.focus;
|
|
326
|
-
Object.defineProperty(HTMLElement.prototype, 'focus', {
|
|
327
|
-
configurable: true,
|
|
328
|
-
writable: true,
|
|
329
|
-
value: function(options){
|
|
330
|
-
if (userActivated()) return nativeElementFocus.call(this, options);
|
|
331
|
-
}
|
|
332
|
-
});
|
|
333
|
-
} catch (_) {}
|
|
334
|
-
})();</script>`;
|
|
141
|
+
/** Srcdoc keeps its head/body/prepend fallback instead of the generic builder's string-only head insertion. */
|
|
142
|
+
function injectSandboxScript(doc, script) {
|
|
335
143
|
if (/<head[^>]*>/i.test(doc))
|
|
336
144
|
return doc.replace(/<head[^>]*>/i, (m) => `${m}${script}`);
|
|
337
145
|
if (/<body[^>]*>/i.test(doc))
|
|
@@ -421,24 +229,13 @@ export function canActivateSrcDocTransport(state) {
|
|
|
421
229
|
export function buildSrcDoc(html, options = {}) {
|
|
422
230
|
if (options.lazyTransport)
|
|
423
231
|
return buildLazySrcDocTransport();
|
|
424
|
-
const
|
|
425
|
-
const isFullDoc = head.startsWith('<!doctype') || head.startsWith('<html');
|
|
426
|
-
const wrapped = isFullDoc
|
|
427
|
-
? html
|
|
428
|
-
: `<!doctype html>
|
|
429
|
-
<html>
|
|
430
|
-
<head>
|
|
431
|
-
<meta charset="utf-8" />
|
|
432
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
433
|
-
</head>
|
|
434
|
-
<body>${html}</body>
|
|
435
|
-
</html>`;
|
|
232
|
+
const wrapped = wrapFragmentAsDocument(html);
|
|
436
233
|
const withSafeTitle = sanitizeTitleInDoc(wrapped);
|
|
437
234
|
const cspValue = options.csp === false ? null : options.csp === true || options.csp === undefined ? DEFAULT_SRC_DOC_CSP : options.csp;
|
|
438
235
|
const withCsp = cspValue ? injectContentSecurityPolicy(withSafeTitle, cspValue) : withSafeTitle;
|
|
439
236
|
const withBase = options.baseHref ? injectBaseHref(withCsp, options.baseHref) : withCsp;
|
|
440
|
-
const withShim =
|
|
441
|
-
const withFocusGuard = options.previewFocusGuard ?
|
|
237
|
+
const withShim = injectSandboxScript(withBase, buildStorageScript({ format: 'srcdoc' }));
|
|
238
|
+
const withFocusGuard = options.previewFocusGuard ? injectSandboxScript(withShim, buildFocusScript({ format: 'srcdoc' })) : withShim;
|
|
442
239
|
const ctx = { baseHref: options.baseHref, ...options.bridgeContext };
|
|
443
240
|
const withBridges = options.bridges?.length
|
|
444
241
|
? applySrcDocBridges(withFocusGuard, options.bridges, ctx)
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
lightness band, chroma floor, CVD >= 8 (worst 10.1 deutan light, 10.9 dark), normal-vision >= 15,
|
|
37
37
|
>= 3:1 contrast — all pass. Dark is stepped for the dark surface, not a flip of light: dark
|
|
38
38
|
series 1 sits a step lighter, because the light orange's chroma reads grey as a large fill on a
|
|
39
|
-
near-black surface. This was
|
|
39
|
+
near-black surface. This was an adopting admin shell's palette first (host commit b183ca5a8). */
|
|
40
40
|
:root, [data-color-scheme="light"], [data-theme="light"] {
|
|
41
41
|
--jini-chart-default-1: oklch(55.29% 0.1129 43.4);
|
|
42
42
|
--jini-chart-default-2: oklch(50% 0.11 240);
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
/* Both dark attributes: the base tokens above flip on `data-color-scheme`, while hosts that follow
|
|
50
|
-
this package's `data-theme` convention on <html> (sketch-editor, shiki,
|
|
50
|
+
this package's `data-theme` convention on <html> (sketch-editor, shiki, host admin shells) flip that. */
|
|
51
51
|
[data-color-scheme="dark"], [data-theme="dark"] {
|
|
52
52
|
--jini-chart-default-1: oklch(62% 0.11 44);
|
|
53
53
|
--jini-chart-default-2: oklch(64% 0.11 240);
|
package/dist/theme/apply.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AdminTheme, AdminThemeDocument, AdminThemeTarget } from '
|
|
1
|
+
import type { AdminTheme, AdminThemeDocument, AdminThemeTarget } from '../core/theme/types.js';
|
|
2
2
|
/** Install both palettes on the injected scope and load fonts. Cleanup restores the host. */
|
|
3
3
|
export declare function applyAdminTheme({ theme }: {
|
|
4
4
|
readonly theme: AdminTheme;
|
package/dist/theme/apply.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { defaultAdminTheme } from '
|
|
2
|
-
import { adminThemeColorKeys, validateAdminTheme } from '
|
|
1
|
+
import { defaultAdminTheme } from '../core/theme/default.js';
|
|
2
|
+
import { adminThemeColorKeys, validateAdminTheme } from '../core/theme/validation.js';
|
|
3
3
|
const fontLeases = new WeakMap();
|
|
4
4
|
function acquireFont(document, href) {
|
|
5
5
|
const leases = fontLeases.get(document) ?? new Map();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ColorScheme, ColorSchemePreference, MatchMediaPort } from '
|
|
1
|
+
import type { ColorScheme, ColorSchemePreference, MatchMediaPort } from '../core/theme/types.js';
|
|
2
2
|
/** Explicit choices are deterministic; system resolution requires the host's media port. */
|
|
3
3
|
export declare function resolveColorScheme({ preference }: {
|
|
4
4
|
readonly preference: ColorSchemePreference;
|
package/dist/theme/default.d.ts
CHANGED
package/dist/theme/default.js
CHANGED
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
3
|
-
name: 'neutral',
|
|
4
|
-
fonts: Object.freeze({ body: 'system-ui, sans-serif', heading: 'system-ui, sans-serif', mono: 'ui-monospace, monospace' }),
|
|
5
|
-
light: Object.freeze({ primary: '#363636', primaryInk: '#ffffff', bg: '#ffffff', surface: '#f5f5f5',
|
|
6
|
-
text: '#202020', muted: '#666666', border: '#d9d9d9', danger: '#b42318', success: '#18763b', warning: '#845800' }),
|
|
7
|
-
dark: Object.freeze({ primary: '#e0e0e0', primaryInk: '#202020', bg: '#171717', surface: '#242424',
|
|
8
|
-
text: '#f0f0f0', muted: '#b3b3b3', border: '#454545', danger: '#ff8b80', success: '#79d69b', warning: '#ebc36b' }),
|
|
9
|
-
radius: '8px', density: 1,
|
|
10
|
-
});
|
|
1
|
+
// Compatibility path; the shared implementation lives in core.
|
|
2
|
+
export * from '../core/theme/default.js';
|
|
11
3
|
//# sourceMappingURL=default.js.map
|