aegon-lv97 1.0.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.
Files changed (78) hide show
  1. package/gen-ai/build-report/chartStreamRefs.ts +227 -0
  2. package/gen-ai/build-report/coherence-check/index.vue +559 -0
  3. package/gen-ai/build-report/components/ReportChapterWorkspace.vue +4571 -0
  4. package/gen-ai/build-report/components/ReportConfigPanel.vue +727 -0
  5. package/gen-ai/build-report/components/ReportGenerateSettingsPanel.vue +2361 -0
  6. package/gen-ai/build-report/components/ReportGeneratingPanel.vue +1765 -0
  7. package/gen-ai/build-report/components/ReportPromptPanel.vue +625 -0
  8. package/gen-ai/build-report/components/ReportSourceFileSummary.vue +665 -0
  9. package/gen-ai/build-report/components/ReportSourcesPanel.vue +1327 -0
  10. package/gen-ai/build-report/components/ReportTemplateDetail.vue +191 -0
  11. package/gen-ai/build-report/components/ReportTemplateEditorFormPanel.vue +667 -0
  12. package/gen-ai/build-report/components/ReportTemplatePicker.vue +302 -0
  13. package/gen-ai/build-report/components/ReportWorkflowSidebar.vue +148 -0
  14. package/gen-ai/build-report/components/paramsSetting.vue +886 -0
  15. package/gen-ai/build-report/components/reportEdit.vue +2283 -0
  16. package/gen-ai/build-report/index.vue +856 -0
  17. package/gen-ai/build-report/report-domain.ts +1133 -0
  18. package/gen-ai/build-report/report-template-data.ts +295 -0
  19. package/gen-ai/build-report/useReportWorkflow.ts +1386 -0
  20. package/gen-ai/common/assets/NotoSans-SC.woff2 +0 -0
  21. package/gen-ai/common/assets/NotoSans-TC.woff2 +0 -0
  22. package/gen-ai/common/assets/NotoSans.woff2 +0 -0
  23. package/gen-ai/common/assets/fonts.css +278 -0
  24. package/gen-ai/common/assets/pagination.css +163 -0
  25. package/gen-ai/common/directives/btnLoading.ts +455 -0
  26. package/gen-ai/common/directives/debounce.ts +98 -0
  27. package/gen-ai/common/directives/index.ts +30 -0
  28. package/gen-ai/common/en.md +417 -0
  29. package/gen-ai/common/index.ts +36 -0
  30. package/gen-ai/common/types/component-expose.ts +9 -0
  31. package/gen-ai/common/utils/api-res-status.ts +7 -0
  32. package/gen-ai/common/utils/navigation.ts +59 -0
  33. package/gen-ai/common/utils/remote-request.ts +45 -0
  34. package/gen-ai/common/utils/scroll.ts +577 -0
  35. package/gen-ai/components/AIExport/docx-export.ts +950 -0
  36. package/gen-ai/components/AIExport/docxExport.ts +2566 -0
  37. package/gen-ai/components/AIExport/index.ts +52 -0
  38. package/gen-ai/components/AIExport/parseMarkdownHtml.ts +653 -0
  39. package/gen-ai/components/CapsuleScrollbar.vue +149 -0
  40. package/gen-ai/components/ChapterTitleScroll.vue +468 -0
  41. package/gen-ai/components/EntryHeroNavCards.vue +379 -0
  42. package/gen-ai/components/GenAiPagination.vue +52 -0
  43. package/gen-ai/components/ModelCard.vue +1114 -0
  44. package/gen-ai/components/Preview/FilePreviewPage.vue +949 -0
  45. package/gen-ai/components/Preview/PreviewCapsuleScroll.vue +205 -0
  46. package/gen-ai/components/Preview/PreviewOutlineTree.vue +148 -0
  47. package/gen-ai/components/Preview/index.ts +11 -0
  48. package/gen-ai/components/Preview/preview.ts +724 -0
  49. package/gen-ai/components/PromptField.vue +505 -0
  50. package/gen-ai/components/RecentReportsTable.vue +974 -0
  51. package/gen-ai/components/ReportCollapsibleSection.vue +172 -0
  52. package/gen-ai/components/ReportHistoryList.vue +637 -0
  53. package/gen-ai/components/ReportSectionConfirmPopover.vue +251 -0
  54. package/gen-ai/components/editor/ChapterTiptapEditor.vue +3718 -0
  55. package/gen-ai/components/editor/StructureBlockView.vue +441 -0
  56. package/gen-ai/components/editor/TiptapToolbar.vue +1036 -0
  57. package/gen-ai/components/editor/TypewriterDocEditor.vue +422 -0
  58. package/gen-ai/components/editor/_StructureOrgNode.vue +326 -0
  59. package/gen-ai/components/editor/index.ts +28 -0
  60. package/gen-ai/components/editor/parseMarkdownHtml.ts +754 -0
  61. package/gen-ai/components/editor/reviewCitationTip.ts +163 -0
  62. package/gen-ai/gen-entry/api/index.ts +32 -0
  63. package/gen-ai/gen-entry/index.vue +287 -0
  64. package/gen-ai/gen-entry/types/index.ts +4 -0
  65. package/gen-ai/i.mark +2 -0
  66. package/gen-ai/management-center/index.vue +1815 -0
  67. package/gen-ai/management-center/mockData.ts +248 -0
  68. package/gen-ai/management-center/types/index.ts +37 -0
  69. package/gen-ai/model-manager/TemplateEditorFormPanel.vue +661 -0
  70. package/gen-ai/model-manager/api/index.ts +32 -0
  71. package/gen-ai/model-manager/api/mockApi.ts +33 -0
  72. package/gen-ai/model-manager/index.vue +1583 -0
  73. package/gen-ai/model-manager/mockData.ts +177 -0
  74. package/gen-ai/model-manager/template-detail-shared.ts +154 -0
  75. package/gen-ai/model-manager/template-editor.vue +345 -0
  76. package/gen-ai/model-manager/types/index.ts +40 -0
  77. package/gen-ai/model-manager/utils.ts +25 -0
  78. package/package.json +12 -0
