aegon-dangerious 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 (32) hide show
  1. package/build-report/components/AiPromptGenerateDialog.vue +349 -0
  2. package/build-report/components/CapsuleScrollbar.vue +145 -0
  3. package/build-report/components/ChapterTitleScroll.vue +292 -0
  4. package/build-report/components/PromptField.vue +314 -0
  5. package/build-report/components/RecentReportsTable.vue +408 -0
  6. package/build-report/components/ReportChapterPreview.vue +437 -0
  7. package/build-report/components/ReportChapterWorkspace.vue +923 -0
  8. package/build-report/components/ReportCollapsibleSection.vue +132 -0
  9. package/build-report/components/ReportConfigPanel.vue +609 -0
  10. package/build-report/components/ReportGenerateSettingsPanel.vue +403 -0
  11. package/build-report/components/ReportGeneratingPanel.vue +908 -0
  12. package/build-report/components/ReportHistoryList.vue +157 -0
  13. package/build-report/components/ReportHistoryPanel.vue +140 -0
  14. package/build-report/components/ReportPromptPanel.vue +445 -0
  15. package/build-report/components/ReportSectionConfirmPopover.vue +232 -0
  16. package/build-report/components/ReportSourceFileSummary.vue +544 -0
  17. package/build-report/components/ReportSourcesMoreOverlay.vue +279 -0
  18. package/build-report/components/ReportSourcesPanel.vue +1055 -0
  19. package/build-report/components/ReportTemplateDetail.vue +176 -0
  20. package/build-report/components/ReportTemplateEditorFormPanel.vue +602 -0
  21. package/build-report/components/ReportTemplatePicker.vue +802 -0
  22. package/build-report/components/ReportTemplatePromptField.vue +314 -0
  23. package/build-report/components/ReportWorkflowSidebar.vue +104 -0
  24. package/build-report/components/reportEdit.vue +334 -0
  25. package/build-report/index.vue +646 -0
  26. package/build-report/report-list.vue +407 -0
  27. package/build-report/report-template-data.ts +248 -0
  28. package/build-report/styles/report-workflow-dialog.css +33 -0
  29. package/build-report/useReportWorkflow.ts +343 -0
  30. package/build-report/utils/report-edit-route.ts +109 -0
  31. package/build-report/utils/scroll.ts +361 -0
  32. package/package.json +12 -0
