@ohos-ports/slides-grab 1.5.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README-ko.md +292 -0
- package/README.md +301 -0
- package/bin/ohos-postinstall.cjs +118 -0
- package/bin/ppt-agent.js +620 -0
- package/convert.cjs +20 -0
- package/native/@img/sharp-libvips-openharmony-arm64/lib/glib-2.0/include/glibconfig.h +220 -0
- package/native/@img/sharp-libvips-openharmony-arm64/lib/index.js +1 -0
- package/native/@img/sharp-libvips-openharmony-arm64/lib/libexpat.so +0 -0
- package/native/@img/sharp-libvips-openharmony-arm64/lib/libffi.so +0 -0
- package/native/@img/sharp-libvips-openharmony-arm64/lib/libjpeg.so +0 -0
- package/native/@img/sharp-libvips-openharmony-arm64/lib/libpcre2-8.so.0 +0 -0
- package/native/@img/sharp-libvips-openharmony-arm64/lib/libvips-cpp.so.42.20.3 +0 -0
- package/native/@img/sharp-libvips-openharmony-arm64/lib/libvips.so.42.20.3 +0 -0
- package/native/@img/sharp-libvips-openharmony-arm64/package.json +13 -0
- package/native/@img/sharp-openharmony-arm64/lib/sharp-openharmony-arm64.node +0 -0
- package/native/@img/sharp-openharmony-arm64/package.json +16 -0
- package/package.json +95 -0
- package/runtimes/claude-code/agents/design-critic-agent.md +23 -0
- package/runtimes/codex/agents/slides-grab-design-critic.md +22 -0
- package/scripts/build-viewer.js +444 -0
- package/scripts/design-gate.js +258 -0
- package/scripts/download-video.js +213 -0
- package/scripts/editor-server.js +1167 -0
- package/scripts/figma-export.js +169 -0
- package/scripts/generate-image.js +116 -0
- package/scripts/generate-images.js +164 -0
- package/scripts/html2pdf.js +822 -0
- package/scripts/html2png.js +246 -0
- package/scripts/html2pptx.js +162 -0
- package/scripts/import-template.js +86 -0
- package/scripts/install-runtime.js +216 -0
- package/scripts/render-tldraw.js +44 -0
- package/scripts/validate-slides.js +221 -0
- package/skills/slides-grab/SKILL.md +65 -0
- package/skills/slides-grab/references/presentation-workflow-reference.md +61 -0
- package/skills/slides-grab-card-news/SKILL.md +35 -0
- package/skills/slides-grab-design/SKILL.md +88 -0
- package/skills/slides-grab-design/references/beautiful-slide-defaults.md +88 -0
- package/skills/slides-grab-design/references/design-gate.md +349 -0
- package/skills/slides-grab-design/references/design-rules.md +76 -0
- package/skills/slides-grab-design/references/design-system-full.md +568 -0
- package/skills/slides-grab-design/references/detailed-design-rules.md +70 -0
- package/skills/slides-grab-export/SKILL.md +55 -0
- package/skills/slides-grab-export/references/export-rules.md +27 -0
- package/skills/slides-grab-export/references/html2pptx.md +627 -0
- package/skills/slides-grab-export/references/ooxml.md +427 -0
- package/skills/slides-grab-export/references/pptx-skill-reference.md +189 -0
- package/skills/slides-grab-html/SKILL.md +67 -0
- package/skills/slides-grab-image/SKILL.md +82 -0
- package/skills/slides-grab-plan/SKILL.md +68 -0
- package/skills/slides-grab-plan/references/design-md-to-slides-conversion.md +135 -0
- package/skills/slides-grab-plan/references/outline-format.md +47 -0
- package/skills/slides-grab-plan/references/plan-workflow-reference.md +140 -0
- package/src/codex-imagen.js +182 -0
- package/src/design-diversity-data.js +6932 -0
- package/src/design-gate-report.js +244 -0
- package/src/design-gate-state.js +329 -0
- package/src/design-import.js +164 -0
- package/src/design-md-parser.js +415 -0
- package/src/design-styles-data.js +1928 -0
- package/src/design-styles.js +209 -0
- package/src/editor/codex-edit.js +584 -0
- package/src/editor/edit-subprocess.js +170 -0
- package/src/editor/editor-codex-prompt.md +50 -0
- package/src/editor/editor.html +1785 -0
- package/src/editor/js/editor-bbox.js +332 -0
- package/src/editor/js/editor-chat.js +56 -0
- package/src/editor/js/editor-direct-edit.js +238 -0
- package/src/editor/js/editor-dom.js +59 -0
- package/src/editor/js/editor-init.js +405 -0
- package/src/editor/js/editor-navigation.js +54 -0
- package/src/editor/js/editor-select.js +558 -0
- package/src/editor/js/editor-send.js +175 -0
- package/src/editor/js/editor-sse.js +163 -0
- package/src/editor/js/editor-state.js +41 -0
- package/src/editor/js/editor-type.js +71 -0
- package/src/editor/js/editor-utils.js +167 -0
- package/src/editor/js/model-registry.js +37 -0
- package/src/editor/screenshot.js +82 -0
- package/src/export-resolution.cjs +68 -0
- package/src/figma.js +71 -0
- package/src/html2pptx-scale.cjs +120 -0
- package/src/html2pptx.cjs +1262 -0
- package/src/image-contract.js +329 -0
- package/src/image-native.js +468 -0
- package/src/nano-banana.js +841 -0
- package/src/ohos-browser-bridge.cjs +272 -0
- package/src/pptx-raster-export.cjs +299 -0
- package/src/resolve.js +110 -0
- package/src/slide-mode.cjs +72 -0
- package/src/template-fidelity.js +267 -0
- package/src/template-import.js +505 -0
- package/src/template-layout.js +261 -0
- package/src/template-pack.js +255 -0
- package/src/tldraw/render.js +473 -0
- package/src/validation/cli.js +120 -0
- package/src/validation/core.js +1023 -0
- package/templates/chart.html +121 -0
- package/templates/closing.html +54 -0
- package/templates/content.html +50 -0
- package/templates/contents.html +60 -0
- package/templates/cover.html +64 -0
- package/templates/custom/.gitkeep +0 -0
- package/templates/custom/README.md +7 -0
- package/templates/design-styles/README.md +20 -0
- package/templates/design-styles/preview.html +4438 -0
- package/templates/diagram-tldraw.html +56 -0
- package/templates/diagram.html +98 -0
- package/templates/quote.html +31 -0
- package/templates/section-divider.html +43 -0
- package/templates/split-layout.html +43 -0
- package/templates/statistics.html +55 -0
- package/templates/team.html +49 -0
- package/templates/timeline.html +59 -0
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
// editor-init.js — Entry point: imports, event bindings, init()
|
|
2
|
+
|
|
3
|
+
import { state, TOOL_MODE_DRAW, TOOL_MODE_SELECT, setSlideFrame } from './editor-state.js';
|
|
4
|
+
import {
|
|
5
|
+
btnPrev, btnNext, slideIframe, slideWrapper, drawLayer, promptInput, modelSelect,
|
|
6
|
+
imageProviderSelect,
|
|
7
|
+
objectLayer,
|
|
8
|
+
btnSend, btnClearBboxes, slideCounter,
|
|
9
|
+
toggleBold, toggleItalic, toggleUnderline, toggleStrike,
|
|
10
|
+
alignLeft, alignCenter, alignRight,
|
|
11
|
+
popoverTextInput, popoverApplyText, popoverTextColorInput, popoverBgColorInput,
|
|
12
|
+
popoverSizeInput, popoverApplySize, toolModeDrawBtn, toolModeSelectBtn,
|
|
13
|
+
} from './editor-dom.js';
|
|
14
|
+
import {
|
|
15
|
+
currentSlideFile, getSlideState, normalizeModelName, setStatus,
|
|
16
|
+
saveSelectedModel, loadModelOptions, clamp,
|
|
17
|
+
} from './editor-utils.js';
|
|
18
|
+
import { renderChatMessages } from './editor-chat.js';
|
|
19
|
+
import {
|
|
20
|
+
onBboxChange, renderBboxes, scaleSlide, startDrawing, moveDrawing, endDrawing,
|
|
21
|
+
clearBboxesForCurrentSlide, initBboxLayerEvents, getXPath,
|
|
22
|
+
} from './editor-bbox.js';
|
|
23
|
+
import {
|
|
24
|
+
setToolMode, updateToolModeUI, renderObjectSelection, updateObjectEditorControls,
|
|
25
|
+
getSelectedObjectElement, setSelectedObjectXPath, updateHoveredObjectFromPointer,
|
|
26
|
+
clearHoveredObject, getSelectableTargetAt, readSelectedObjectStyleState,
|
|
27
|
+
initObjectInteractionEvents, setObjectTransformCommitHandler, consumeObjectTransformClickSuppression,
|
|
28
|
+
} from './editor-select.js';
|
|
29
|
+
import {
|
|
30
|
+
mutateSelectedObject, applyTextDecorationToken,
|
|
31
|
+
scheduleDirectSave, recordDirectEditHistory, undoDirectEdit, redoDirectEdit, clearDirectEditHistory,
|
|
32
|
+
consumeInternalDocumentRestore,
|
|
33
|
+
} from './editor-direct-edit.js';
|
|
34
|
+
import { updateSendState, applyChanges } from './editor-send.js';
|
|
35
|
+
import { goToSlide } from './editor-navigation.js';
|
|
36
|
+
import { connectSSE, loadRunsInitial } from './editor-sse.js';
|
|
37
|
+
import { configureEditorClient, getEditorClient, updateEditorClientLabels } from './editor-type.js';
|
|
38
|
+
|
|
39
|
+
// Late-binding: connect bbox changes to updateSendState
|
|
40
|
+
onBboxChange(updateSendState);
|
|
41
|
+
|
|
42
|
+
// Bbox layer events
|
|
43
|
+
initBboxLayerEvents();
|
|
44
|
+
|
|
45
|
+
// Navigation
|
|
46
|
+
btnPrev.addEventListener('click', () => { void goToSlide(state.currentIndex - 1); });
|
|
47
|
+
btnNext.addEventListener('click', () => { void goToSlide(state.currentIndex + 1); });
|
|
48
|
+
|
|
49
|
+
// Tool modes
|
|
50
|
+
toolModeDrawBtn.addEventListener('click', () => {
|
|
51
|
+
setToolMode(TOOL_MODE_DRAW);
|
|
52
|
+
updateEditorClientLabels();
|
|
53
|
+
});
|
|
54
|
+
toolModeSelectBtn.addEventListener('click', () => {
|
|
55
|
+
setToolMode(TOOL_MODE_SELECT);
|
|
56
|
+
updateEditorClientLabels();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Clear bboxes
|
|
60
|
+
btnClearBboxes.addEventListener('click', clearBboxesForCurrentSlide);
|
|
61
|
+
|
|
62
|
+
// Drawing
|
|
63
|
+
drawLayer.addEventListener('mousedown', startDrawing);
|
|
64
|
+
drawLayer.addEventListener('mousemove', (event) => {
|
|
65
|
+
if (state.toolMode !== TOOL_MODE_SELECT) return;
|
|
66
|
+
updateHoveredObjectFromPointer(event.clientX, event.clientY);
|
|
67
|
+
});
|
|
68
|
+
drawLayer.addEventListener('mouseleave', clearHoveredObject);
|
|
69
|
+
drawLayer.addEventListener('click', (event) => {
|
|
70
|
+
if (state.toolMode !== TOOL_MODE_SELECT) return;
|
|
71
|
+
if (consumeObjectTransformClickSuppression()) return;
|
|
72
|
+
if (event.target && event.target.closest && event.target.closest('.object-handle')) return;
|
|
73
|
+
const target = getSelectableTargetAt(event.clientX, event.clientY);
|
|
74
|
+
if (!target) {
|
|
75
|
+
setSelectedObjectXPath('', 'No selectable object at this point.');
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const xpath = getXPath(target);
|
|
80
|
+
setSelectedObjectXPath(xpath, `Object selected on ${currentSlideFile()}.`);
|
|
81
|
+
});
|
|
82
|
+
if (objectLayer) {
|
|
83
|
+
objectLayer.addEventListener('mousemove', (event) => {
|
|
84
|
+
if (state.toolMode !== TOOL_MODE_SELECT) return;
|
|
85
|
+
updateHoveredObjectFromPointer(event.clientX, event.clientY);
|
|
86
|
+
});
|
|
87
|
+
objectLayer.addEventListener('mouseleave', clearHoveredObject);
|
|
88
|
+
objectLayer.addEventListener('click', (event) => {
|
|
89
|
+
if (state.toolMode !== TOOL_MODE_SELECT) return;
|
|
90
|
+
if (consumeObjectTransformClickSuppression()) return;
|
|
91
|
+
const target = event.target;
|
|
92
|
+
if (!(target instanceof HTMLElement)) return;
|
|
93
|
+
if (target.closest('.object-handle')) return;
|
|
94
|
+
|
|
95
|
+
const selectable = getSelectableTargetAt(event.clientX, event.clientY);
|
|
96
|
+
if (!selectable) {
|
|
97
|
+
setSelectedObjectXPath('', 'No selectable object at this point.');
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const xpath = getXPath(selectable);
|
|
102
|
+
setSelectedObjectXPath(xpath, `Object selected on ${currentSlideFile()}.`);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
window.addEventListener('mousemove', moveDrawing);
|
|
106
|
+
window.addEventListener('mouseup', endDrawing);
|
|
107
|
+
|
|
108
|
+
initObjectInteractionEvents();
|
|
109
|
+
setObjectTransformCommitHandler(({ beforeHtml } = {}) => {
|
|
110
|
+
recordDirectEditHistory(beforeHtml);
|
|
111
|
+
scheduleDirectSave(300, 'Object geometry updated and saved.');
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Send
|
|
115
|
+
btnSend.addEventListener('click', applyChanges);
|
|
116
|
+
|
|
117
|
+
// Model select
|
|
118
|
+
modelSelect.addEventListener('change', () => {
|
|
119
|
+
const nextModel = normalizeModelName(modelSelect.value);
|
|
120
|
+
if (!state.availableModels.includes(nextModel)) {
|
|
121
|
+
modelSelect.value = state.selectedModel;
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const slide = currentSlideFile();
|
|
126
|
+
if (slide) {
|
|
127
|
+
const ss = getSlideState(slide);
|
|
128
|
+
ss.model = nextModel;
|
|
129
|
+
}
|
|
130
|
+
state.selectedModel = nextModel;
|
|
131
|
+
state.defaultModel = nextModel;
|
|
132
|
+
saveSelectedModel(state.selectedModel);
|
|
133
|
+
updateSendState();
|
|
134
|
+
setStatus(`Model selected: ${state.selectedModel}`);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
imageProviderSelect?.addEventListener('change', () => {
|
|
138
|
+
state.imageProvider = imageProviderSelect.value || 'codex';
|
|
139
|
+
updateSendState();
|
|
140
|
+
setStatus(`Image provider selected: ${state.imageProvider}`);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Prompt input
|
|
144
|
+
promptInput.addEventListener('input', () => {
|
|
145
|
+
const slide = currentSlideFile();
|
|
146
|
+
if (slide) {
|
|
147
|
+
const ss = getSlideState(slide);
|
|
148
|
+
ss.prompt = promptInput.value;
|
|
149
|
+
}
|
|
150
|
+
updateSendState();
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// Text editing
|
|
154
|
+
popoverApplyText.addEventListener('click', () => {
|
|
155
|
+
if (popoverApplyText.disabled) return;
|
|
156
|
+
mutateSelectedObject((el) => {
|
|
157
|
+
const escaped = popoverTextInput.value
|
|
158
|
+
.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
159
|
+
el.innerHTML = escaped.replace(/\n/g, '<br>');
|
|
160
|
+
}, 'Object text updated and saved.', { delay: 120 });
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
popoverApplySize.addEventListener('click', () => {
|
|
164
|
+
if (popoverApplySize.disabled) return;
|
|
165
|
+
const size = clamp(Number.parseInt(popoverSizeInput.value || '24', 10) || 24, 8, 180);
|
|
166
|
+
mutateSelectedObject((el) => {
|
|
167
|
+
el.style.fontSize = `${size}px`;
|
|
168
|
+
}, 'Object font size updated and saved.');
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
popoverTextInput.addEventListener('keydown', (event) => {
|
|
172
|
+
if ((event.ctrlKey || event.metaKey) && event.key === 'Enter') {
|
|
173
|
+
event.preventDefault();
|
|
174
|
+
event.stopPropagation();
|
|
175
|
+
popoverApplyText.click();
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
popoverSizeInput.addEventListener('keydown', (event) => {
|
|
180
|
+
if (event.key === 'Enter') {
|
|
181
|
+
event.preventDefault();
|
|
182
|
+
event.stopPropagation();
|
|
183
|
+
popoverApplySize.click();
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
popoverTextColorInput.addEventListener('input', () => {
|
|
188
|
+
if (popoverTextColorInput.disabled) return;
|
|
189
|
+
mutateSelectedObject((el) => {
|
|
190
|
+
el.style.color = popoverTextColorInput.value;
|
|
191
|
+
}, 'Text color updated.', { delay: 300 });
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
popoverBgColorInput.addEventListener('input', () => {
|
|
195
|
+
if (popoverBgColorInput.disabled) return;
|
|
196
|
+
mutateSelectedObject((el) => {
|
|
197
|
+
el.style.backgroundColor = popoverBgColorInput.value;
|
|
198
|
+
}, 'Background color updated.', { delay: 300 });
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
function hasEditableFocus() {
|
|
202
|
+
const activeElement = document.activeElement;
|
|
203
|
+
if (!(activeElement instanceof HTMLElement)) return false;
|
|
204
|
+
if (activeElement.matches('input, textarea, select')) return true;
|
|
205
|
+
return activeElement.isContentEditable;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Style toggles
|
|
209
|
+
toggleBold.addEventListener('click', () => {
|
|
210
|
+
mutateSelectedObject((el) => {
|
|
211
|
+
const nextBold = !readSelectedObjectStyleState(el).bold;
|
|
212
|
+
el.style.fontWeight = nextBold ? '700' : '400';
|
|
213
|
+
}, 'Object font weight updated and saved.');
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
toggleItalic.addEventListener('click', () => {
|
|
217
|
+
mutateSelectedObject((el) => {
|
|
218
|
+
const nextItalic = !readSelectedObjectStyleState(el).italic;
|
|
219
|
+
el.style.fontStyle = nextItalic ? 'italic' : 'normal';
|
|
220
|
+
}, 'Object font style updated and saved.');
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
toggleUnderline.addEventListener('click', () => {
|
|
224
|
+
mutateSelectedObject((el) => {
|
|
225
|
+
const nextUnderline = !readSelectedObjectStyleState(el).underline;
|
|
226
|
+
applyTextDecorationToken(el, 'underline', nextUnderline);
|
|
227
|
+
}, 'Object underline updated and saved.');
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
toggleStrike.addEventListener('click', () => {
|
|
231
|
+
mutateSelectedObject((el) => {
|
|
232
|
+
const nextStrike = !readSelectedObjectStyleState(el).strike;
|
|
233
|
+
applyTextDecorationToken(el, 'line-through', nextStrike);
|
|
234
|
+
}, 'Object strikethrough updated and saved.');
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// Alignment
|
|
238
|
+
alignLeft.addEventListener('click', () => {
|
|
239
|
+
mutateSelectedObject((el) => {
|
|
240
|
+
el.style.textAlign = 'left';
|
|
241
|
+
}, 'Object alignment updated and saved.');
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
alignCenter.addEventListener('click', () => {
|
|
245
|
+
mutateSelectedObject((el) => {
|
|
246
|
+
el.style.textAlign = 'center';
|
|
247
|
+
}, 'Object alignment updated and saved.');
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
alignRight.addEventListener('click', () => {
|
|
251
|
+
mutateSelectedObject((el) => {
|
|
252
|
+
el.style.textAlign = 'right';
|
|
253
|
+
}, 'Object alignment updated and saved.');
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
// Global keyboard
|
|
257
|
+
document.addEventListener('keydown', (event) => {
|
|
258
|
+
const inEditableField = hasEditableFocus();
|
|
259
|
+
const key = event.key.toLowerCase();
|
|
260
|
+
const canHandleHistoryShortcut = !inEditableField || document.activeElement?.matches?.('input[type="color"]');
|
|
261
|
+
|
|
262
|
+
if (state.toolMode === TOOL_MODE_SELECT && (event.ctrlKey || event.metaKey) && canHandleHistoryShortcut) {
|
|
263
|
+
if (key === 'z') {
|
|
264
|
+
event.preventDefault();
|
|
265
|
+
if (event.shiftKey) {
|
|
266
|
+
redoDirectEdit();
|
|
267
|
+
} else {
|
|
268
|
+
undoDirectEdit();
|
|
269
|
+
}
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (key === 'y') {
|
|
274
|
+
event.preventDefault();
|
|
275
|
+
redoDirectEdit();
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (state.toolMode === TOOL_MODE_SELECT && (event.ctrlKey || event.metaKey) && !inEditableField) {
|
|
281
|
+
if (key === 'b') { event.preventDefault(); if (!toggleBold.disabled) toggleBold.click(); return; }
|
|
282
|
+
if (key === 'i') { event.preventDefault(); if (!toggleItalic.disabled) toggleItalic.click(); return; }
|
|
283
|
+
if (key === 'u') { event.preventDefault(); if (!toggleUnderline.disabled) toggleUnderline.click(); return; }
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if ((event.ctrlKey || event.metaKey) && event.key === 'Enter') {
|
|
287
|
+
event.preventDefault();
|
|
288
|
+
applyChanges();
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (event.key === 'Escape') {
|
|
293
|
+
if (document.activeElement) document.activeElement.blur();
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (inEditableField) return;
|
|
298
|
+
|
|
299
|
+
if (event.key === 'ArrowLeft') {
|
|
300
|
+
event.preventDefault();
|
|
301
|
+
void goToSlide(state.currentIndex - 1);
|
|
302
|
+
} else if (event.key === 'ArrowRight') {
|
|
303
|
+
event.preventDefault();
|
|
304
|
+
void goToSlide(state.currentIndex + 1);
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
// Resize
|
|
309
|
+
window.addEventListener('resize', scaleSlide);
|
|
310
|
+
|
|
311
|
+
// Iframe load
|
|
312
|
+
slideIframe.addEventListener('load', () => {
|
|
313
|
+
const slide = currentSlideFile();
|
|
314
|
+
if (slide) {
|
|
315
|
+
if (!consumeInternalDocumentRestore(slide)) {
|
|
316
|
+
clearDirectEditHistory(slide);
|
|
317
|
+
}
|
|
318
|
+
const ss = getSlideState(slide);
|
|
319
|
+
if (ss.selectedObjectXPath && !getSelectedObjectElement(slide)) {
|
|
320
|
+
ss.selectedObjectXPath = '';
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
state.hoveredObjectXPath = '';
|
|
324
|
+
renderBboxes();
|
|
325
|
+
renderObjectSelection();
|
|
326
|
+
updateObjectEditorControls();
|
|
327
|
+
updateSendState();
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
function applySlideFrameCss(width, height) {
|
|
331
|
+
if (slideWrapper) {
|
|
332
|
+
slideWrapper.style.width = `${width}px`;
|
|
333
|
+
slideWrapper.style.height = `${height}px`;
|
|
334
|
+
}
|
|
335
|
+
if (slideIframe) {
|
|
336
|
+
slideIframe.style.width = `${width}px`;
|
|
337
|
+
slideIframe.style.height = `${height}px`;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
async function loadEditorConfig() {
|
|
342
|
+
const serverAuthoredType = state.editorType;
|
|
343
|
+
configureEditorClient(serverAuthoredType);
|
|
344
|
+
let cfg = {};
|
|
345
|
+
try {
|
|
346
|
+
const res = await fetch('/api/config');
|
|
347
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
348
|
+
cfg = await res.json();
|
|
349
|
+
if (cfg?.editorType !== serverAuthoredType) {
|
|
350
|
+
throw new Error(`Editor type mismatch: page=${serverAuthoredType}, config=${cfg?.editorType || 'missing'}`);
|
|
351
|
+
}
|
|
352
|
+
} catch {
|
|
353
|
+
// Keep the server-authored editor type and default frame dimensions.
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const w = cfg?.framePx?.width;
|
|
357
|
+
const h = cfg?.framePx?.height;
|
|
358
|
+
if (w && h) {
|
|
359
|
+
setSlideFrame(w, h);
|
|
360
|
+
applySlideFrameCss(w, h);
|
|
361
|
+
}
|
|
362
|
+
if (cfg?.slideMode) {
|
|
363
|
+
document.body.dataset.slideMode = cfg.slideMode;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Init
|
|
368
|
+
async function init() {
|
|
369
|
+
setStatus('Loading slide list...');
|
|
370
|
+
|
|
371
|
+
await loadEditorConfig();
|
|
372
|
+
|
|
373
|
+
try {
|
|
374
|
+
const res = await fetch('/api/slides');
|
|
375
|
+
if (!res.ok) {
|
|
376
|
+
throw new Error(`Failed to fetch slide list: ${res.status}`);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
state.slides = await res.json();
|
|
380
|
+
|
|
381
|
+
if (state.slides.length === 0) {
|
|
382
|
+
setStatus('No slides found.');
|
|
383
|
+
slideCounter.textContent = '0 / 0';
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const editorClient = getEditorClient();
|
|
388
|
+
if (editorClient.usesModel) await loadModelOptions();
|
|
389
|
+
updateToolModeUI();
|
|
390
|
+
updateEditorClientLabels();
|
|
391
|
+
await goToSlide(0);
|
|
392
|
+
scaleSlide();
|
|
393
|
+
await loadRunsInitial();
|
|
394
|
+
connectSSE();
|
|
395
|
+
|
|
396
|
+
setStatus(editorClient.usesModel
|
|
397
|
+
? `Ready. Model: ${state.selectedModel}. Draw red pending bboxes, run Codex, then review green bboxes.`
|
|
398
|
+
: `Ready. Image provider: ${state.imageProvider}. Draw red pending bboxes, then regenerate the image.`);
|
|
399
|
+
} catch (error) {
|
|
400
|
+
setStatus(`Error loading slides: ${error.message}`);
|
|
401
|
+
console.error('Init error:', error);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
init();
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// editor-navigation.js — Slide navigation (goToSlide, persistDraft)
|
|
2
|
+
|
|
3
|
+
import { state } from './editor-state.js';
|
|
4
|
+
import {
|
|
5
|
+
slideIframe, slideCounter, btnPrev, btnNext, sessionFileChip,
|
|
6
|
+
promptInput, modelSelect,
|
|
7
|
+
} from './editor-dom.js';
|
|
8
|
+
import { currentSlideFile, getSlideState, normalizeModelName, setStatus } from './editor-utils.js';
|
|
9
|
+
import { renderChatMessages } from './editor-chat.js';
|
|
10
|
+
import { renderBboxes, scaleSlide } from './editor-bbox.js';
|
|
11
|
+
import { renderObjectSelection, updateObjectEditorControls } from './editor-select.js';
|
|
12
|
+
import { flushDirectSaveForSlide } from './editor-direct-edit.js';
|
|
13
|
+
import { updateSendState } from './editor-send.js';
|
|
14
|
+
|
|
15
|
+
export function persistCurrentSlideDraft() {
|
|
16
|
+
const slide = currentSlideFile();
|
|
17
|
+
if (!slide) return;
|
|
18
|
+
const ss = getSlideState(slide);
|
|
19
|
+
ss.prompt = promptInput.value;
|
|
20
|
+
ss.model = normalizeModelName(modelSelect.value) || ss.model || state.defaultModel;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function goToSlide(index) {
|
|
24
|
+
if (index < 0 || index >= state.slides.length) return;
|
|
25
|
+
|
|
26
|
+
const previousSlide = currentSlideFile();
|
|
27
|
+
persistCurrentSlideDraft();
|
|
28
|
+
if (previousSlide) {
|
|
29
|
+
await flushDirectSaveForSlide(previousSlide);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
state.currentIndex = index;
|
|
33
|
+
const slide = currentSlideFile();
|
|
34
|
+
slideIframe.src = `/slides/${slide}`;
|
|
35
|
+
if (sessionFileChip) sessionFileChip.textContent = slide;
|
|
36
|
+
slideCounter.textContent = `${state.currentIndex + 1} / ${state.slides.length}`;
|
|
37
|
+
btnPrev.disabled = state.currentIndex === 0;
|
|
38
|
+
btnNext.disabled = state.currentIndex === state.slides.length - 1;
|
|
39
|
+
|
|
40
|
+
const ss = getSlideState(slide);
|
|
41
|
+
if (!state.availableModels.includes(normalizeModelName(ss.model))) {
|
|
42
|
+
ss.model = state.defaultModel;
|
|
43
|
+
}
|
|
44
|
+
state.selectedModel = ss.model;
|
|
45
|
+
modelSelect.value = state.selectedModel;
|
|
46
|
+
promptInput.value = ss.prompt || '';
|
|
47
|
+
state.hoveredObjectXPath = '';
|
|
48
|
+
renderChatMessages();
|
|
49
|
+
renderBboxes();
|
|
50
|
+
renderObjectSelection();
|
|
51
|
+
updateObjectEditorControls();
|
|
52
|
+
updateSendState();
|
|
53
|
+
setStatus(`Loaded ${slide}`);
|
|
54
|
+
}
|