diff --git a/README.md b/README.md index 83224c0..7d4477c 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,22 @@ The first screen is a finished Web workspace, not an empty editor. The Agent pre The Web editor and PPTX exporter consume the same semantic `DeckSpec`. Clicking **Export PPT** creates native text, images, shapes, lines, and charts; the Agent generation step never claims that a PPTX already exists. +### Multi-format delivery & degrade strategies + +CreatPPT supports 4 delivery formats: `web`, `pptx`, `pdf`, and `png`. Every element type defines its full Web capability and explicit PPTX degrade strategy: + +| Element Type | Web Capability | PPTX Degrade Strategy | PDF | PNG | +| --- | --- | --- | --- | --- | +| `text` | Full Rich Editing & Styling | Native PPTX Text Shape | ✅ | ✅ | +| `image` | Object Fit & Free Transformations | Native PPTX Picture Part | ✅ | ✅ | +| `rect` / `ellipse` / `line` / `arrow` | Vector Shapes & Borders | Native PPTX Shape | ✅ | ✅ | +| `table` | Cell Editing, Formula Calculations & Merging | Native PPTX Table (Evaluated Values) | ✅ | ✅ | +| `chart` | Interactive Tooltips (Bar/Pie/Line/Scatter/Area) | Native PPTX Chart Object | ✅ | ✅ | +| `form` | HTML Controls & Data Submission | Static Text Label + `[___]` Placeholder | ✅ | ✅ | +| `embed` | Sandboxed Iframe & Fallback | Fallback Image / Hyperlink Text | ✅ | ✅ | +| `animation` | IntersectionObserver + CSS Animations | Injected `` OpenXML Nodes | Static | Static | +| `action` | Slide Jump & URL Navigation | Native PPTX Shape/Text Hyperlink | ✅ | Static | + ### Built-in internationalization The editor supports English and Simplified Chinese. On first launch it follows `navigator.languages` / `navigator.language`, then remembers a manual choice in local storage and synchronizes ``. diff --git a/README.zh-CN.md b/README.zh-CN.md index caf0954..30a7e43 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -90,6 +90,22 @@ 网页编辑器和 PPTX 导出器共享同一份语义 `DeckSpec`。用户点击“导出 PPT”后,文字、图片、形状、线条和图表才会生成原生 PPTX;Agent 生成阶段不会声称已经有 PPTX。 +### 多格式交付与降级策略对比表 + +CreatPPT 支持 `web`、`pptx`、`pdf` 和 `png` 4 种交付格式。每种元素类型均有完整的 Web 表达能力与明确的 PPTX 降级策略: + +| 元素类型 | Web 表达能力 | PPTX 降级策略 | PDF | PNG | +| --- | --- | --- | --- | --- | +| `text` | 完整富文本编辑与样式 | 原生 PPTX Text 文本框 | ✅ | ✅ | +| `image` | 自由变换与 Object Fit 适应 | 原生 PPTX Picture 图片对象 | ✅ | ✅ | +| `rect` / `ellipse` / `line` / `arrow` | 矢量形状与边框 | 原生 PPTX 矢量 Shape | ✅ | ✅ | +| `table` | 单元格编辑、公式联动计算与合并 | 原生 PPTX Table (导出计算后的数值) | ✅ | ✅ | +| `chart` | 交互 Tooltip (柱/饼/折线/散点/面积) | 原生 PPTX Chart 图表对象 | ✅ | ✅ | +| `form` | HTML 表单控件与数据提交 | 静态文本标签 + `[___]` 占位 | ✅ | ✅ | +| `embed` | 沙盒 iframe 嵌入与 Fallback | 降级图片 / 超链接文本 | ✅ | ✅ | +| `animation` | IntersectionObserver + CSS 动画 | 注入 OpenXML `` 节点 | 静态 | 静态 | +| `action` | 页面跳转与 URL 路由导航 | 原生 PPTX Shape/Text Hyperlink 超链接 | ✅ | 静态 | + ### 完整国际化 编辑器支持 English 和简体中文。首次打开时读取 `navigator.languages` / `navigator.language`,用户手动选择后写入本地存储,并同步 ``。 diff --git a/package-lock.json b/package-lock.json index 81a5997..d35935e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,6 +36,18 @@ }, "engines": { "node": ">=20" + }, + "peerDependencies": { + "@deepseek-ai/cordis": ">=4.0.0", + "@deepseek-ai/dsh-tools": ">=0.0.1-rc.1" + }, + "peerDependenciesMeta": { + "@deepseek-ai/cordis": { + "optional": true + }, + "@deepseek-ai/dsh-tools": { + "optional": true + } } }, "node_modules/@babel/helper-string-parser": { diff --git a/src/components/slides/EditableChart.vue b/src/components/slides/EditableChart.vue new file mode 100644 index 0000000..6bc1ce6 --- /dev/null +++ b/src/components/slides/EditableChart.vue @@ -0,0 +1,286 @@ + + + + + diff --git a/src/components/slides/EditableForm.vue b/src/components/slides/EditableForm.vue new file mode 100644 index 0000000..60c7ac0 --- /dev/null +++ b/src/components/slides/EditableForm.vue @@ -0,0 +1,203 @@ + + + + + diff --git a/src/components/slides/EditableTable.vue b/src/components/slides/EditableTable.vue new file mode 100644 index 0000000..22f195d --- /dev/null +++ b/src/components/slides/EditableTable.vue @@ -0,0 +1,171 @@ + + + + + diff --git a/src/components/slides/EmbedView.vue b/src/components/slides/EmbedView.vue new file mode 100644 index 0000000..b378518 --- /dev/null +++ b/src/components/slides/EmbedView.vue @@ -0,0 +1,90 @@ + + + + + diff --git a/src/components/slides/SlideElementView.vue b/src/components/slides/SlideElementView.vue index 7dbad26..602df8b 100644 --- a/src/components/slides/SlideElementView.vue +++ b/src/components/slides/SlideElementView.vue @@ -7,6 +7,10 @@ import type { SlideElement } from '@/domain/types' import { useEditorState } from '@/editor/state' import { elementTypeLabel, useI18n } from '@/i18n' import { resolveAssetUrl } from '@/utils/assets' +import EditableTable from './EditableTable.vue' +import EditableChart from './EditableChart.vue' +import EditableForm from './EditableForm.vue' +import EmbedView from './EmbedView.vue' const props = withDefaults(defineProps<{ element: SlideElement @@ -70,7 +74,22 @@ function elementLabel(): string { return elementTypeLabel(props.element.type) } +function handleActionClick(event: MouseEvent) { + if (props.element.action) { + const { type, target } = props.element.action + if (type === 'slideJump') { + const slide = editor.deck.value.slides.find(s => s.id === String(target)) || editor.deck.value.slides[Number(target) - 1] + if (slide) editor.selectSlide(slide.id) + } else if (type === 'url' || type === 'hyperlink') { + if (target) window.open(String(target), '_blank') + } + } +} + function select(event?: Event) { + if (props.element.action && !props.editable) { + handleActionClick(event as MouseEvent) + } if (!props.editable) return event?.stopPropagation() const pointer = event as PointerEvent | MouseEvent | undefined @@ -383,6 +402,10 @@ function handleKeydown(event: KeyboardEvent) { @keydown="handleKeydown" >{{ element.text }} + + + +