@@ -0,0 +1,441 @@
1
+ <template>
2
+ <NodeViewWrapper
3
+ class="report-structure-block"
4
+ :class="{
5
+ 'is-readonly': !editor?.isEditable,
6
+ 'is-selected': selected,
7
+ }"
8
+ data-type="structure-block"
9
+ @click="onBlockClick"
10
+ >
11
+ <div
12
+ v-if="selected && editor?.isEditable"
13
+ class="report-structure-block__actions"
14
+ contenteditable="false"
15
+ @mousedown.prevent
16
+ @click.stop
17
+ >
18
+ <button
19
+ type="button"
20
+ class="report-structure-block__action"
21
+ title="刪除"
22
+ @click="removeBlock"
23
+ >
24
+ <Trash2 :size="15" :stroke-width="2" />
25
+ </button>
26
+ <button
27
+ type="button"
28
+ class="report-structure-block__action"
29
+ title="撤銷"
30
+ :disabled="!editor?.can().undo()"
31
+ @click="editor?.chain().focus().undo().run()"
32
+ >
33
+ <Undo2 :size="15" :stroke-width="2" />
34
+ </button>
35
+ <button
36
+ type="button"
37
+ class="report-structure-block__action"
38
+ title="重做"
39
+ :disabled="!editor?.can().redo()"
40
+ @click="editor?.chain().focus().redo().run()"
41
+ >
42
+ <Redo2 :size="15" :stroke-width="2" />
43
+ </button>
44
+ <button
45
+ type="button"
46
+ class="report-structure-block__action"
47
+ title="重置視圖"
48
+ @click="resetView"
49
+ >
50
+ <RefreshCw :size="15" :stroke-width="2" />
51
+ </button>
52
+ </div>
53
+
54
+ <div ref="viewportRef" class="report-structure-block__viewport" contenteditable="false">
55
+ <div class="report-structure-block__stage" :style="stageStyle">
56
+ <div ref="contentRef" class="report-structure-block__content" :style="contentStyle">
57
+ <ReportStructureOrgNode
58
+ :node="tree"
59
+ :selected-id="activeNodeId"
60
+ :readonly="!editor?.isEditable"
61
+ @select="onSelectNode"
62
+ @add="onAddChild"
63
+ @rename="onRenameNode"
64
+ />
65
+ </div>
66
+ </div>
67
+
68
+ <div class="report-structure-block__zoombar" @mousedown.prevent @click.stop>
69
+ <button
70
+ type="button"
71
+ class="report-structure-block__zoom-btn"
72
+ title="全屏"
73
+ @click="toggleFullscreen"
74
+ >
75
+ <Maximize2 :size="14" :stroke-width="2" />
76
+ </button>
77
+ <button
78
+ type="button"
79
+ class="report-structure-block__zoom-btn"
80
+ title="適應視圖"
81
+ @click="fitView"
82
+ >
83
+ <Scan :size="14" :stroke-width="2" />
84
+ </button>
85
+ <span class="report-structure-block__zoom-divider" aria-hidden="true" />
86
+ <button
87
+ type="button"
88
+ class="report-structure-block__zoom-btn"
89
+ title="縮小"
90
+ @click="zoomOut"
91
+ >
92
+ <Minus :size="14" :stroke-width="2" />
93
+ </button>
94
+ <span class="report-structure-block__zoom-label">{{ zoom }}%</span>
95
+ <button type="button" class="report-structure-block__zoom-btn" title="放大" @click="zoomIn">
96
+ <Plus :size="14" :stroke-width="2" />
97
+ </button>
98
+ </div>
99
+ </div>
100
+ </NodeViewWrapper>
101
+ </template>
102
+
103
+ <script setup lang="ts">
104
+ import { NodeViewWrapper, nodeViewProps } from '@tiptap/vue-3'
105
+ import { Maximize2, Minus, Plus, Redo2, RefreshCw, Scan, Trash2, Undo2 } from 'lucide-vue-next'
106
+ import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
107
+ import type { StructureOrgNode } from './_StructureOrgNode.vue'
108
+ import ReportStructureOrgNode from './_StructureOrgNode.vue'
109
+
110
+ const props = defineProps(nodeViewProps)
111
+
112
+ const DEFAULT_TREE: StructureOrgNode = {
113
+ id: 'root',
114
+ label: '',
115
+ pct: 0,
116
+ children: [],
117
+ }
118
+
119
+ function parseTree(raw: unknown): StructureOrgNode {
120
+ if (!raw) return structuredClone(DEFAULT_TREE)
121
+ try {
122
+ const data = typeof raw === 'string' ? JSON.parse(raw) : raw
123
+ if (data && typeof data === 'object' && data.id && Array.isArray(data.children)) {
124
+ return data as StructureOrgNode
125
+ }
126
+ } catch {
127
+ /* fall through */
128
+ }
129
+ return structuredClone(DEFAULT_TREE)
130
+ }
131
+
132
+ function cloneTree(node: StructureOrgNode): StructureOrgNode {
133
+ return {
134
+ ...node,
135
+ children: node.children.map(cloneTree),
136
+ }
137
+ }
138
+
139
+ function findNode(node: StructureOrgNode, id: string): StructureOrgNode | null {
140
+ if (node.id === id) return node
141
+ for (const child of node.children) {
142
+ const found = findNode(child, id)
143
+ if (found) return found
144
+ }
145
+ return null
146
+ }
147
+
148
+ function createChildId() {
149
+ return `n-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`
150
+ }
151
+
152
+ const viewportRef = ref<HTMLElement | null>(null)
153
+ const contentRef = ref<HTMLElement | null>(null)
154
+ const activeNodeId = ref<string | null>(null)
155
+ const contentSize = ref({ w: 0, h: 0 })
156
+ const zoom = computed(() => Number(props.node.attrs.zoom) || 70)
157
+ const tree = computed(() => parseTree(props.node.attrs.tree))
158
+
159
+ /** stage 使用縮放後佔位,保證 transform 不撐出滾動條,並可水平垂直居中 */
160
+ const stageStyle = computed(() => {
161
+ const w = contentSize.value.w
162
+ const h = contentSize.value.h
163
+ if (!w || !h) return undefined
164
+ const scale = zoom.value / 100
165
+ return {
166
+ width: `${w * scale}px`,
167
+ height: `${h * scale}px`,
168
+ }
169
+ })
170
+
171
+ const contentStyle = computed(() => ({
172
+ transform: `scale(${zoom.value / 100})`,
173
+ transformOrigin: 'top left',
174
+ }))
175
+
176
+ let contentObserver: ResizeObserver | null = null
177
+
178
+ function measureContentSize() {
179
+ const el = contentRef.value
180
+ if (!el) return
181
+ // scrollWidth/Height 不受 transform 影響,即 100% 自然尺寸
182
+ contentSize.value = {
183
+ w: Math.max(el.scrollWidth, el.offsetWidth, 1),
184
+ h: Math.max(el.scrollHeight, el.offsetHeight, 1),
185
+ }
186
+ }
187
+
188
+ async function refreshContentSize() {
189
+ await nextTick()
190
+ measureContentSize()
191
+ }
192
+
193
+ watch(
194
+ () => props.selected,
195
+ (isSelected) => {
196
+ if (!isSelected) activeNodeId.value = null
197
+ }
198
+ )
199
+
200
+ watch(
201
+ () => props.node.attrs.tree,
202
+ () => {
203
+ void refreshContentSize()
204
+ },
205
+ { deep: true }
206
+ )
207
+
208
+ onMounted(() => {
209
+ void refreshContentSize()
210
+ if (typeof ResizeObserver !== 'undefined' && contentRef.value) {
211
+ contentObserver = new ResizeObserver(() => measureContentSize())
212
+ contentObserver.observe(contentRef.value)
213
+ }
214
+ })
215
+
216
+ onBeforeUnmount(() => {
217
+ contentObserver?.disconnect()
218
+ contentObserver = null
219
+ })
220
+
221
+ function updateAttrs(attrs: Record<string, unknown>) {
222
+ props.updateAttributes(attrs)
223
+ }
224
+
225
+ function onBlockClick() {
226
+ if (!props.editor?.isEditable) return
227
+ const pos = props.getPos?.()
228
+ if (typeof pos !== 'number') return
229
+ props.editor.commands.setNodeSelection(pos)
230
+ }
231
+
232
+ function onSelectNode(id: string) {
233
+ onBlockClick()
234
+ activeNodeId.value = id
235
+ }
236
+
237
+ function onAddChild(parentId: string) {
238
+ if (!props.editor?.isEditable) return
239
+ const next = cloneTree(tree.value)
240
+ const parent = findNode(next, parentId)
241
+ if (!parent) return
242
+ parent.children.push({
243
+ id: createChildId(),
244
+ label: '',
245
+ pct: 0,
246
+ children: [],
247
+ })
248
+ updateAttrs({ tree: next })
249
+ activeNodeId.value = parentId
250
+ void refreshContentSize()
251
+ }
252
+
253
+ function onRenameNode(id: string, label: string) {
254
+ if (!props.editor?.isEditable) return
255
+ const next = cloneTree(tree.value)
256
+ const target = findNode(next, id)
257
+ if (!target) return
258
+ target.label = label
259
+ updateAttrs({ tree: next })
260
+ void refreshContentSize()
261
+ }
262
+
263
+ function removeBlock() {
264
+ props.deleteNode()
265
+ }
266
+
267
+ function resetView() {
268
+ updateAttrs({ zoom: 70 })
269
+ }
270
+
271
+ /**
272
+ * 適應視圖:按視口自動縮放並水平/垂直居中顯示全部內容,不出現滾動條。
273
+ * 不改動默認 zoom=70 與其他操作邏輯。
274
+ */
275
+ async function fitView() {
276
+ const viewport = viewportRef.value
277
+ const content = contentRef.value
278
+ if (!viewport || !content) return
279
+
280
+ await refreshContentSize()
281
+ const naturalW = Math.max(contentSize.value.w, 1)
282
+ const naturalH = Math.max(contentSize.value.h, 1)
283
+
284
+ // 預留邊距與右下角縮放條空間,避免貼邊或被遮擋
285
+ const padX = 48
286
+ const padY = 64
287
+ const availW = Math.max(viewport.clientWidth - padX, 1)
288
+ const availH = Math.max(viewport.clientHeight - padY, 1)
289
+
290
+ const fitScale = Math.min(availW / naturalW, availH / naturalH, 1)
291
+ // 向下取整並留 1% 餘量,避免亞像素導致出現滾動條
292
+ let nextZoom = Math.floor(fitScale * 100) - 1
293
+ nextZoom = Math.max(10, Math.min(200, nextZoom))
294
+
295
+ updateAttrs({ zoom: nextZoom })
296
+ await nextTick()
297
+ measureContentSize()
298
+ viewport.scrollLeft = 0
299
+ viewport.scrollTop = 0
300
+ }
301
+
302
+ function zoomIn() {
303
+ updateAttrs({ zoom: Math.min(200, zoom.value + 10) })
304
+ }
305
+
306
+ function zoomOut() {
307
+ updateAttrs({ zoom: Math.max(50, zoom.value - 10) })
308
+ }
309
+
310
+ async function toggleFullscreen() {
311
+ const el = viewportRef.value
312
+ if (!el) return
313
+ if (document.fullscreenElement) {
314
+ await document.exitFullscreen()
315
+ return
316
+ }
317
+ await el.requestFullscreen?.()
318
+ }
319
+ </script>
320
+
321
+ <style scoped>
322
+ .report-structure-block {
323
+ position: relative;
324
+ margin: 16px 0 20px;
325
+ user-select: none;
326
+ }
327
+
328
+ .report-structure-block__actions {
329
+ position: absolute;
330
+ top: -44px;
331
+ left: 0;
332
+ z-index: 3;
333
+ display: inline-flex;
334
+ align-items: center;
335
+ gap: 2px;
336
+ padding: 6px 8px;
337
+ border-radius: 999px;
338
+ background: #2f2f2f;
339
+ box-shadow: 0 4px 14px rgba(0, 0, 0, 0.22);
340
+ }
341
+
342
+ .report-structure-block__action {
343
+ display: inline-flex;
344
+ align-items: center;
345
+ justify-content: center;
346
+ width: 28px;
347
+ height: 28px;
348
+ padding: 0;
349
+ border: none;
350
+ border-radius: 999px;
351
+ background: transparent;
352
+ color: #f5f5f5;
353
+ cursor: pointer;
354
+ }
355
+
356
+ .report-structure-block__action:hover:not(:disabled) {
357
+ background: rgba(255, 255, 255, 0.12);
358
+ }
359
+
360
+ .report-structure-block__action:disabled {
361
+ opacity: 0.35;
362
+ cursor: not-allowed;
363
+ }
364
+
365
+ .report-structure-block__viewport {
366
+ position: relative;
367
+ display: flex;
368
+ align-items: center;
369
+ justify-content: center;
370
+ min-height: 280px;
371
+ border: 1px solid #e5e5e5;
372
+ border-radius: 4px;
373
+ background: #fff;
374
+ overflow: auto;
375
+ transition: border-color 0.15s ease;
376
+ }
377
+
378
+ .report-structure-block.is-selected .report-structure-block__viewport {
379
+ border-color: #000;
380
+ }
381
+
382
+ .report-structure-block__stage {
383
+ position: relative;
384
+ flex-shrink: 0;
385
+ overflow: hidden;
386
+ }
387
+
388
+ .report-structure-block__content {
389
+ width: max-content;
390
+ padding: 24px 32px 40px;
391
+ }
392
+
393
+ .report-structure-block__zoombar {
394
+ position: absolute;
395
+ right: 12px;
396
+ bottom: 12px;
397
+ z-index: 4;
398
+ display: inline-flex;
399
+ align-items: center;
400
+ gap: 2px;
401
+ padding: 4px 6px;
402
+ border-radius: 8px;
403
+ background: #3a3a3a;
404
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.18);
405
+ }
406
+
407
+ .report-structure-block__zoom-btn {
408
+ display: inline-flex;
409
+ align-items: center;
410
+ justify-content: center;
411
+ width: 26px;
412
+ height: 26px;
413
+ padding: 0;
414
+ border: none;
415
+ border-radius: 6px;
416
+ background: transparent;
417
+ color: #f5f5f5;
418
+ cursor: pointer;
419
+ }
420
+
421
+ .report-structure-block__zoom-btn:hover {
422
+ background: rgba(255, 255, 255, 0.12);
423
+ }
424
+
425
+ .report-structure-block__zoom-divider {
426
+ width: 1px;
427
+ height: 14px;
428
+ margin: 0 4px;
429
+ background: rgba(255, 255, 255, 0.25);
430
+ }
431
+
432
+ .report-structure-block__zoom-label {
433
+ min-width: 42px;
434
+ padding: 0 4px;
435
+ color: #f5f5f5;
436
+ font-family: var(--gen-ai-font-family, 'Noto Sans', 'Noto Sans TC', sans-serif);
437
+ font-size: 12px;
438
+ text-align: center;
439
+ line-height: 1;
440
+ }
441
+ </style>