@tienne/gestalt 0.102.0 → 0.103.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/README.ko.md +2 -1
- package/README.md +2 -1
- package/dist/package.json +1 -1
- package/dist/plugin/role-agents/video-summarizer/AGENT.md +177 -113
- package/dist/plugin/skills/_shared/proactive-routing.md +2 -1
- package/dist/plugin/skills/research/references/design.md +1 -1
- package/dist/plugin/skills/video-digest/SKILL.md +408 -0
- package/dist/plugin/skills/video-digest/templates/background.md +15 -0
- package/dist/plugin/skills/video-digest/templates/blog.md +16 -0
- package/dist/plugin/skills/video-digest/templates/comments.md +19 -0
- package/dist/plugin/skills/video-digest/templates/counterpoints.md +15 -0
- package/dist/plugin/skills/video-digest/templates/flashcards.md +15 -0
- package/dist/plugin/skills/video-digest/templates/interview.md +15 -0
- package/dist/plugin/skills/video-digest/templates/meeting.md +15 -0
- package/dist/plugin/skills/video-digest/templates/mindmap.md +16 -0
- package/dist/plugin/skills/video-digest/templates/quiz.md +16 -0
- package/dist/plugin/skills/video-digest/templates/quotes.md +16 -0
- package/dist/plugin/skills/video-digest/templates/resources.md +14 -0
- package/dist/plugin/skills/video-digest/templates/timestamps.md +14 -0
- package/dist/src/cli/commands/digest.d.ts +57 -0
- package/dist/src/cli/commands/digest.d.ts.map +1 -0
- package/dist/src/cli/commands/digest.js +368 -0
- package/dist/src/cli/commands/digest.js.map +1 -0
- package/dist/src/cli/index.d.ts.map +1 -1
- package/dist/src/cli/index.js +77 -0
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/digest/blocks.d.ts +1853 -0
- package/dist/src/digest/blocks.d.ts.map +1 -0
- package/dist/src/digest/blocks.js +148 -0
- package/dist/src/digest/blocks.js.map +1 -0
- package/dist/src/digest/captions.d.ts +37 -0
- package/dist/src/digest/captions.d.ts.map +1 -0
- package/dist/src/digest/captions.js +138 -0
- package/dist/src/digest/captions.js.map +1 -0
- package/dist/src/digest/comments.d.ts +113 -0
- package/dist/src/digest/comments.d.ts.map +1 -0
- package/dist/src/digest/comments.js +153 -0
- package/dist/src/digest/comments.js.map +1 -0
- package/dist/src/digest/format.d.ts +9 -0
- package/dist/src/digest/format.d.ts.map +1 -0
- package/dist/src/digest/format.js +26 -0
- package/dist/src/digest/format.js.map +1 -0
- package/dist/src/digest/frames.d.ts +26 -0
- package/dist/src/digest/frames.d.ts.map +1 -0
- package/dist/src/digest/frames.js +109 -0
- package/dist/src/digest/frames.js.map +1 -0
- package/dist/src/digest/index.d.ts +25 -0
- package/dist/src/digest/index.d.ts.map +1 -0
- package/dist/src/digest/index.js +25 -0
- package/dist/src/digest/index.js.map +1 -0
- package/dist/src/digest/ingest.d.ts +65 -0
- package/dist/src/digest/ingest.d.ts.map +1 -0
- package/dist/src/digest/ingest.js +303 -0
- package/dist/src/digest/ingest.js.map +1 -0
- package/dist/src/digest/lint-extension.d.ts +57 -0
- package/dist/src/digest/lint-extension.d.ts.map +1 -0
- package/dist/src/digest/lint-extension.js +395 -0
- package/dist/src/digest/lint-extension.js.map +1 -0
- package/dist/src/digest/lint.d.ts +40 -0
- package/dist/src/digest/lint.d.ts.map +1 -0
- package/dist/src/digest/lint.js +517 -0
- package/dist/src/digest/lint.js.map +1 -0
- package/dist/src/digest/merge.d.ts +40 -0
- package/dist/src/digest/merge.d.ts.map +1 -0
- package/dist/src/digest/merge.js +104 -0
- package/dist/src/digest/merge.js.map +1 -0
- package/dist/src/digest/ocr.d.ts +96 -0
- package/dist/src/digest/ocr.d.ts.map +1 -0
- package/dist/src/digest/ocr.js +168 -0
- package/dist/src/digest/ocr.js.map +1 -0
- package/dist/src/digest/prose.d.ts +43 -0
- package/dist/src/digest/prose.d.ts.map +1 -0
- package/dist/src/digest/prose.js +112 -0
- package/dist/src/digest/prose.js.map +1 -0
- package/dist/src/digest/render-blocks.d.ts +32 -0
- package/dist/src/digest/render-blocks.d.ts.map +1 -0
- package/dist/src/digest/render-blocks.js +292 -0
- package/dist/src/digest/render-blocks.js.map +1 -0
- package/dist/src/digest/render-html.d.ts +22 -0
- package/dist/src/digest/render-html.d.ts.map +1 -0
- package/dist/src/digest/render-html.js +466 -0
- package/dist/src/digest/render-html.js.map +1 -0
- package/dist/src/digest/render-md.d.ts +18 -0
- package/dist/src/digest/render-md.d.ts.map +1 -0
- package/dist/src/digest/render-md.js +113 -0
- package/dist/src/digest/render-md.js.map +1 -0
- package/dist/src/digest/render.d.ts +47 -0
- package/dist/src/digest/render.d.ts.map +1 -0
- package/dist/src/digest/render.js +123 -0
- package/dist/src/digest/render.js.map +1 -0
- package/dist/src/digest/reports.d.ts +26 -0
- package/dist/src/digest/reports.d.ts.map +1 -0
- package/dist/src/digest/reports.js +70 -0
- package/dist/src/digest/reports.js.map +1 -0
- package/dist/src/digest/schema-base.d.ts +31 -0
- package/dist/src/digest/schema-base.d.ts.map +1 -0
- package/dist/src/digest/schema-base.js +19 -0
- package/dist/src/digest/schema-base.js.map +1 -0
- package/dist/src/digest/schema.d.ts +5713 -0
- package/dist/src/digest/schema.d.ts.map +1 -0
- package/dist/src/digest/schema.js +229 -0
- package/dist/src/digest/schema.js.map +1 -0
- package/dist/src/digest/server.d.ts +36 -0
- package/dist/src/digest/server.d.ts.map +1 -0
- package/dist/src/digest/server.js +278 -0
- package/dist/src/digest/server.js.map +1 -0
- package/dist/src/digest/slice.d.ts +20 -0
- package/dist/src/digest/slice.d.ts.map +1 -0
- package/dist/src/digest/slice.js +48 -0
- package/dist/src/digest/slice.js.map +1 -0
- package/dist/src/digest/store.d.ts +67 -0
- package/dist/src/digest/store.d.ts.map +1 -0
- package/dist/src/digest/store.js +152 -0
- package/dist/src/digest/store.js.map +1 -0
- package/dist/src/digest/svg.d.ts +44 -0
- package/dist/src/digest/svg.d.ts.map +1 -0
- package/dist/src/digest/svg.js +264 -0
- package/dist/src/digest/svg.js.map +1 -0
- package/dist/src/digest/templates.d.ts +38 -0
- package/dist/src/digest/templates.d.ts.map +1 -0
- package/dist/src/digest/templates.js +125 -0
- package/dist/src/digest/templates.js.map +1 -0
- package/dist/src/digest/translate.d.ts +46 -0
- package/dist/src/digest/translate.d.ts.map +1 -0
- package/dist/src/digest/translate.js +50 -0
- package/dist/src/digest/translate.js.map +1 -0
- package/dist/src/digest/viewer-js.d.ts +4 -0
- package/dist/src/digest/viewer-js.d.ts.map +1 -0
- package/dist/src/digest/viewer-js.js +710 -0
- package/dist/src/digest/viewer-js.js.map +1 -0
- package/dist/src/digest/viewer-logic.d.ts +46 -0
- package/dist/src/digest/viewer-logic.d.ts.map +1 -0
- package/dist/src/digest/viewer-logic.js +89 -0
- package/dist/src/digest/viewer-logic.js.map +1 -0
- package/dist/src/digest/ytdlp.d.ts +34 -0
- package/dist/src/digest/ytdlp.d.ts.map +1 -0
- package/dist/src/digest/ytdlp.js +64 -0
- package/dist/src/digest/ytdlp.js.map +1 -0
- package/dist/src/mcp/schemas.d.ts +48 -0
- package/dist/src/mcp/schemas.d.ts.map +1 -1
- package/dist/src/mcp/schemas.js +25 -0
- package/dist/src/mcp/schemas.js.map +1 -1
- package/dist/src/mcp/server.d.ts.map +1 -1
- package/dist/src/mcp/server.js +7 -1
- package/dist/src/mcp/server.js.map +1 -1
- package/dist/src/mcp/tools/digest-passthrough.d.ts +3 -0
- package/dist/src/mcp/tools/digest-passthrough.d.ts.map +1 -0
- package/dist/src/mcp/tools/digest-passthrough.js +148 -0
- package/dist/src/mcp/tools/digest-passthrough.js.map +1 -0
- package/package.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/.mcp.json +1 -1
- package/plugin/mcp.json +1 -1
- package/plugin/role-agents/video-summarizer/AGENT.md +177 -113
- package/plugin/skills/_shared/proactive-routing.md +2 -1
- package/plugin/skills/research/references/design.md +1 -1
- package/plugin/skills/video-digest/SKILL.md +408 -0
- package/plugin/skills/video-digest/templates/background.md +15 -0
- package/plugin/skills/video-digest/templates/blog.md +16 -0
- package/plugin/skills/video-digest/templates/comments.md +19 -0
- package/plugin/skills/video-digest/templates/counterpoints.md +15 -0
- package/plugin/skills/video-digest/templates/flashcards.md +15 -0
- package/plugin/skills/video-digest/templates/interview.md +15 -0
- package/plugin/skills/video-digest/templates/meeting.md +15 -0
- package/plugin/skills/video-digest/templates/mindmap.md +16 -0
- package/plugin/skills/video-digest/templates/quiz.md +16 -0
- package/plugin/skills/video-digest/templates/quotes.md +16 -0
- package/plugin/skills/video-digest/templates/resources.md +14 -0
- package/plugin/skills/video-digest/templates/timestamps.md +14 -0
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
import { extensionKey } from './blocks.js';
|
|
2
|
+
import { CAPTION_LABEL, formatTime, stripNumbering, watchUrlAt } from './format.js';
|
|
3
|
+
import { frameFileName } from './frames.js';
|
|
4
|
+
import { extensionHtml, extensionPanelId, sectionDiagramHtml, } from './render-blocks.js';
|
|
5
|
+
import { renderDigestMarkdown, renderExtensionMarkdown } from './render-md.js';
|
|
6
|
+
import { DIFFICULTY_LABEL, isPreset, LENGTH_LABEL, reportKey, reportLabel } from './reports.js';
|
|
7
|
+
import { VIEWER_JS } from './viewer-js.js';
|
|
8
|
+
import { boldPattern, keywordPattern, REPORT_DIFFICULTIES, REPORT_LENGTHS, youtubeWatchUrl, } from './schema.js';
|
|
9
|
+
const MAX_IMAGE_BYTES = 5 * 1024 * 1024;
|
|
10
|
+
const MAX_TOTAL_IMAGE_BYTES = 15 * 1024 * 1024;
|
|
11
|
+
const e = (s) => s
|
|
12
|
+
.replace(/&/g, '&')
|
|
13
|
+
.replace(/</g, '<')
|
|
14
|
+
.replace(/>/g, '>')
|
|
15
|
+
.replace(/"/g, '"')
|
|
16
|
+
.replace(/'/g, ''');
|
|
17
|
+
/** research 렌더러의 예산과 같다. 한 장 5MB, 합계 15MB를 넘으면 그 프레임은 뺀다 */
|
|
18
|
+
class FrameBudget {
|
|
19
|
+
warnings;
|
|
20
|
+
total = 0;
|
|
21
|
+
constructor(warnings) {
|
|
22
|
+
this.warnings = warnings;
|
|
23
|
+
}
|
|
24
|
+
dataUri(data, fileName) {
|
|
25
|
+
if (data.length > MAX_IMAGE_BYTES) {
|
|
26
|
+
this.warnings.push(`프레임이 5MB를 넘어 건너뛰었다: ${fileName}`);
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
if (!this.charge(data.length, fileName))
|
|
30
|
+
return null;
|
|
31
|
+
return `data:image/jpeg;base64,${data.toString('base64')}`;
|
|
32
|
+
}
|
|
33
|
+
/** 이미 만든 data URI를 다시 실을 때도 HTML 크기는 그만큼 늘어서 같은 합계에 센다 */
|
|
34
|
+
charge(bytes, fileName) {
|
|
35
|
+
if (this.total + bytes > MAX_TOTAL_IMAGE_BYTES) {
|
|
36
|
+
this.warnings.push(`프레임 합계가 15MB를 넘어 건너뛰었다: ${fileName}`);
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
this.total += bytes;
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// 본문에 `</script>`가 들어 있어도 태그를 닫지 못하게 꺾쇠를 유니코드 이스케이프로 바꾼다
|
|
44
|
+
export function embedJson(value) {
|
|
45
|
+
return JSON.stringify(value)
|
|
46
|
+
.replace(/</g, '\\u003c')
|
|
47
|
+
.replace(/\u2028/g, '\\u2028')
|
|
48
|
+
.replace(/\u2029/g, '\\u2029');
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* digest.json을 뷰어 HTML 한 장으로 바꾼다. LLM을 부르지 않는다.
|
|
52
|
+
* 본문 글자는 escape한 뒤 굵게와 키워드 마커 두 가지만 태그로 바꾼다.
|
|
53
|
+
*/
|
|
54
|
+
export function renderDigestHtml(digest, transcript, opts = {}) {
|
|
55
|
+
const warnings = [];
|
|
56
|
+
const { meta, segments } = transcript;
|
|
57
|
+
const long = meta.durationSec >= 3600;
|
|
58
|
+
const isYoutube = meta.source === 'youtube' && Boolean(meta.videoId);
|
|
59
|
+
const at = (i) => segments[i]?.startSec ?? 0;
|
|
60
|
+
const loadFrame = opts.inlineFrames ? opts.loadFrame : undefined;
|
|
61
|
+
const budget = loadFrame ? new FrameBudget(warnings) : null;
|
|
62
|
+
const inline = (text, keywordIds) => e(text)
|
|
63
|
+
.replace(boldPattern(), (_m, inner) => `<strong>${inner}</strong>`)
|
|
64
|
+
.replace(keywordPattern(), (_m, id, label) => {
|
|
65
|
+
if (!keywordIds.has(id)) {
|
|
66
|
+
warnings.push(`키워드 목록에 없는 마커: ${id}`);
|
|
67
|
+
return label;
|
|
68
|
+
}
|
|
69
|
+
return `<button type="button" class="kw" data-kw="${id}">${label}</button>`;
|
|
70
|
+
});
|
|
71
|
+
// 스크립트 줄마다 Tab이 멈추면 긴 영상에서 정지점이 수천 개라 줄 링크는 Tab 순서에서 뺀다
|
|
72
|
+
const timeLink = (sec, tabbable = true) => {
|
|
73
|
+
const label = formatTime(sec, long);
|
|
74
|
+
const url = watchUrlAt(meta, sec);
|
|
75
|
+
if (!url)
|
|
76
|
+
return `<span class="ts" data-sec="${sec}">${label}</span>`;
|
|
77
|
+
const tab = tabbable ? '' : ' tabindex="-1"';
|
|
78
|
+
return `<a class="ts" href="${e(url)}" target="_blank" rel="noopener" data-sec="${sec}"${tab}>${label}</a>`;
|
|
79
|
+
};
|
|
80
|
+
const cite = (c) => `<sup><button type="button" class="cite" data-key="${c.key}" data-from="${c.from}" data-to="${c.to}" aria-label="출처 ${c.key + 1}">[${c.key + 1}]</button></sup>`;
|
|
81
|
+
const sec = (c) => segments[c.key]?.startSec ?? 0;
|
|
82
|
+
const blockCtx = { escape: e, cite, time: (c) => timeLink(sec(c)), sec };
|
|
83
|
+
const missingFrame = (subId, file) => warnings.push(`프레임이 없어 자리를 비웠다: ${subId} (${file})`);
|
|
84
|
+
// 확인 수단이 없는 렌더에서는 프레임이 없다고 경고하지 않는다. 예산 초과는 FrameBudget이 따로 경고한다
|
|
85
|
+
// 리포트 여럿이 같은 프레임을 쓰면 파일은 한 번만 읽는다. 같은 data URI가 HTML에 한 번 더 실리므로 예산은 다시 센다
|
|
86
|
+
const inlined = new Map();
|
|
87
|
+
const frame = (sec, subId) => {
|
|
88
|
+
const file = frameFileName(sec);
|
|
89
|
+
let src = null;
|
|
90
|
+
if (budget && inlined.has(file)) {
|
|
91
|
+
const hit = inlined.get(file);
|
|
92
|
+
if (hit && budget.charge(hit.bytes, file))
|
|
93
|
+
src = hit.src;
|
|
94
|
+
}
|
|
95
|
+
else if (budget && loadFrame) {
|
|
96
|
+
const data = loadFrame(file);
|
|
97
|
+
if (data)
|
|
98
|
+
src = budget.dataUri(data, file);
|
|
99
|
+
else
|
|
100
|
+
missingFrame(subId, file);
|
|
101
|
+
inlined.set(file, src && data ? { src, bytes: data.length } : null);
|
|
102
|
+
}
|
|
103
|
+
else if (opts.frameExists) {
|
|
104
|
+
if (opts.frameExists(file))
|
|
105
|
+
src = `frames/${file}`;
|
|
106
|
+
else
|
|
107
|
+
missingFrame(subId, file);
|
|
108
|
+
}
|
|
109
|
+
if (!src)
|
|
110
|
+
return '';
|
|
111
|
+
const url = watchUrlAt(meta, sec);
|
|
112
|
+
const img = `<img src="${e(src)}" alt="${formatTime(sec, long)} 장면" loading="lazy">`;
|
|
113
|
+
const body = url
|
|
114
|
+
? `<a class="frame-link" href="${e(url)}" target="_blank" rel="noopener" data-sec="${sec}">${img}</a>`
|
|
115
|
+
: `<span class="frame-link" data-sec="${sec}">${img}</span>`;
|
|
116
|
+
return `<figure class="frame">${body}<figcaption>${timeLink(sec)}</figcaption></figure>`;
|
|
117
|
+
};
|
|
118
|
+
const reportsMeta = digest.reports.map((r) => ({ report: r, key: reportKey(r.options) }));
|
|
119
|
+
const base = digest.reports[0].options;
|
|
120
|
+
// 첫 리포트는 1차와 같은 id를 써서 이미 공유된 앵커가 그대로 열린다. 나머지는 id에 못 쓰는 ~로 접두를 붙여 겹치지 않게 한다
|
|
121
|
+
const renderReport = (report, ri, key) => {
|
|
122
|
+
const { lead, sections, skipped, keywords } = report;
|
|
123
|
+
const kwIds = new Set(keywords.map((k) => k.id));
|
|
124
|
+
const pfx = ri === 0 ? '' : `r${ri}~`;
|
|
125
|
+
const hidden = ri === 0 ? '' : ' hidden';
|
|
126
|
+
const bullets = (list) => `<ul>${list
|
|
127
|
+
.map((b) => `<li>${inline(b.text, kwIds)}${cite(b.cite)}${b.children?.length ? bullets(b.children) : ''}</li>`)
|
|
128
|
+
.join('')}</ul>`;
|
|
129
|
+
const sectionsHtml = sections
|
|
130
|
+
.map((s, si) => {
|
|
131
|
+
const subs = s.subsections
|
|
132
|
+
.map((sub, ui) => `<div class="sub" id="sub-${pfx}${e(sub.id)}" data-from="${sub.cite.from}" data-to="${sub.cite.to}"><h3><span class="num">${si + 1}.${ui + 1}.</span> ${e(stripNumbering(sub.title))} ${timeLink(at(sub.cite.key))}</h3>${frame(sub.frameSec, sub.id)}${bullets(sub.bullets)}</div>`)
|
|
133
|
+
.join('');
|
|
134
|
+
return `<section class="sec" id="sec-${pfx}${e(s.id)}"><h2><span class="num">${si + 1}.</span> ${e(stripNumbering(s.title))} ${timeLink(at(s.cite.key))}</h2><p class="conclusion">${inline(s.conclusion, kwIds)}${cite(s.cite)}</p>${s.diagram ? sectionDiagramHtml(s.diagram, stripNumbering(s.title), blockCtx) : ''}${subs}</section>`;
|
|
135
|
+
})
|
|
136
|
+
.join('');
|
|
137
|
+
const toc = `<nav class="toc" aria-label="목차"><h2>목차</h2><ol>${sections
|
|
138
|
+
.map((s, si) => `<li><a href="#sec-${pfx}${e(s.id)}" data-nav="sec-${pfx}${e(s.id)}">${si + 1}. ${e(stripNumbering(s.title))}</a><ol>${s.subsections
|
|
139
|
+
.map((sub, ui) => `<li><a href="#sub-${pfx}${e(sub.id)}">${si + 1}.${ui + 1}. ${e(stripNumbering(sub.title))}</a></li>`)
|
|
140
|
+
.join('')}</ol></li>`)
|
|
141
|
+
.join('')}</ol></nav>`;
|
|
142
|
+
const minimap = `<nav class="minimap" aria-label="섹션 이동" data-ri="${ri}"${hidden}>${sections
|
|
143
|
+
.map((s, si) => `<a href="#sec-${pfx}${e(s.id)}" data-nav="sec-${pfx}${e(s.id)}" title="${e(stripNumbering(s.title))}" aria-label="${si + 1}. ${e(stripNumbering(s.title))}"><span aria-hidden="true">${si + 1}</span></a>`)
|
|
144
|
+
.join('')}</nav>`;
|
|
145
|
+
const keywordsHtml = keywords.length
|
|
146
|
+
? `<section class="extra" id="${pfx}keywords"><h2>키워드</h2><dl>${keywords
|
|
147
|
+
.map((k) => `<dt>${e(k.term)} ${timeLink(at(k.firstCite.key))}</dt><dd>${inline(k.explanation, kwIds)}</dd>`)
|
|
148
|
+
.join('')}</dl></section>`
|
|
149
|
+
: '';
|
|
150
|
+
const skippedHtml = skipped.length
|
|
151
|
+
? `<section class="extra" id="${pfx}skipped"><h2>요약에서 뺀 구간</h2><ul>${skipped
|
|
152
|
+
.map((s) => `<li>${timeLink(at(s.from))} 문장 ${s.from + 1}-${s.to + 1}: ${inline(s.reason, kwIds)}</li>`)
|
|
153
|
+
.join('')}</ul></section>`
|
|
154
|
+
: '';
|
|
155
|
+
const scope = [
|
|
156
|
+
report.options.range
|
|
157
|
+
? `<p class="report-scope">${formatTime(report.options.range[0], long)}~${formatTime(report.options.range[1], long)} 구간만 요약했어요.</p>`
|
|
158
|
+
: '',
|
|
159
|
+
report.options.prompt ? `<p class="report-scope">요청: ${e(report.options.prompt)}</p>` : '',
|
|
160
|
+
].join('');
|
|
161
|
+
const body = `<div class="report" data-ri="${ri}" data-report="${e(key)}" lang="${e(report.options.lang)}"${hidden}>${scope}<section class="lead"><p class="q">${inline(lead.question, kwIds)}</p><p>${inline(lead.answer, kwIds)}</p><p class="takeaway">${inline(lead.takeaway, kwIds)}</p></section>
|
|
162
|
+
${toc}
|
|
163
|
+
${sectionsHtml}
|
|
164
|
+
${keywordsHtml}
|
|
165
|
+
${skippedHtml}</div>`;
|
|
166
|
+
return { body, minimap };
|
|
167
|
+
};
|
|
168
|
+
const rendered = reportsMeta.map(({ report, key }, ri) => renderReport(report, ri, key));
|
|
169
|
+
const pressed = (on) => `aria-pressed="${on ? 'true' : 'false'}"`;
|
|
170
|
+
const customs = reportsMeta.filter(({ report }) => !isPreset(report.options, base));
|
|
171
|
+
const reportBar = `<div class="report-bar" role="group" aria-label="리포트 고르기"><span class="bar-label">분량</span>${REPORT_LENGTHS.map((l) => `<button type="button" data-length="${l}" ${pressed(l === base.length)}>${LENGTH_LABEL[l]}</button>`).join('')}<span class="bar-label">난이도</span>${REPORT_DIFFICULTIES.map((d) => `<button type="button" data-difficulty="${d}" ${pressed(d === base.difficulty)}>${DIFFICULTY_LABEL[d]}</button>`).join('')}${customs.length
|
|
172
|
+
? `<label class="bar-label">다른 리포트 <select id="report-select">${reportsMeta
|
|
173
|
+
.map(({ report, key }, ri) => `<option value="${ri}" data-report="${e(key)}">${e(reportLabel(report.options, long))}</option>`)
|
|
174
|
+
.join('')}</select></label>`
|
|
175
|
+
: ''}</div><div id="report-missing" class="report-missing" role="status" hidden><p>이 조합의 리포트는 아직 없어요. 아래 명령을 세션에 붙여 넣으면 만들어요.</p><code id="report-cmd"></code> <button type="button" id="copy-cmd">명령 복사</button></div>`;
|
|
176
|
+
const watchBase = isYoutube ? youtubeWatchUrl(meta.videoId) : null;
|
|
177
|
+
const player = isYoutube
|
|
178
|
+
? `<div id="player-slot" class="player-slot"><div id="player" class="player"><div id="player-frame"></div><a class="player-fallback" href="${e(watchBase)}" target="_blank" rel="noopener"><img src="https://i.ytimg.com/vi/${meta.videoId}/hqdefault.jpg" alt=""><span>유튜브에서 열기</span></a><button type="button" id="player-dock" class="player-dock" aria-label="미니 플레이어 닫기">×</button></div></div>`
|
|
179
|
+
: `<div id="player-slot" class="player-slot"><div id="player" class="player player-local"><video id="player-video" controls playsinline preload="metadata" aria-label="영상 재생"></video><p class="player-note">로컬 영상은 <code>gestalt digest serve ${e(digest.slug)}</code>로 열어야 재생돼요. 원본 파일을 옮겼으면 ingest를 다시 돌려 주세요.</p><button type="button" id="player-dock" class="player-dock" aria-label="미니 플레이어 닫기">×</button></div></div>`;
|
|
180
|
+
const trLang = meta.translationLang;
|
|
181
|
+
const hasTr = trLang !== undefined && segments.some((s) => s.translation);
|
|
182
|
+
const trToggle = hasTr
|
|
183
|
+
? ` <button type="button" id="tr-toggle" class="tr-toggle" aria-pressed="true" hidden>번역</button>`
|
|
184
|
+
: '';
|
|
185
|
+
const tr = (s) => hasTr && s.translation
|
|
186
|
+
? `<span class="tr" lang="${e(trLang ?? '')}">${e(s.translation)}</span>`
|
|
187
|
+
: '';
|
|
188
|
+
const script = `<div class="script"><h2>스크립트 <small>${e(CAPTION_LABEL[meta.captionSource])}</small>${trToggle}</h2><div id="script-body" class="script-body" tabindex="0" role="region" aria-label="영상 스크립트"><ol id="script-list">${segments
|
|
189
|
+
.map((s) => `<li data-i="${s.i}">${timeLink(s.startSec, false)}<span class="txt"><span class="src">${e(s.text)}</span>${tr(s)}</span></li>`)
|
|
190
|
+
.join('')}</ol></div></div>`;
|
|
191
|
+
const facts = [meta.channel, meta.publishedAt, formatTime(meta.durationSec, long)]
|
|
192
|
+
.filter(Boolean)
|
|
193
|
+
.map((f) => `<span>${e(f)}</span>`)
|
|
194
|
+
.join('');
|
|
195
|
+
const data = {
|
|
196
|
+
videoId: isYoutube ? meta.videoId : null,
|
|
197
|
+
localVideo: meta.source === 'file',
|
|
198
|
+
watchBase,
|
|
199
|
+
long,
|
|
200
|
+
starts: segments.map((s) => s.startSec),
|
|
201
|
+
slug: digest.slug,
|
|
202
|
+
reports: reportsMeta.map(({ report, key }) => ({
|
|
203
|
+
key,
|
|
204
|
+
length: report.options.length,
|
|
205
|
+
difficulty: report.options.difficulty,
|
|
206
|
+
lang: report.options.lang,
|
|
207
|
+
preset: isPreset(report.options, base),
|
|
208
|
+
keywords: Object.fromEntries(report.keywords.map((k) => [
|
|
209
|
+
k.id,
|
|
210
|
+
{
|
|
211
|
+
term: k.term,
|
|
212
|
+
explanation: k.explanation.replace(keywordPattern(), '$2').replace(boldPattern(), '$1'),
|
|
213
|
+
sec: at(k.firstCite.key),
|
|
214
|
+
},
|
|
215
|
+
])),
|
|
216
|
+
markdown: renderDigestMarkdown(digest, transcript, {
|
|
217
|
+
frameExists: opts.frameExists,
|
|
218
|
+
report,
|
|
219
|
+
}),
|
|
220
|
+
})),
|
|
221
|
+
extensions: digest.extensions.map((x) => ({
|
|
222
|
+
key: extensionKey(x),
|
|
223
|
+
panel: extensionPanelId(x),
|
|
224
|
+
markdown: renderExtensionMarkdown(x, transcript),
|
|
225
|
+
})),
|
|
226
|
+
};
|
|
227
|
+
const extensions = digest.extensions;
|
|
228
|
+
const made = new Set(extensions.map((x) => x.template));
|
|
229
|
+
const menuTemplates = (opts.templates ?? []).filter((t) => !made.has(t.id));
|
|
230
|
+
const hasTabs = extensions.length > 0 || menuTemplates.length > 0;
|
|
231
|
+
const tab = (id, label, on) => `<button type="button" role="tab" id="tab-${e(id)}" data-tab="${e(id)}" aria-controls="${e(id)}" aria-selected="${on ? 'true' : 'false'}"${on ? '' : ' tabindex="-1"'}>${e(label)}</button>`;
|
|
232
|
+
const moreMenu = menuTemplates.length
|
|
233
|
+
? `<button type="button" id="more-btn" class="more-btn" aria-expanded="false" aria-controls="more-menu">+ 더 만들기</button></div><div id="more-menu" class="more-menu" hidden><p>템플릿을 고르면 세션에 붙여 넣을 명령을 복사해요.</p><ul>${menuTemplates
|
|
234
|
+
.map((t) => `<li><button type="button" data-template="${e(t.id)}"><b>${e(t.title)}</b><small>${e(t.description)}</small></button></li>`)
|
|
235
|
+
.join('')}</ul><div id="more-cmd-box" hidden><code id="more-cmd"></code> <button type="button" id="more-copy">명령 복사</button></div></div>`
|
|
236
|
+
: '</div>';
|
|
237
|
+
const tabRow = hasTabs
|
|
238
|
+
? `<div class="tab-row"><div class="tabs" role="tablist" aria-label="리포트 종류">${tab('panel-summary', '요약', true)}${extensions
|
|
239
|
+
.map((x) => tab(extensionPanelId(x), x.title, false))
|
|
240
|
+
.join('')}</div>${moreMenu}`
|
|
241
|
+
: '';
|
|
242
|
+
const extPanels = extensions.map((x) => extensionHtml(x, blockCtx)).join('\n');
|
|
243
|
+
const summary = `${hasTabs ? reportBar : ''}${rendered.map((r) => r.body).join('\n')}`;
|
|
244
|
+
const summaryPanel = hasTabs
|
|
245
|
+
? `<div id="panel-summary" role="tabpanel" aria-labelledby="tab-panel-summary" tabindex="0">${summary}</div>\n${extPanels}`
|
|
246
|
+
: summary;
|
|
247
|
+
const html = `<!doctype html>
|
|
248
|
+
<html lang="${e(base.lang)}">
|
|
249
|
+
<head>
|
|
250
|
+
<meta charset="utf-8">
|
|
251
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
|
252
|
+
<meta name="referrer" content="strict-origin-when-cross-origin">
|
|
253
|
+
<title>${e(meta.title)}</title>
|
|
254
|
+
<style>${CSS}</style>
|
|
255
|
+
${hasTabs ? '<noscript><style>.ext{display:block!important}.tabs,.more-btn,.quiz-check,.zoom-btn{display:none}.quiz-exp{display:block!important}</style></noscript>' : ''}
|
|
256
|
+
</head>
|
|
257
|
+
<body>
|
|
258
|
+
<div class="layout">
|
|
259
|
+
<aside class="side">${player}${script}</aside>
|
|
260
|
+
<main class="main">
|
|
261
|
+
<header class="head"><h1>${e(meta.title)}</h1><p class="facts">${facts}</p><div class="tools"><button type="button" id="copy-md">마크다운 복사</button><label>글자 크기 <input id="font-range" type="range" min="85" max="145" step="5" value="100"> <output id="font-value">100%</output></label><p id="copy-status" class="sr-only" role="status" aria-live="polite"></p></div>
|
|
262
|
+
${hasTabs ? '' : reportBar}
|
|
263
|
+
<noscript><p class="note">자바스크립트가 꺼져 있어 출처 툴팁과 키워드 팝업, 재생 따라가기, 리포트 전환이 동작하지 않아요.${isYoutube ? ' 시각 링크는 유튜브 새 탭으로 열려요.' : ''}</p></noscript></header>
|
|
264
|
+
${tabRow}
|
|
265
|
+
${summaryPanel}
|
|
266
|
+
</main>
|
|
267
|
+
</div>
|
|
268
|
+
${rendered.map((r) => r.minimap).join('')}
|
|
269
|
+
<script type="application/json" id="digest-data">${embedJson(data)}</script>
|
|
270
|
+
<script>${VIEWER_JS}</script>
|
|
271
|
+
</body>
|
|
272
|
+
</html>
|
|
273
|
+
`;
|
|
274
|
+
return { html, warnings: [...new Set(warnings)] };
|
|
275
|
+
}
|
|
276
|
+
// 토큰은 research 렌더러와 같은 값을 쓴다
|
|
277
|
+
const CSS = `
|
|
278
|
+
:root{--bg:#fafaf9;--fg:#1c1917;--muted:#78716c;--line:#e7e5e4;--card:#fff;--accent:#2563eb;--warn:#b45309;--chip:#f5f5f4;--hl:#fef3c7;--on-accent:#fff;--scale:1;--float-w:min(360px,60vw,calc(40vh * 16 / 9))}
|
|
279
|
+
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){--bg:#0c0a09;--fg:#f5f5f4;--muted:#a8a29e;--line:#292524;--card:#1c1917;--accent:#60a5fa;--warn:#fbbf24;--chip:#292524;--hl:#422006;--on-accent:#0c0a09}}
|
|
280
|
+
:root[data-theme="dark"]{--bg:#0c0a09;--fg:#f5f5f4;--muted:#a8a29e;--line:#292524;--card:#1c1917;--accent:#60a5fa;--warn:#fbbf24;--chip:#292524;--hl:#422006;--on-accent:#0c0a09}
|
|
281
|
+
*{box-sizing:border-box}
|
|
282
|
+
html{font-size:calc(100% * var(--scale))}
|
|
283
|
+
body{margin:0;background:var(--bg);color:var(--fg);font:1rem/1.7 -apple-system,BlinkMacSystemFont,"Apple SD Gothic Neo","Pretendard",sans-serif}
|
|
284
|
+
a{color:var(--accent)}
|
|
285
|
+
button{font:inherit;color:inherit}
|
|
286
|
+
.layout{display:grid;grid-template-columns:minmax(300px,420px) minmax(0,1fr);gap:32px;max-width:1360px;margin:0 auto;padding:0 56px 0 16px}
|
|
287
|
+
.side{position:sticky;top:0;height:100vh;display:flex;flex-direction:column;gap:12px;padding:16px 0}
|
|
288
|
+
.player{position:relative;background:#000;border-radius:8px;overflow:hidden;flex:none}
|
|
289
|
+
#player-frame{position:absolute;inset:0;width:100%;height:100%;border:0;opacity:0;pointer-events:none}
|
|
290
|
+
.player.is-live #player-frame{position:static;display:block;height:auto;aspect-ratio:16/9;opacity:1;pointer-events:auto}
|
|
291
|
+
.player.is-live .player-fallback{display:none}
|
|
292
|
+
.player-fallback{display:block;position:relative;color:#fff;text-decoration:none}
|
|
293
|
+
.player-fallback img{display:block;width:100%;aspect-ratio:16/9;object-fit:cover;opacity:.7}
|
|
294
|
+
.player-fallback span{position:absolute;inset:auto 0 0 0;padding:10px 14px;background:rgba(0,0,0,.6);font-weight:600}
|
|
295
|
+
.player-note{background:var(--chip);color:var(--muted);padding:12px 14px;font-size:.9em;margin:0}
|
|
296
|
+
.player-local video{display:none;width:100%;aspect-ratio:16/9;background:#000}
|
|
297
|
+
.player-local.is-live video{display:block}
|
|
298
|
+
.player-local.is-live .player-note{display:none}
|
|
299
|
+
.player-slot{flex:none}
|
|
300
|
+
.player-slot.is-floating{aspect-ratio:16/9}
|
|
301
|
+
.player.is-floating{position:fixed;right:calc(16px + env(safe-area-inset-right, 0px));bottom:calc(16px + env(safe-area-inset-bottom, 0px));width:var(--float-w);z-index:15;box-shadow:0 8px 24px rgba(0,0,0,.35)}
|
|
302
|
+
.player-dock{display:none;position:absolute;top:6px;right:6px;width:40px;height:40px;border:0;border-radius:50%;background:rgba(0,0,0,.6);color:#fff;cursor:pointer;align-items:center;justify-content:center;font-size:18px;line-height:1}
|
|
303
|
+
.player.is-floating .player-dock{display:flex}
|
|
304
|
+
.script{display:flex;flex-direction:column;min-height:0;flex:1;border:1px solid var(--line);border-radius:8px;background:var(--card)}
|
|
305
|
+
.script h2{margin:0;padding:8px 12px;font-size:.95em;border-bottom:1px solid var(--line)}
|
|
306
|
+
.script h2 small{color:var(--muted);font-weight:400}
|
|
307
|
+
.script-body{overflow:auto;flex:1;position:relative}
|
|
308
|
+
#script-list{list-style:none;margin:0;padding:4px 0}
|
|
309
|
+
#script-list li{display:flex;gap:10px;padding:4px 12px;font-size:.9em;line-height:1.55}
|
|
310
|
+
#script-list li.is-playing{background:var(--hl)}
|
|
311
|
+
.tr{display:block;color:var(--muted);font-size:.95em}
|
|
312
|
+
.script.hide-tr .tr{display:none}
|
|
313
|
+
.tr-toggle{float:right;border:1px solid var(--line);background:var(--card);border-radius:6px;padding:0 8px;font-size:.85em;font-weight:400;cursor:pointer}
|
|
314
|
+
.tr-toggle[aria-pressed="true"]{background:var(--accent);color:var(--on-accent);border-color:var(--accent)}
|
|
315
|
+
.ts{font-variant-numeric:tabular-nums;color:var(--accent);text-decoration:none;font-size:.85em;white-space:nowrap;cursor:pointer}
|
|
316
|
+
a.ts:hover{text-decoration:underline}
|
|
317
|
+
.main{padding:24px 0 96px;min-width:0}
|
|
318
|
+
.head h1{margin:0 0 4px;font-size:1.6em;line-height:1.35}
|
|
319
|
+
.facts{margin:0;color:var(--muted);display:flex;flex-wrap:wrap;gap:12px;font-size:.9em}
|
|
320
|
+
.tools{display:flex;flex-wrap:wrap;gap:16px;align-items:center;margin:12px 0;font-size:.85em;color:var(--muted)}
|
|
321
|
+
.tools button{border:1px solid var(--line);background:var(--card);border-radius:6px;padding:4px 10px;cursor:pointer}
|
|
322
|
+
.note{color:var(--warn);font-size:.9em}
|
|
323
|
+
.report-bar{display:flex;flex-wrap:wrap;gap:6px;align-items:center;margin:8px 0;font-size:.85em}
|
|
324
|
+
.report-bar .bar-label{color:var(--muted);margin-left:6px}
|
|
325
|
+
.report-bar .bar-label:first-child{margin-left:0}
|
|
326
|
+
.report-bar button{border:1px solid var(--line);background:var(--card);border-radius:999px;padding:3px 12px;cursor:pointer}
|
|
327
|
+
.report-bar button[aria-pressed="true"]{background:var(--accent);border-color:var(--accent);color:var(--on-accent)}
|
|
328
|
+
.report-bar select{font:inherit;color:inherit;background:var(--card);border:1px solid var(--line);border-radius:6px;padding:2px 6px}
|
|
329
|
+
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
|
|
330
|
+
.report-missing{border:1px dashed var(--line);border-radius:8px;padding:8px 12px;margin:8px 0;font-size:.88em;background:var(--card)}
|
|
331
|
+
.report-missing p{margin:0 0 6px}
|
|
332
|
+
.report-missing code{display:block;white-space:nowrap;overflow-x:auto;background:var(--chip);padding:4px 8px;border-radius:4px;margin:0 0 6px}
|
|
333
|
+
#report-cmd{user-select:all}
|
|
334
|
+
.report-missing button{border:1px solid var(--line);background:var(--card);border-radius:6px;padding:2px 10px;cursor:pointer}
|
|
335
|
+
.report-scope{color:var(--muted);font-size:.9em;margin:12px 0 0}
|
|
336
|
+
.tab-row{display:flex;flex-wrap:wrap;align-items:flex-end;gap:8px;border-bottom:1px solid var(--line);margin:12px 0 8px}
|
|
337
|
+
.tabs{display:flex;flex-wrap:wrap;gap:2px}
|
|
338
|
+
.tabs [role="tab"]{border:0;border-bottom:2px solid transparent;background:none;color:var(--muted);padding:6px 12px;font:inherit;font-size:.92em;cursor:pointer}
|
|
339
|
+
.tabs [role="tab"][aria-selected="true"]{color:var(--fg);border-bottom-color:var(--accent);font-weight:600}
|
|
340
|
+
.more-btn{margin-left:auto;margin-bottom:4px;border:1px dashed var(--line);background:var(--card);border-radius:999px;padding:3px 12px;font-size:.85em;cursor:pointer}
|
|
341
|
+
.more-menu{border:1px solid var(--line);border-radius:8px;padding:8px 12px;margin:0 0 8px;background:var(--card);font-size:.88em}
|
|
342
|
+
.more-menu p{margin:0 0 6px;color:var(--muted)}
|
|
343
|
+
.more-menu ul{list-style:none;margin:0 0 6px;padding:0;display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:6px}
|
|
344
|
+
.more-menu li button{width:100%;text-align:left;border:1px solid var(--line);background:var(--bg);border-radius:6px;padding:6px 8px;cursor:pointer;color:inherit;font:inherit}
|
|
345
|
+
.more-menu li small{display:block;color:var(--muted);font-size:.85em}
|
|
346
|
+
.more-menu code{display:block;white-space:nowrap;overflow-x:auto;background:var(--chip);padding:4px 8px;border-radius:4px;margin:0 0 6px}
|
|
347
|
+
#more-copy{border:1px solid var(--line);background:var(--card);border-radius:6px;padding:2px 10px;cursor:pointer}
|
|
348
|
+
.ext h2{font-size:1.35em;border-bottom:1px solid var(--line);padding-bottom:6px}
|
|
349
|
+
.ext h3{font-size:1.1em;margin:20px 0 6px}
|
|
350
|
+
.ext h4{font-size:1em;margin:12px 0 4px}
|
|
351
|
+
.quote{margin:16px 0;padding:10px 16px;border-left:3px solid var(--accent);background:var(--chip);border-radius:0 8px 8px 0}
|
|
352
|
+
.quote blockquote{margin:0;font-size:1.05em}
|
|
353
|
+
.quote-tr{margin:6px 0 0;color:var(--muted)}
|
|
354
|
+
.quote figcaption{margin-top:6px;font-size:.9em;color:var(--muted);display:flex;flex-wrap:wrap;gap:8px}
|
|
355
|
+
.ext-toc{list-style:none;padding:0;margin:8px 0}
|
|
356
|
+
.ext-toc li{display:flex;gap:12px;padding:6px 0;border-bottom:1px solid var(--line)}
|
|
357
|
+
.ext-toc li .ts{flex:none;padding-top:2px}
|
|
358
|
+
.ext-toc p{margin:2px 0 0;color:var(--muted);font-size:.95em}
|
|
359
|
+
.actions{list-style:none;padding:0;margin:8px 0}
|
|
360
|
+
.actions li{display:flex;gap:10px;padding:4px 0}
|
|
361
|
+
.actions .box{flex:none;width:14px;height:14px;margin-top:6px;border:1.5px solid var(--muted);border-radius:3px}
|
|
362
|
+
.actions small{display:flex;gap:10px;color:var(--muted)}
|
|
363
|
+
.qa dt{font-weight:600;margin-top:12px}
|
|
364
|
+
.qa dt::before{content:"Q. ";color:var(--accent)}
|
|
365
|
+
.qa dd{margin:4px 0 0}
|
|
366
|
+
.cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:10px;margin:12px 0}
|
|
367
|
+
.card{border:1px solid var(--line);border-radius:10px;background:var(--card);padding:12px 14px}
|
|
368
|
+
.card summary{cursor:pointer;font-weight:600;list-style:none;display:flex;justify-content:space-between;gap:10px}
|
|
369
|
+
.card summary::-webkit-details-marker{display:none}
|
|
370
|
+
.card summary::after{content:"";flex:none;width:7px;height:7px;margin-top:.4em;border-right:2px solid var(--muted);border-bottom:2px solid var(--muted);transform:rotate(45deg)}
|
|
371
|
+
.card[open] summary{color:var(--muted);font-weight:400;font-size:.9em}
|
|
372
|
+
.card[open] summary::after{transform:rotate(-135deg);margin-top:.6em}
|
|
373
|
+
.card-back{margin-top:8px}
|
|
374
|
+
.quiz{padding-left:20px}
|
|
375
|
+
.quiz-q{border:1px solid var(--line);border-radius:10px;background:var(--card);padding:10px 14px;margin:0 0 12px}
|
|
376
|
+
.quiz-q legend{font-weight:600;padding:0 4px}
|
|
377
|
+
.quiz-choice{display:block;padding:3px 0;cursor:pointer}
|
|
378
|
+
.quiz-check{margin-top:6px;border:1px solid var(--line);background:var(--bg);border-radius:6px;padding:2px 10px;cursor:pointer;color:inherit;font:inherit}
|
|
379
|
+
.quiz-exp{margin-top:8px;border-top:1px solid var(--line)}
|
|
380
|
+
.quiz-exp p{margin:6px 0}
|
|
381
|
+
.quiz-result{margin:6px 0}.quiz-result:empty{margin:0}
|
|
382
|
+
.quiz-q.is-right .quiz-result{color:var(--accent);font-weight:600}
|
|
383
|
+
.quiz-q.is-wrong .quiz-result{color:var(--warn);font-weight:600}
|
|
384
|
+
.quiz-choice.is-answer>span:not(.quiz-mark){background:var(--hl);font-weight:600}
|
|
385
|
+
.quiz-choice.is-picked>span:not(.quiz-mark){outline:2px dashed var(--warn);outline-offset:1px;border-radius:3px}
|
|
386
|
+
.diagram{margin:16px 0}
|
|
387
|
+
.dg-scroll{overflow-x:auto;border:1px solid var(--line);border-radius:10px;background:var(--card);padding:8px}
|
|
388
|
+
.dg-svg{display:block;max-width:none}
|
|
389
|
+
.dg-svg text{fill:var(--fg);font-size:13px;font-family:inherit}
|
|
390
|
+
.dg-svg rect{fill:var(--bg);stroke:var(--line)}
|
|
391
|
+
.dg-svg path,.dg-svg line{stroke:var(--muted);fill:none;stroke-width:1.2}
|
|
392
|
+
.dg-svg .mm-d0 rect,.dg-svg .dg-head rect{fill:var(--accent);stroke:var(--accent)}
|
|
393
|
+
.dg-svg .mm-d0 text,.dg-svg .dg-head text{fill:var(--on-accent);font-size:14px;font-weight:600}
|
|
394
|
+
.dg-svg .mm-d1 rect,.dg-flow rect,.dg-cycle rect{stroke:var(--accent)}
|
|
395
|
+
.dg-svg .dg-arrow{fill:var(--muted);stroke:none}
|
|
396
|
+
.dg-svg .mm-cite,.dg-svg .dg-cite{cursor:pointer}
|
|
397
|
+
.dg-svg .mm-cite:hover rect,.dg-svg .dg-cite:hover rect{fill:var(--hl)}
|
|
398
|
+
.dg-svg .mm-cite:focus{outline:none}.dg-svg .dg-cite:focus{outline:none}
|
|
399
|
+
.dg-svg .mm-cite:focus-visible rect,.dg-svg .dg-cite:focus-visible rect{stroke:var(--accent);stroke-width:2}
|
|
400
|
+
.dg-bar{display:flex;justify-content:flex-end;align-items:center;gap:8px;margin-top:4px;font-size:.9em;color:var(--muted)}
|
|
401
|
+
.dg-bar span{margin-right:auto}
|
|
402
|
+
.zoom-btn{border:1px solid var(--line);background:var(--card);border-radius:6px;padding:2px 10px;cursor:pointer;color:inherit;font:inherit}
|
|
403
|
+
.dg-outline{margin-top:4px;font-size:.92em}
|
|
404
|
+
.dg-outline summary{cursor:pointer;color:var(--muted)}
|
|
405
|
+
#zoom-dialog{width:min(96vw,1400px);max-height:92vh;border:1px solid var(--line);border-radius:12px;background:var(--card);color:var(--fg);padding:12px}
|
|
406
|
+
#zoom-dialog::backdrop{background:rgba(0,0,0,.5)}
|
|
407
|
+
#zoom-dialog .zoom-body{overflow:auto;max-height:calc(92vh - 64px)}
|
|
408
|
+
#zoom-dialog .zoom-body svg{width:100%;height:auto;min-width:600px}
|
|
409
|
+
#zoom-dialog .zoom-close{float:right}
|
|
410
|
+
.cm-overview{font-size:1.05em}
|
|
411
|
+
.cm-meta{color:var(--muted);font-size:.9em}
|
|
412
|
+
.cm-groups{list-style:none;padding:0}
|
|
413
|
+
.cm-group{border:1px solid var(--line);border-radius:10px;background:var(--card);padding:10px 14px;margin:0 0 10px}
|
|
414
|
+
.cm-head{display:flex;flex-wrap:wrap;align-items:baseline;gap:8px}
|
|
415
|
+
.cm-tone{font-size:.8em;border-radius:999px;padding:0 8px;background:var(--chip);color:var(--muted)}
|
|
416
|
+
.cm-positive .cm-tone{color:var(--accent)}
|
|
417
|
+
.cm-negative .cm-tone{color:var(--warn)}
|
|
418
|
+
.cm-count{margin-left:auto;color:var(--muted);font-size:.9em}
|
|
419
|
+
.cm-bar{height:4px;background:var(--chip);border-radius:2px;margin:6px 0}
|
|
420
|
+
.cm-bar i{display:block;height:100%;background:currentColor;border-radius:2px;color:var(--muted)}
|
|
421
|
+
.cm-positive .cm-bar i{color:var(--accent)}
|
|
422
|
+
.cm-negative .cm-bar i{color:var(--warn)}
|
|
423
|
+
.cm-ex{padding-left:18px;color:var(--muted);font-size:.95em}
|
|
424
|
+
.cm-ex q{white-space:pre-line}
|
|
425
|
+
body[data-tab="ext"] .minimap{display:none}
|
|
426
|
+
.lead{border-left:3px solid var(--accent);padding:4px 16px;margin:20px 0}
|
|
427
|
+
.lead .q{font-weight:700;font-size:1.1em}
|
|
428
|
+
.lead .takeaway{color:var(--muted)}
|
|
429
|
+
.toc{border:1px solid var(--line);border-radius:8px;padding:8px 20px;background:var(--card);margin:20px 0}
|
|
430
|
+
.toc h2{font-size:1em;margin:8px 0}
|
|
431
|
+
.toc ol{list-style:none;padding-left:0;margin:4px 0}
|
|
432
|
+
.toc ol ol{padding-left:16px;font-size:.92em}
|
|
433
|
+
.toc a{text-decoration:none;color:var(--fg)}
|
|
434
|
+
.toc a.is-current{color:var(--accent);font-weight:600}
|
|
435
|
+
.sec{scroll-margin-top:16px;padding-top:12px}
|
|
436
|
+
.sec h2{font-size:1.35em;border-bottom:1px solid var(--line);padding-bottom:6px}
|
|
437
|
+
.sub{scroll-margin-top:16px}
|
|
438
|
+
.sub h3{font-size:1.1em;margin:24px 0 8px}
|
|
439
|
+
.num{color:var(--muted)}
|
|
440
|
+
.conclusion{font-weight:500}
|
|
441
|
+
.frame{margin:8px 0 12px}
|
|
442
|
+
.frame img{display:block;width:100%;max-width:640px;height:auto;max-height:360px;aspect-ratio:auto 16/9;object-fit:contain;border-radius:6px;border:1px solid var(--line);cursor:pointer}
|
|
443
|
+
.frame figcaption{margin-top:2px}
|
|
444
|
+
.cite{border:0;background:none;padding:0 1px;color:var(--accent);cursor:pointer;font-size:.8em}
|
|
445
|
+
.kw{border:0;background:none;padding:0;border-bottom:1px dashed var(--accent);cursor:pointer}
|
|
446
|
+
.extra dt{font-weight:600;margin-top:8px}
|
|
447
|
+
.extra dd{margin:0 0 4px 0;color:var(--muted)}
|
|
448
|
+
.pop{position:absolute;z-index:20;max-width:min(420px,calc(100vw - 16px));background:var(--card);border:1px solid var(--line);border-radius:8px;box-shadow:0 6px 24px rgba(0,0,0,.18);padding:10px 12px;font-size:.88em}
|
|
449
|
+
.pop-head{display:flex;gap:6px;align-items:baseline;margin-bottom:4px}
|
|
450
|
+
.pop-range,.pop-more{color:var(--muted)}
|
|
451
|
+
.pop-lines{margin:0;padding-left:18px}
|
|
452
|
+
.pop-lines li.is-key{font-weight:600}
|
|
453
|
+
.pop p{margin:4px 0 0}
|
|
454
|
+
.minimap{position:fixed;right:12px;top:50%;transform:translateY(-50%);display:flex;flex-direction:column;gap:6px;z-index:10}
|
|
455
|
+
.minimap a{display:flex;width:28px;height:28px;border-radius:50%;align-items:center;justify-content:center;background:var(--card);border:1px solid var(--line);color:var(--muted);text-decoration:none;font-size:12px}
|
|
456
|
+
.minimap a.is-current{background:var(--accent);border-color:var(--accent);color:var(--on-accent)}
|
|
457
|
+
.minimap[hidden]{display:none}
|
|
458
|
+
@media (max-width:900px){
|
|
459
|
+
.layout{grid-template-columns:minmax(0,1fr);padding:0 16px;gap:0}
|
|
460
|
+
.side{position:static;height:auto}
|
|
461
|
+
.script-body{max-height:40vh}
|
|
462
|
+
.minimap{display:none}
|
|
463
|
+
.main{padding-bottom:calc(var(--float-w) * 9 / 16 + 32px + env(safe-area-inset-bottom, 0px))}
|
|
464
|
+
}
|
|
465
|
+
`;
|
|
466
|
+
//# sourceMappingURL=render-html.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-html.js","sourceRoot":"","sources":["../../../src/digest/render-html.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Extension } from './blocks.js';
|
|
2
|
+
import { type Digest, type Report, type Transcript } from './schema.js';
|
|
3
|
+
export interface RenderMdOptions {
|
|
4
|
+
/** 이 이름의 프레임이 있으면 소섹션에 `<framesHref>/...` 상대 경로로 싣는다 */
|
|
5
|
+
frameExists?: (fileName: string) => boolean;
|
|
6
|
+
/** 마크다운 파일에서 frames 폴더까지의 상대 경로. 슬러그 폴더 밖에 쓸 때만 바꾼다 */
|
|
7
|
+
framesHref?: string;
|
|
8
|
+
/** 내보낼 리포트. 없으면 첫 리포트다 */
|
|
9
|
+
report?: Report;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* digest.json을 마크다운 한 파일로 바꾼다. LLM을 부르지 않는다.
|
|
13
|
+
* 기존 video-summarizer가 직접 쓰던 마크다운을 이 출력이 대신한다.
|
|
14
|
+
*/
|
|
15
|
+
export declare function renderDigestMarkdown(digest: Digest, transcript: Transcript, opts?: RenderMdOptions): string;
|
|
16
|
+
/** 확장 리포트 한 벌을 마크다운으로. 머리말은 요약 마크다운과 같다 */
|
|
17
|
+
export declare function renderExtensionMarkdown(ext: Extension, transcript: Transcript): string;
|
|
18
|
+
//# sourceMappingURL=render-md.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-md.d.ts","sourceRoot":"","sources":["../../../src/digest/render-md.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAK7C,OAAO,EAIL,KAAK,MAAM,EACX,KAAK,MAAM,EACX,KAAK,UAAU,EAChB,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,eAAe;IAC9B,wDAAwD;IACxD,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;IAC5C,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0BAA0B;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAKD;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,UAAU,EACtB,IAAI,GAAE,eAAoB,GACzB,MAAM,CAyDR;AAED,2CAA2C;AAC3C,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,GAAG,MAAM,CAOtF"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { frameFileName } from './frames.js';
|
|
2
|
+
import { CAPTION_LABEL, formatTime, stripNumbering, watchUrlAt } from './format.js';
|
|
3
|
+
import { blockMarkdown, mdInline, sectionDiagramMd } from './render-blocks.js';
|
|
4
|
+
import { reportLabel } from './reports.js';
|
|
5
|
+
import { keywordPattern, } from './schema.js';
|
|
6
|
+
/** 키워드 마커는 표시 글자만 남긴다. 굵게(`**`)는 마크다운 문법 그대로라 둔다 */
|
|
7
|
+
const plain = (text) => text.replace(keywordPattern(), (_m, _id, label) => label);
|
|
8
|
+
/**
|
|
9
|
+
* digest.json을 마크다운 한 파일로 바꾼다. LLM을 부르지 않는다.
|
|
10
|
+
* 기존 video-summarizer가 직접 쓰던 마크다운을 이 출력이 대신한다.
|
|
11
|
+
*/
|
|
12
|
+
export function renderDigestMarkdown(digest, transcript, opts = {}) {
|
|
13
|
+
const { meta, segments } = transcript;
|
|
14
|
+
const long = meta.durationSec >= 3600;
|
|
15
|
+
const at = (i) => segments[i]?.startSec ?? 0;
|
|
16
|
+
const framesHref = opts.framesHref ?? 'frames';
|
|
17
|
+
const cite = mdCiter(transcript);
|
|
18
|
+
const out = mdHeader(transcript);
|
|
19
|
+
const report = opts.report ?? digest.reports[0];
|
|
20
|
+
// 리포트가 하나뿐이고 옵션도 기본이면 1차 출력과 같게 둔다
|
|
21
|
+
if (digest.reports.length > 1 || report.options.range || report.options.prompt) {
|
|
22
|
+
out.push(`> 리포트: ${reportLabel(report.options, long)}`);
|
|
23
|
+
if (report.options.prompt) {
|
|
24
|
+
out.push(`> 요청: ${report.options.prompt.replace(/\s+/g, ' ')}`);
|
|
25
|
+
}
|
|
26
|
+
out.push('');
|
|
27
|
+
}
|
|
28
|
+
const { lead, sections, skipped, keywords } = report;
|
|
29
|
+
out.push(`**${plain(lead.question)}**`, '', plain(lead.answer), '', plain(lead.takeaway), '');
|
|
30
|
+
const bullet = (b, depth) => {
|
|
31
|
+
const pad = ' '.repeat(depth);
|
|
32
|
+
const lines = [`${pad}- ${plain(b.text)}${cite(b.cite)}`];
|
|
33
|
+
for (const c of b.children ?? [])
|
|
34
|
+
lines.push(...bullet(c, depth + 1));
|
|
35
|
+
return lines;
|
|
36
|
+
};
|
|
37
|
+
const time = mdTimer(transcript);
|
|
38
|
+
sections.forEach((s, si) => {
|
|
39
|
+
out.push(`## ${si + 1}. ${stripNumbering(s.title)}`, '', plain(s.conclusion), '');
|
|
40
|
+
if (s.diagram)
|
|
41
|
+
out.push(...sectionDiagramMd(s.diagram, { cite, time }));
|
|
42
|
+
s.subsections.forEach((sub, ui) => {
|
|
43
|
+
out.push(`### ${si + 1}.${ui + 1}. ${stripNumbering(sub.title)}`, '');
|
|
44
|
+
const frame = frameFileName(sub.frameSec);
|
|
45
|
+
if (opts.frameExists?.(frame))
|
|
46
|
+
out.push(``, '');
|
|
47
|
+
for (const b of sub.bullets)
|
|
48
|
+
out.push(...bullet(b, 0));
|
|
49
|
+
out.push('');
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
if (keywords.length > 0) {
|
|
53
|
+
out.push('## 키워드', '');
|
|
54
|
+
for (const k of keywords)
|
|
55
|
+
out.push(`- **${k.term}**: ${plain(k.explanation)}${cite(k.firstCite)}`);
|
|
56
|
+
out.push('');
|
|
57
|
+
}
|
|
58
|
+
if (skipped.length > 0) {
|
|
59
|
+
out.push('## 요약에서 뺀 구간', '');
|
|
60
|
+
for (const s of skipped) {
|
|
61
|
+
out.push(`- ${formatTime(at(s.from), long)}~ 문장 ${s.from + 1}-${s.to + 1}: ${plain(s.reason)}`);
|
|
62
|
+
}
|
|
63
|
+
out.push('');
|
|
64
|
+
}
|
|
65
|
+
return finish(out);
|
|
66
|
+
}
|
|
67
|
+
/** 확장 리포트 한 벌을 마크다운으로. 머리말은 요약 마크다운과 같다 */
|
|
68
|
+
export function renderExtensionMarkdown(ext, transcript) {
|
|
69
|
+
const cite = mdCiter(transcript);
|
|
70
|
+
const out = mdHeader(transcript);
|
|
71
|
+
out.push(`> 리포트: ${mdInline(ext.title)}`, '');
|
|
72
|
+
const time = mdTimer(transcript);
|
|
73
|
+
for (const b of ext.blocks)
|
|
74
|
+
out.push(...blockMarkdown(b, { cite, time }));
|
|
75
|
+
return finish(out);
|
|
76
|
+
}
|
|
77
|
+
function mdCiter(transcript) {
|
|
78
|
+
const { meta, segments } = transcript;
|
|
79
|
+
const long = meta.durationSec >= 3600;
|
|
80
|
+
const at = (i) => segments[i]?.startSec ?? 0;
|
|
81
|
+
return (c) => {
|
|
82
|
+
const url = watchUrlAt(meta, at(c.key));
|
|
83
|
+
return url ? ` [[${c.key + 1}]](${url})` : ` [${c.key + 1} ${formatTime(at(c.key), long)}]`;
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function mdTimer({ meta, segments }) {
|
|
87
|
+
const long = meta.durationSec >= 3600;
|
|
88
|
+
return (c) => {
|
|
89
|
+
const sec = segments[c.key]?.startSec ?? 0;
|
|
90
|
+
const url = watchUrlAt(meta, sec);
|
|
91
|
+
return url ? `[${formatTime(sec, long)}](${url})` : formatTime(sec, long);
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function mdHeader({ meta }) {
|
|
95
|
+
const long = meta.durationSec >= 3600;
|
|
96
|
+
const out = [`# ${meta.title}`, ''];
|
|
97
|
+
const facts = [
|
|
98
|
+
meta.channel,
|
|
99
|
+
meta.publishedAt,
|
|
100
|
+
formatTime(meta.durationSec, long),
|
|
101
|
+
CAPTION_LABEL[meta.captionSource],
|
|
102
|
+
].filter(Boolean);
|
|
103
|
+
out.push(`> ${facts.join(' | ')}`);
|
|
104
|
+
if (meta.source === 'youtube' && meta.url)
|
|
105
|
+
out.push(`> ${meta.url}`);
|
|
106
|
+
out.push('');
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
const finish = (out) => `${out
|
|
110
|
+
.join('\n')
|
|
111
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
112
|
+
.trimEnd()}\n`;
|
|
113
|
+
//# sourceMappingURL=render-md.js.map
|