返回 AiToEarn
live-ui-core.mjs
根目录 / project / aitoearn-web / .agents / skills / impeccable / scripts / live-ui-core.mjs
1 /**
2 * Framework-neutral Impeccable live chrome contract.
3 *
4 * The production browser bundle is intentionally plain DOM so Svelte, React,
5 * Vue, and static adapters can all mount the same chrome. This module is the
6 * testable contract/inventory for that bundle; live-browser.js mirrors these
7 * values at runtime because it is served as a standalone script.
8 */
9
10 export const LIVE_CHROME_MOUNT_CONTRACT = Object.freeze([
11 'root',
12 'transport',
13 'state',
14 'actions',
15 ]);
16
17 export const LIVE_UI_SURFACES = Object.freeze([
18 {
19 key: 'global-bottom-bar',
20 ids: [
21 'impeccable-live-global-bar',
22 'impeccable-live-global-bar-brand',
23 'impeccable-live-pick-toggle',
24 'impeccable-live-insert-toggle',
25 'impeccable-live-detect-toggle',
26 'impeccable-live-detect-badge',
27 'impeccable-live-design-toggle',
28 'impeccable-live-page-chat',
29 'impeccable-live-page-chat-input',
30 'impeccable-live-page-chat-voice',
31 ],
32 states: ['rest', 'hover', 'focus-visible', 'pressed', 'active', 'tooltip'],
33 },
34 {
35 key: 'pending-copy-edit-dock',
36 ids: ['impeccable-live-pending-dock'],
37 states: ['closed', 'open', 'hover', 'pressed', 'loading', 'rollback', 'keep-fixing'],
38 },
39 {
40 key: 'element-selection-chrome',
41 ids: [
42 'impeccable-live-highlight',
43 'impeccable-live-tooltip',
44 'impeccable-live-bar',
45 'impeccable-live-configure-input-wrap',
46 'impeccable-live-input',
47 'impeccable-live-configure-voice',
48 ],
49 states: ['rest', 'hover', 'focus-visible', 'pressed', 'disabled'],
50 },
51 {
52 key: 'action-picker',
53 ids: ['impeccable-live-picker'],
54 states: ['closed', 'open', 'option-hover', 'option-focus'],
55 },
56 {
57 key: 'edit-chrome',
58 ids: ['impeccable-live-edit-badge'],
59 states: ['enabled', 'disabled', 'editing', 'cancel', 'save', 'edited-content'],
60 },
61 {
62 key: 'generating-row',
63 ids: ['impeccable-live-bar', 'impeccable-live-shader'],
64 states: ['action-label', 'animated-dots', 'generating', 'done'],
65 },
66 {
67 key: 'variant-cycling-row',
68 ids: ['impeccable-live-bar', 'impeccable-live-params-panel'],
69 states: ['variant-1', 'variant-2', 'variant-3', 'left-disabled', 'right-disabled', 'dot-click', 'accept', 'discard'],
70 },
71 {
72 key: 'variant-params-panel',
73 ids: ['impeccable-live-params-panel'],
74 states: ['closed', 'open-above', 'open-below', 'range', 'steps', 'toggle'],
75 },
76 {
77 key: 'saving-confirmed-rows',
78 ids: ['impeccable-live-bar'],
79 states: ['saving', 'applying-variant', 'confirmed'],
80 },
81 {
82 key: 'insert-mode-chrome',
83 ids: [
84 'impeccable-live-insert-line',
85 'impeccable-live-insert-placeholder',
86 'impeccable-live-placeholder-resize',
87 'impeccable-live-insert-input',
88 'impeccable-live-insert-voice',
89 'impeccable-live-insert-create',
90 'impeccable-live-insert-create-tooltip',
91 ],
92 states: ['toggle-active', 'line', 'placeholder', 'resize', 'enabled', 'disabled', 'tooltip'],
93 },
94 {
95 key: 'annotation-chrome',
96 ids: [
97 'impeccable-live-annot',
98 'impeccable-live-annot-svg',
99 'impeccable-live-annot-pins',
100 'impeccable-live-annot-clear',
101 ],
102 states: ['overlay', 'drawing', 'pin', 'pin-edit', 'clear'],
103 },
104 {
105 key: 'design-system-panel',
106 ids: ['impeccable-live-design-host'],
107 states: ['closed', 'open', 'tabs', 'token-tiles', 'copy'],
108 },
109 {
110 key: 'toasts-and-errors',
111 ids: ['impeccable-live-toast'],
112 states: ['normal', 'error', 'no-variants-mounted'],
113 },
114 {
115 key: 'css-isolation-boundary',
116 ids: ['impeccable-live-root'],
117 states: ['shadow-root', 'style-tags', 'hostile-css'],
118 },
119 ]);
120
121 export const LIVE_UI_COMPONENT_IDS = Object.freeze([
122 ...new Set(LIVE_UI_SURFACES.flatMap((surface) => surface.ids)),
123 ]);
124
125 export function resolveLiveUiRoot(env = globalThis) {
126 const doc = env?.document;
127 const explicit = env?.__IMPECCABLE_LIVE_UI_ROOT__
128 || env?.window?.__IMPECCABLE_LIVE_UI_ROOT__;
129 if (explicit && typeof explicit.appendChild === 'function') return explicit;
130 return doc?.body || null;
131 }
132
133 export function getLiveUiElementById(id, env = globalThis) {
134 const doc = env?.document;
135 const root = resolveLiveUiRoot(env);
136 if (!id) return null;
137 if (root?.getElementById) {
138 const found = root.getElementById(id);
139 if (found) return found;
140 }
141 if (root?.querySelector) {
142 const found = root.querySelector('#' + escapeCssIdent(id));
143 if (found) return found;
144 }
145 return doc?.getElementById?.(id) || null;
146 }
147
148 export function appendToLiveUiRoot(el, env = globalThis) {
149 const root = resolveLiveUiRoot(env);
150 if (!root) throw new Error('Impeccable live UI root is not available');
151 root.appendChild(el);
152 return el;
153 }
154
155 export function appendStyleToLiveUiRoot(styleEl, env = globalThis) {
156 const doc = env?.document;
157 const root = resolveLiveUiRoot(env);
158 if (root && root !== doc?.body) {
159 root.appendChild(styleEl);
160 } else {
161 (doc?.head || doc?.body || root).appendChild(styleEl);
162 }
163 return styleEl;
164 }
165
166 export function activeElementDeep(doc = globalThis.document) {
167 let active = doc?.activeElement || null;
168 while (active?.shadowRoot?.activeElement) {
169 active = active.shadowRoot.activeElement;
170 }
171 return active;
172 }
173
174 function escapeCssIdent(value) {
175 if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') {
176 return CSS.escape(String(value));
177 }
178 return String(value).replace(/([ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~])/g, '\\$1');
179 }
180
180 lines Plain Text