@@ -0,0 +1,361 @@
1
+ import { computed, nextTick, onUnmounted, ref, watch } from 'vue'
2
+
3
+ export const CAPSULE_SCROLL_CONTAINER_CLASS = 'capsule-scroll-container'
4
+
5
+ export interface CapsuleScrollbarTheme {
6
+ thumbColor?: string
7
+ thumbHoverColor?: string
8
+ thumbActiveColor?: string
9
+ trackColor?: string
10
+ thumbWidthPx?: number
11
+ classPrefix?: string
12
+ }
13
+
14
+ export interface CapsuleScrollbarOptions extends CapsuleScrollbarTheme {
15
+ scrollEl: { value: HTMLElement | null }
16
+ anchorEl?: { value: HTMLElement | null }
17
+ active: { value: boolean }
18
+ gutterPx?: number
19
+ thumbWidthPx?: number
20
+ minThumbPx?: number
21
+ visibleRatio?: number
22
+ alwaysVisible?: boolean
23
+ autoHideAfterMs?: number
24
+ rightOffsetPx?: number
25
+ }
26
+
27
+ export interface CapsuleScrollbarBindings {
28
+ canScroll: { value: boolean }
29
+ shown: { value: boolean }
30
+ visible: { value: boolean }
31
+ rootStyle: { value: { [key: string]: string } }
32
+ thumbStyle: { value: { [key: string]: string } }
33
+ rootClass: string
34
+ thumbClass: string
35
+ onThumbMouseDown: (event: MouseEvent) => void
36
+ refresh: () => void
37
+ bind: () => void
38
+ unbind: () => void
39
+ }
40
+
41
+ const DEFAULT_CLASS_PREFIX = 'capsule-scrollbar'
42
+
43
+ interface NativeHideSnapshot {
44
+ paddingRight: string
45
+ marginRight: string
46
+ width: string
47
+ boxSizing: string
48
+ scrollbarWidth: string
49
+ msOverflowStyle: string
50
+ }
51
+
52
+ export function markCapsuleScrollContainer(el: HTMLElement): () => void {
53
+ const computed = getComputedStyle(el)
54
+ const snapshot: NativeHideSnapshot = {
55
+ paddingRight: el.style.paddingRight,
56
+ marginRight: el.style.marginRight,
57
+ width: el.style.width,
58
+ boxSizing: el.style.boxSizing,
59
+ scrollbarWidth: el.style.scrollbarWidth,
60
+ msOverflowStyle: (el.style as CSSStyleDeclaration & { msOverflowStyle?: string }).msOverflowStyle ?? '',
61
+ }
62
+
63
+ el.style.setProperty('--capsule-scroll-padding-right', computed.paddingRight)
64
+ el.classList.add(CAPSULE_SCROLL_CONTAINER_CLASS)
65
+ el.style.scrollbarWidth = 'none'
66
+ ;(el.style as CSSStyleDeclaration & { msOverflowStyle?: string }).msOverflowStyle = 'none'
67
+
68
+ return () => {
69
+ el.classList.remove(CAPSULE_SCROLL_CONTAINER_CLASS)
70
+ el.style.removeProperty('--capsule-scroll-padding-right')
71
+ el.style.paddingRight = snapshot.paddingRight
72
+ el.style.marginRight = snapshot.marginRight
73
+ el.style.width = snapshot.width
74
+ el.style.boxSizing = snapshot.boxSizing
75
+ el.style.scrollbarWidth = snapshot.scrollbarWidth
76
+ ;(el.style as CSSStyleDeclaration & { msOverflowStyle?: string }).msOverflowStyle =
77
+ snapshot.msOverflowStyle
78
+ }
79
+ }
80
+
81
+ export function useCapsuleScrollbar(options: CapsuleScrollbarOptions): CapsuleScrollbarBindings {
82
+ const thumbWidthPx = options.thumbWidthPx ?? 10
83
+ const gutterPx = options.gutterPx ?? thumbWidthPx
84
+ const minThumbPx = options.minThumbPx ?? 48
85
+ const visibleRatio = options.visibleRatio ?? 1
86
+ const rightOffsetPx = options.rightOffsetPx ?? 1
87
+ const autoHideAfterMs = options.autoHideAfterMs ?? 3000
88
+ const alwaysVisible = options.alwaysVisible ?? autoHideAfterMs <= 0
89
+ const classPrefix = options.classPrefix ?? DEFAULT_CLASS_PREFIX
90
+
91
+ const canScroll = ref(false)
92
+ const shown = ref(false)
93
+ const zoneTop = ref(0)
94
+ const zoneRight = ref(rightOffsetPx)
95
+ const zoneHeight = ref(0)
96
+ const thumbHeight = ref(minThumbPx)
97
+ const thumbOffset = ref(0)
98
+
99
+ const visible = computed(() => canScroll.value && (alwaysVisible || shown.value))
100
+
101
+ let resizeObserver: ResizeObserver | undefined
102
+ let thumbDragCleanup: (() => void) | undefined
103
+ let removeContainerMark: (() => void) | undefined
104
+ let hideTimer: number | undefined
105
+ let rafId = 0
106
+
107
+ const rootStyle = computed(() => ({
108
+ top: `${zoneTop.value + thumbOffset.value}px`,
109
+ right: `${zoneRight.value}px`,
110
+ width: `${gutterPx}px`,
111
+ height: `${Math.round(thumbHeight.value)}px`,
112
+ }))
113
+
114
+ const thumbStyle = computed(() => ({
115
+ width: `${thumbWidthPx}px`,
116
+ height: '100%',
117
+ }))
118
+
119
+ function clearHideTimer() {
120
+ if (hideTimer !== undefined) {
121
+ clearTimeout(hideTimer)
122
+ hideTimer = undefined
123
+ }
124
+ }
125
+
126
+ function scheduleHide() {
127
+ clearHideTimer()
128
+ if (alwaysVisible || autoHideAfterMs <= 0) return
129
+ hideTimer = window.setTimeout(() => {
130
+ shown.value = false
131
+ hideTimer = undefined
132
+ }, autoHideAfterMs)
133
+ }
134
+
135
+ function revealBar() {
136
+ if (!canScroll.value) return
137
+ shown.value = true
138
+ scheduleHide()
139
+ }
140
+
141
+ function refreshLayout() {
142
+ const scrollEl = options.scrollEl.value
143
+ const anchorEl = options.anchorEl?.value ?? scrollEl
144
+ if (!options.active.value || !scrollEl || !anchorEl) {
145
+ canScroll.value = false
146
+ shown.value = false
147
+ return
148
+ }
149
+
150
+ const { scrollTop, scrollHeight, clientHeight } = scrollEl
151
+ const scrollable = scrollHeight > clientHeight + 1
152
+ canScroll.value = scrollable
153
+ if (!scrollable) {
154
+ shown.value = false
155
+ return
156
+ }
157
+
158
+ const anchorRect = anchorEl.getBoundingClientRect()
159
+ const shrinkInset = (anchorRect.height * (1 - visibleRatio)) / 2
160
+
161
+ zoneTop.value = Math.round(anchorRect.top + shrinkInset)
162
+ zoneRight.value = rightOffsetPx
163
+ zoneHeight.value = Math.round(anchorRect.height * visibleRatio)
164
+
165
+ const trackH = zoneHeight.value
166
+ thumbHeight.value = Math.max(minThumbPx, (clientHeight / scrollHeight) * trackH)
167
+ const maxThumbTravel = Math.max(0, trackH - thumbHeight.value)
168
+ const scrollMax = scrollHeight - clientHeight
169
+ thumbOffset.value = scrollMax > 0 ? Math.round((scrollTop / scrollMax) * maxThumbTravel) : 0
170
+ }
171
+
172
+ function scheduleRefresh() {
173
+ cancelAnimationFrame(rafId)
174
+ rafId = requestAnimationFrame(refreshLayout)
175
+ }
176
+
177
+ function onScroll() {
178
+ revealBar()
179
+ scheduleRefresh()
180
+ }
181
+
182
+ function bind() {
183
+ unbind()
184
+ const scrollEl = options.scrollEl.value
185
+ if (!scrollEl) return
186
+
187
+ removeContainerMark = markCapsuleScrollContainer(scrollEl)
188
+ refreshLayout()
189
+ scrollEl.addEventListener('scroll', onScroll, { passive: true })
190
+ resizeObserver = new ResizeObserver(() => scheduleRefresh())
191
+ resizeObserver.observe(scrollEl)
192
+ if (options.anchorEl?.value) resizeObserver.observe(options.anchorEl.value)
193
+ window.addEventListener('resize', scheduleRefresh, { passive: true })
194
+ }
195
+
196
+ function unbind() {
197
+ cancelAnimationFrame(rafId)
198
+ clearHideTimer()
199
+ window.removeEventListener('resize', scheduleRefresh)
200
+ resizeObserver?.disconnect()
201
+ resizeObserver = undefined
202
+ thumbDragCleanup?.()
203
+ thumbDragCleanup = undefined
204
+ options.scrollEl.value?.removeEventListener('scroll', onScroll)
205
+ removeContainerMark?.()
206
+ removeContainerMark = undefined
207
+ canScroll.value = false
208
+ shown.value = false
209
+ }
210
+
211
+ function onThumbMouseDown(event: MouseEvent) {
212
+ const scrollEl = options.scrollEl.value
213
+ if (!scrollEl) return
214
+
215
+ event.preventDefault()
216
+ revealBar()
217
+ clearHideTimer()
218
+
219
+ const startY = event.clientY
220
+ const startScrollTop = scrollEl.scrollTop
221
+ const trackH = zoneHeight.value
222
+ const scrollMax = scrollEl.scrollHeight - scrollEl.clientHeight
223
+ const maxThumbTravel = Math.max(0, trackH - thumbHeight.value)
224
+
225
+ const onMove = (ev: MouseEvent) => {
226
+ if (maxThumbTravel <= 0 || scrollMax <= 0) return
227
+ scrollEl.scrollTop = startScrollTop + ((ev.clientY - startY) / maxThumbTravel) * scrollMax
228
+ }
229
+
230
+ const onUp = () => {
231
+ document.removeEventListener('mousemove', onMove)
232
+ document.removeEventListener('mouseup', onUp)
233
+ thumbDragCleanup = undefined
234
+ scheduleHide()
235
+ }
236
+
237
+ document.addEventListener('mousemove', onMove)
238
+ document.addEventListener('mouseup', onUp)
239
+ thumbDragCleanup = onUp
240
+ }
241
+
242
+ watch(
243
+ () => options.active.value,
244
+ (active) => {
245
+ if (active) {
246
+ void nextTick(() => {
247
+ bind()
248
+ void nextTick(refreshLayout)
249
+ })
250
+ return
251
+ }
252
+ unbind()
253
+ },
254
+ { immediate: true },
255
+ )
256
+
257
+ onUnmounted(unbind)
258
+
259
+ return {
260
+ canScroll,
261
+ shown,
262
+ visible,
263
+ rootStyle,
264
+ thumbStyle,
265
+ rootClass: classPrefix,
266
+ thumbClass: `${classPrefix}__thumb`,
267
+ onThumbMouseDown,
268
+ refresh: refreshLayout,
269
+ bind,
270
+ unbind,
271
+ }
272
+ }
273
+
274
+ // --- 頁面滿屏滾動鎖定 ---
275
+
276
+ /** 鎖定 document 滿屏滾動,返回 cleanup 恢復原 overflow */
277
+ export function lockDocumentScroll(): () => void {
278
+ const html = document.documentElement
279
+ const body = document.body
280
+ const prevHtml = html.style.overflow
281
+ const prevBody = body.style.overflow
282
+
283
+ html.style.overflow = 'hidden'
284
+ body.style.overflow = 'hidden'
285
+
286
+ return () => {
287
+ html.style.overflow = prevHtml
288
+ body.style.overflow = prevBody
289
+ }
290
+ }
291
+
292
+ // --- 視口主內容區高度 ---
293
+
294
+ export interface ViewportBodyHeightOptions {
295
+ /** 從視口高度中扣除的固定像素 */
296
+ subtract?: number
297
+ /** 按選擇器測量需扣除的高度(如 PageHeader) */
298
+ subtractSelector?: string
299
+ /** 測量 subtractSelector 時的父級容器,默認取 element 的 offsetParent 或 document */
300
+ subtractRoot?: HTMLElement
301
+ /** 是否監聽 resize,默認 true */
302
+ listenResize?: boolean
303
+ }
304
+
305
+ /** 當前視口高度(px) */
306
+ export function getViewportHeight(): number {
307
+ return window.innerHeight
308
+ }
309
+
310
+ function resolveSubtract(el: HTMLElement, options?: ViewportBodyHeightOptions): number {
311
+ if (options?.subtract !== undefined) return options.subtract
312
+
313
+ if (options?.subtractSelector) {
314
+ const root = options.subtractRoot ?? el.parentElement ?? document.body
315
+ const node = root.querySelector(options.subtractSelector) as HTMLElement | null
316
+ if (node) return node.getBoundingClientRect().height
317
+ }
318
+
319
+ return 0
320
+ }
321
+
322
+ /** 計算主內容區可用高度(視口高度 − 扣除項) */
323
+ export function getViewportBodyHeight(
324
+ el: HTMLElement,
325
+ options?: ViewportBodyHeightOptions,
326
+ ): number {
327
+ const subtract = resolveSubtract(el, options)
328
+ return Math.max(0, getViewportHeight() - subtract)
329
+ }
330
+
331
+ /**
332
+ * 將元素高度綁定為當前視口可用高度;返回 cleanup(移除監聽並清空 inline height)
333
+ */
334
+ export function bindViewportBodyHeight(
335
+ el: HTMLElement,
336
+ options?: ViewportBodyHeightOptions,
337
+ ): () => void {
338
+ const listenResize = options?.listenResize ?? true
339
+
340
+ const update = () => {
341
+ const height = getViewportBodyHeight(el, options)
342
+ el.style.height = `${height}px`
343
+ el.style.maxHeight = `${height}px`
344
+ }
345
+
346
+ update()
347
+
348
+ if (!listenResize) {
349
+ return () => {
350
+ el.style.height = ''
351
+ el.style.maxHeight = ''
352
+ }
353
+ }
354
+
355
+ window.addEventListener('resize', update)
356
+ return () => {
357
+ window.removeEventListener('resize', update)
358
+ el.style.height = ''
359
+ el.style.maxHeight = ''
360
+ }
361
+ }
package/package.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "aegon-dangerious",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "keywords": [],
10
+ "author": "",
11
+ "license": "ISC"
12
+ }