@tienne/gestalt 0.102.0 → 0.104.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 (179) hide show
  1. package/README.ko.md +2 -1
  2. package/README.md +2 -1
  3. package/dist/package.json +1 -1
  4. package/dist/plugin/role-agents/video-summarizer/AGENT.md +208 -108
  5. package/dist/plugin/skills/_shared/proactive-routing.md +2 -1
  6. package/dist/plugin/skills/research/references/design.md +1 -1
  7. package/dist/plugin/skills/video-digest/SKILL.md +430 -0
  8. package/dist/plugin/skills/video-digest/templates/background.md +15 -0
  9. package/dist/plugin/skills/video-digest/templates/blog.md +16 -0
  10. package/dist/plugin/skills/video-digest/templates/comments.md +19 -0
  11. package/dist/plugin/skills/video-digest/templates/counterpoints.md +15 -0
  12. package/dist/plugin/skills/video-digest/templates/flashcards.md +15 -0
  13. package/dist/plugin/skills/video-digest/templates/interview.md +15 -0
  14. package/dist/plugin/skills/video-digest/templates/meeting.md +15 -0
  15. package/dist/plugin/skills/video-digest/templates/mindmap.md +16 -0
  16. package/dist/plugin/skills/video-digest/templates/quiz.md +16 -0
  17. package/dist/plugin/skills/video-digest/templates/quotes.md +16 -0
  18. package/dist/plugin/skills/video-digest/templates/resources.md +14 -0
  19. package/dist/plugin/skills/video-digest/templates/timestamps.md +14 -0
  20. package/dist/plugin/skills/video-digest/templates/visual.md +22 -0
  21. package/dist/src/cli/commands/digest.d.ts +60 -0
  22. package/dist/src/cli/commands/digest.d.ts.map +1 -0
  23. package/dist/src/cli/commands/digest.js +401 -0
  24. package/dist/src/cli/commands/digest.js.map +1 -0
  25. package/dist/src/cli/index.d.ts.map +1 -1
  26. package/dist/src/cli/index.js +81 -0
  27. package/dist/src/cli/index.js.map +1 -1
  28. package/dist/src/digest/blocks.d.ts +5224 -0
  29. package/dist/src/digest/blocks.d.ts.map +1 -0
  30. package/dist/src/digest/blocks.js +156 -0
  31. package/dist/src/digest/blocks.js.map +1 -0
  32. package/dist/src/digest/captions.d.ts +37 -0
  33. package/dist/src/digest/captions.d.ts.map +1 -0
  34. package/dist/src/digest/captions.js +138 -0
  35. package/dist/src/digest/captions.js.map +1 -0
  36. package/dist/src/digest/comments.d.ts +113 -0
  37. package/dist/src/digest/comments.d.ts.map +1 -0
  38. package/dist/src/digest/comments.js +153 -0
  39. package/dist/src/digest/comments.js.map +1 -0
  40. package/dist/src/digest/diagram-schema.d.ts +765 -0
  41. package/dist/src/digest/diagram-schema.d.ts.map +1 -0
  42. package/dist/src/digest/diagram-schema.js +72 -0
  43. package/dist/src/digest/diagram-schema.js.map +1 -0
  44. package/dist/src/digest/format.d.ts +9 -0
  45. package/dist/src/digest/format.d.ts.map +1 -0
  46. package/dist/src/digest/format.js +26 -0
  47. package/dist/src/digest/format.js.map +1 -0
  48. package/dist/src/digest/frames.d.ts +46 -0
  49. package/dist/src/digest/frames.d.ts.map +1 -0
  50. package/dist/src/digest/frames.js +206 -0
  51. package/dist/src/digest/frames.js.map +1 -0
  52. package/dist/src/digest/index.d.ts +26 -0
  53. package/dist/src/digest/index.d.ts.map +1 -0
  54. package/dist/src/digest/index.js +26 -0
  55. package/dist/src/digest/index.js.map +1 -0
  56. package/dist/src/digest/ingest.d.ts +65 -0
  57. package/dist/src/digest/ingest.d.ts.map +1 -0
  58. package/dist/src/digest/ingest.js +303 -0
  59. package/dist/src/digest/ingest.js.map +1 -0
  60. package/dist/src/digest/lint-extension.d.ts +71 -0
  61. package/dist/src/digest/lint-extension.d.ts.map +1 -0
  62. package/dist/src/digest/lint-extension.js +512 -0
  63. package/dist/src/digest/lint-extension.js.map +1 -0
  64. package/dist/src/digest/lint.d.ts +50 -0
  65. package/dist/src/digest/lint.d.ts.map +1 -0
  66. package/dist/src/digest/lint.js +560 -0
  67. package/dist/src/digest/lint.js.map +1 -0
  68. package/dist/src/digest/merge.d.ts +40 -0
  69. package/dist/src/digest/merge.d.ts.map +1 -0
  70. package/dist/src/digest/merge.js +107 -0
  71. package/dist/src/digest/merge.js.map +1 -0
  72. package/dist/src/digest/ocr.d.ts +100 -0
  73. package/dist/src/digest/ocr.d.ts.map +1 -0
  74. package/dist/src/digest/ocr.js +179 -0
  75. package/dist/src/digest/ocr.js.map +1 -0
  76. package/dist/src/digest/prose.d.ts +47 -0
  77. package/dist/src/digest/prose.d.ts.map +1 -0
  78. package/dist/src/digest/prose.js +171 -0
  79. package/dist/src/digest/prose.js.map +1 -0
  80. package/dist/src/digest/render-blocks.d.ts +38 -0
  81. package/dist/src/digest/render-blocks.d.ts.map +1 -0
  82. package/dist/src/digest/render-blocks.js +378 -0
  83. package/dist/src/digest/render-blocks.js.map +1 -0
  84. package/dist/src/digest/render-html.d.ts +22 -0
  85. package/dist/src/digest/render-html.d.ts.map +1 -0
  86. package/dist/src/digest/render-html.js +505 -0
  87. package/dist/src/digest/render-html.js.map +1 -0
  88. package/dist/src/digest/render-md.d.ts +18 -0
  89. package/dist/src/digest/render-md.d.ts.map +1 -0
  90. package/dist/src/digest/render-md.js +126 -0
  91. package/dist/src/digest/render-md.js.map +1 -0
  92. package/dist/src/digest/render.d.ts +47 -0
  93. package/dist/src/digest/render.d.ts.map +1 -0
  94. package/dist/src/digest/render.js +126 -0
  95. package/dist/src/digest/render.js.map +1 -0
  96. package/dist/src/digest/reports.d.ts +26 -0
  97. package/dist/src/digest/reports.d.ts.map +1 -0
  98. package/dist/src/digest/reports.js +70 -0
  99. package/dist/src/digest/reports.js.map +1 -0
  100. package/dist/src/digest/scenes.d.ts +92 -0
  101. package/dist/src/digest/scenes.d.ts.map +1 -0
  102. package/dist/src/digest/scenes.js +182 -0
  103. package/dist/src/digest/scenes.js.map +1 -0
  104. package/dist/src/digest/schema-base.d.ts +31 -0
  105. package/dist/src/digest/schema-base.d.ts.map +1 -0
  106. package/dist/src/digest/schema-base.js +19 -0
  107. package/dist/src/digest/schema-base.js.map +1 -0
  108. package/dist/src/digest/schema.d.ts +12886 -0
  109. package/dist/src/digest/schema.d.ts.map +1 -0
  110. package/dist/src/digest/schema.js +215 -0
  111. package/dist/src/digest/schema.js.map +1 -0
  112. package/dist/src/digest/server.d.ts +36 -0
  113. package/dist/src/digest/server.d.ts.map +1 -0
  114. package/dist/src/digest/server.js +278 -0
  115. package/dist/src/digest/server.js.map +1 -0
  116. package/dist/src/digest/slice.d.ts +20 -0
  117. package/dist/src/digest/slice.d.ts.map +1 -0
  118. package/dist/src/digest/slice.js +48 -0
  119. package/dist/src/digest/slice.js.map +1 -0
  120. package/dist/src/digest/store.d.ts +67 -0
  121. package/dist/src/digest/store.d.ts.map +1 -0
  122. package/dist/src/digest/store.js +152 -0
  123. package/dist/src/digest/store.js.map +1 -0
  124. package/dist/src/digest/svg.d.ts +74 -0
  125. package/dist/src/digest/svg.d.ts.map +1 -0
  126. package/dist/src/digest/svg.js +415 -0
  127. package/dist/src/digest/svg.js.map +1 -0
  128. package/dist/src/digest/templates.d.ts +38 -0
  129. package/dist/src/digest/templates.d.ts.map +1 -0
  130. package/dist/src/digest/templates.js +125 -0
  131. package/dist/src/digest/templates.js.map +1 -0
  132. package/dist/src/digest/translate.d.ts +46 -0
  133. package/dist/src/digest/translate.d.ts.map +1 -0
  134. package/dist/src/digest/translate.js +50 -0
  135. package/dist/src/digest/translate.js.map +1 -0
  136. package/dist/src/digest/viewer-js.d.ts +4 -0
  137. package/dist/src/digest/viewer-js.d.ts.map +1 -0
  138. package/dist/src/digest/viewer-js.js +735 -0
  139. package/dist/src/digest/viewer-js.js.map +1 -0
  140. package/dist/src/digest/viewer-logic.d.ts +46 -0
  141. package/dist/src/digest/viewer-logic.d.ts.map +1 -0
  142. package/dist/src/digest/viewer-logic.js +89 -0
  143. package/dist/src/digest/viewer-logic.js.map +1 -0
  144. package/dist/src/digest/ytdlp.d.ts +34 -0
  145. package/dist/src/digest/ytdlp.d.ts.map +1 -0
  146. package/dist/src/digest/ytdlp.js +64 -0
  147. package/dist/src/digest/ytdlp.js.map +1 -0
  148. package/dist/src/mcp/schemas.d.ts +48 -0
  149. package/dist/src/mcp/schemas.d.ts.map +1 -1
  150. package/dist/src/mcp/schemas.js +25 -0
  151. package/dist/src/mcp/schemas.js.map +1 -1
  152. package/dist/src/mcp/server.d.ts.map +1 -1
  153. package/dist/src/mcp/server.js +7 -1
  154. package/dist/src/mcp/server.js.map +1 -1
  155. package/dist/src/mcp/tools/digest-passthrough.d.ts +3 -0
  156. package/dist/src/mcp/tools/digest-passthrough.d.ts.map +1 -0
  157. package/dist/src/mcp/tools/digest-passthrough.js +151 -0
  158. package/dist/src/mcp/tools/digest-passthrough.js.map +1 -0
  159. package/package.json +1 -1
  160. package/plugin/.codex-plugin/plugin.json +1 -1
  161. package/plugin/.mcp.json +1 -1
  162. package/plugin/mcp.json +1 -1
  163. package/plugin/role-agents/video-summarizer/AGENT.md +208 -108
  164. package/plugin/skills/_shared/proactive-routing.md +2 -1
  165. package/plugin/skills/research/references/design.md +1 -1
  166. package/plugin/skills/video-digest/SKILL.md +430 -0
  167. package/plugin/skills/video-digest/templates/background.md +15 -0
  168. package/plugin/skills/video-digest/templates/blog.md +16 -0
  169. package/plugin/skills/video-digest/templates/comments.md +19 -0
  170. package/plugin/skills/video-digest/templates/counterpoints.md +15 -0
  171. package/plugin/skills/video-digest/templates/flashcards.md +15 -0
  172. package/plugin/skills/video-digest/templates/interview.md +15 -0
  173. package/plugin/skills/video-digest/templates/meeting.md +15 -0
  174. package/plugin/skills/video-digest/templates/mindmap.md +16 -0
  175. package/plugin/skills/video-digest/templates/quiz.md +16 -0
  176. package/plugin/skills/video-digest/templates/quotes.md +16 -0
  177. package/plugin/skills/video-digest/templates/resources.md +14 -0
  178. package/plugin/skills/video-digest/templates/timestamps.md +14 -0
  179. package/plugin/skills/video-digest/templates/visual.md +22 -0
@@ -0,0 +1,126 @@
1
+ import { frameFileName, shotFileName } 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
+ // 뷰어와 같이 장면이 있으면 첫 문장 프레임 대신 장면을 싣는다
45
+ const shots = (sub.shots ?? []).filter((x) => opts.frameExists?.(shotFileName(x.sec)));
46
+ for (const shot of shots) {
47
+ // 개행이 남으면 이미지 문법이 깨져 다음 줄이 따로 해석된다
48
+ const alt = shot.caption
49
+ .replace(/\s+/g, ' ')
50
+ .trim()
51
+ .replace(/[\\[\]<>!]/g, (ch) => `\\${ch}`);
52
+ out.push(`![${alt}](${framesHref}/${shotFileName(shot.sec)})`, '');
53
+ const url = watchUrlAt(transcript.meta, shot.sec);
54
+ const at = formatTime(shot.sec, long);
55
+ out.push(`*${mdInline(shot.caption)}* ${url ? `[${at}](${url})` : at}${cite(shot.cite)}`, '');
56
+ }
57
+ const frame = frameFileName(sub.frameSec);
58
+ if (shots.length === 0 && opts.frameExists?.(frame))
59
+ out.push(`![${formatTime(sub.frameSec, long)}](${framesHref}/${frame})`, '');
60
+ for (const b of sub.bullets)
61
+ out.push(...bullet(b, 0));
62
+ out.push('');
63
+ });
64
+ });
65
+ if (keywords.length > 0) {
66
+ out.push('## 키워드', '');
67
+ for (const k of keywords)
68
+ out.push(`- **${k.term}**: ${plain(k.explanation)}${cite(k.firstCite)}`);
69
+ out.push('');
70
+ }
71
+ if (skipped.length > 0) {
72
+ out.push('## 요약에서 뺀 구간', '');
73
+ for (const s of skipped) {
74
+ out.push(`- ${formatTime(at(s.from), long)}~ 문장 ${s.from + 1}-${s.to + 1}: ${plain(s.reason)}`);
75
+ }
76
+ out.push('');
77
+ }
78
+ return finish(out);
79
+ }
80
+ /** 확장 리포트 한 벌을 마크다운으로. 머리말은 요약 마크다운과 같다 */
81
+ export function renderExtensionMarkdown(ext, transcript) {
82
+ const cite = mdCiter(transcript);
83
+ const out = mdHeader(transcript);
84
+ out.push(`> 리포트: ${mdInline(ext.title)}`, '');
85
+ const time = mdTimer(transcript);
86
+ for (const b of ext.blocks)
87
+ out.push(...blockMarkdown(b, { cite, time }));
88
+ return finish(out);
89
+ }
90
+ function mdCiter(transcript) {
91
+ const { meta, segments } = transcript;
92
+ const long = meta.durationSec >= 3600;
93
+ const at = (i) => segments[i]?.startSec ?? 0;
94
+ return (c) => {
95
+ const url = watchUrlAt(meta, at(c.key));
96
+ return url ? ` [[${c.key + 1}]](${url})` : ` [${c.key + 1} ${formatTime(at(c.key), long)}]`;
97
+ };
98
+ }
99
+ function mdTimer({ meta, segments }) {
100
+ const long = meta.durationSec >= 3600;
101
+ return (c) => {
102
+ const sec = segments[c.key]?.startSec ?? 0;
103
+ const url = watchUrlAt(meta, sec);
104
+ return url ? `[${formatTime(sec, long)}](${url})` : formatTime(sec, long);
105
+ };
106
+ }
107
+ function mdHeader({ meta }) {
108
+ const long = meta.durationSec >= 3600;
109
+ const out = [`# ${meta.title}`, ''];
110
+ const facts = [
111
+ meta.channel,
112
+ meta.publishedAt,
113
+ formatTime(meta.durationSec, long),
114
+ CAPTION_LABEL[meta.captionSource],
115
+ ].filter(Boolean);
116
+ out.push(`> ${facts.join(' | ')}`);
117
+ if (meta.source === 'youtube' && meta.url)
118
+ out.push(`> ${meta.url}`);
119
+ out.push('');
120
+ return out;
121
+ }
122
+ const finish = (out) => `${out
123
+ .join('\n')
124
+ .replace(/\n{3,}/g, '\n\n')
125
+ .trimEnd()}\n`;
126
+ //# sourceMappingURL=render-md.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-md.js","sourceRoot":"","sources":["../../../src/digest/render-md.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ import { type DigestIssue } from './lint.js';
2
+ import { type Digest, type Transcript } from './schema.js';
3
+ import { DigestStore } from './store.js';
4
+ import { type DigestTemplate } from './templates.js';
5
+ export interface RenderFilesOptions {
6
+ repoRoot: string;
7
+ inlineFrames?: boolean;
8
+ /** 아는 템플릿. lint가 확장의 블록을 대조하고 뷰어의 "+ 더 만들기" 메뉴가 이 목록을 띄운다 */
9
+ templates?: DigestTemplate[];
10
+ }
11
+ export interface RenderFilesResult {
12
+ slug: string;
13
+ htmlPath: string;
14
+ mdPath: string;
15
+ warnings: string[];
16
+ }
17
+ /** 렌더할 파일을 못 읽었다. code는 ges_digest validate가 내는 코드와 같다 */
18
+ export declare class DigestLoadError extends Error {
19
+ readonly code: 'TRANSCRIPT_NOT_FOUND' | 'TRANSCRIPT_INVALID' | 'DIGEST_NOT_FOUND' | 'DIGEST_NOT_JSON' | 'INVALID_DIGEST' | 'REPORT_NOT_FOUND';
20
+ constructor(code: 'TRANSCRIPT_NOT_FOUND' | 'TRANSCRIPT_INVALID' | 'DIGEST_NOT_FOUND' | 'DIGEST_NOT_JSON' | 'INVALID_DIGEST' | 'REPORT_NOT_FOUND', message: string);
21
+ }
22
+ /** lint 오류가 남아 렌더를 막았다. 호출하는 쪽이 오류 전체를 구조로 받게 issues를 싣는다 */
23
+ export declare class DigestLintError extends Error {
24
+ readonly issues: DigestIssue[];
25
+ constructor(issues: DigestIssue[]);
26
+ }
27
+ /** lint 오류가 남은 요약은 출처가 엉뚱한 문장을 가리킬 수 있어 렌더하지 않는다 */
28
+ export declare function loadForRender(store: DigestStore, slug: string, templates?: DigestTemplate[]): {
29
+ transcript: Transcript;
30
+ digest: Digest;
31
+ };
32
+ /** digest.html과 digest.md를 한 번에 쓴다. 둘 다 같은 digest.json에서 나온다 */
33
+ export declare function renderToFiles(slug: string, opts: RenderFilesOptions): RenderFilesResult;
34
+ /**
35
+ * 마크다운만 원하는 자리에 쓴다. out이 없으면 저장 폴더에 쓴다.
36
+ * 첫 리포트는 digest.md, 다른 리포트와 확장은 digest.<key>.md다. render가 digest.md를 덮어쓰니 이름을 나눈다.
37
+ * 확장 키에는 늘 .이 들어가고 리포트 키에는 없어서 report 하나로 둘 다 받는다
38
+ */
39
+ export declare function exportMarkdown(slug: string, opts: {
40
+ repoRoot: string;
41
+ out?: string;
42
+ report?: string;
43
+ }): {
44
+ slug: string;
45
+ mdPath: string;
46
+ };
47
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../../src/digest/render.ts"],"names":[],"mappings":"AAKA,OAAO,EAA0B,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAIrE,OAAO,EAAe,KAAK,MAAM,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAe,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAElE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,cAAc,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,0DAA0D;AAC1D,qBAAa,eAAgB,SAAQ,KAAK;IAEtC,QAAQ,CAAC,IAAI,EACT,sBAAsB,GACtB,oBAAoB,GACpB,kBAAkB,GAClB,iBAAiB,GACjB,gBAAgB,GAChB,kBAAkB;gBANb,IAAI,EACT,sBAAsB,GACtB,oBAAoB,GACpB,kBAAkB,GAClB,iBAAiB,GACjB,gBAAgB,GAChB,kBAAkB,EACtB,OAAO,EAAE,MAAM;CAKlB;AAED,6DAA6D;AAC7D,qBAAa,eAAgB,SAAQ,KAAK;IAC5B,QAAQ,CAAC,MAAM,EAAE,WAAW,EAAE;gBAArB,MAAM,EAAE,WAAW,EAAE;CAO3C;AAED,oDAAoD;AACpD,wBAAgB,aAAa,CAC3B,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,MAAM,EACZ,SAAS,CAAC,EAAE,cAAc,EAAE,GAC3B;IAAE,UAAU,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CA6B5C;AAED,gEAAgE;AAChE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB,GAAG,iBAAiB,CAiBvF;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GACxD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CA6ClC"}
@@ -0,0 +1,126 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { dirname, join, relative, resolve, sep } from 'node:path';
3
+ import { extensionKey } from './blocks.js';
4
+ import { commentsMap } from './comments.js';
5
+ import { sceneSecs } from './scenes.js';
6
+ import { lintDigest, lintErrors } from './lint.js';
7
+ import { renderDigestHtml } from './render-html.js';
8
+ import { renderDigestMarkdown, renderExtensionMarkdown } from './render-md.js';
9
+ import { findReport, reportKey } from './reports.js';
10
+ import { parseDigest } from './schema.js';
11
+ import { DigestStore } from './store.js';
12
+ import { templateMap } from './templates.js';
13
+ /** 렌더할 파일을 못 읽었다. code는 ges_digest validate가 내는 코드와 같다 */
14
+ export class DigestLoadError extends Error {
15
+ code;
16
+ constructor(code, message) {
17
+ super(message);
18
+ this.code = code;
19
+ this.name = 'DigestLoadError';
20
+ }
21
+ }
22
+ /** lint 오류가 남아 렌더를 막았다. 호출하는 쪽이 오류 전체를 구조로 받게 issues를 싣는다 */
23
+ export class DigestLintError extends Error {
24
+ issues;
25
+ constructor(issues) {
26
+ super(`validate 오류 ${issues.length}개가 남아 있어 렌더하지 않았어요.\n` +
27
+ issues.map((i) => `${i.code} ${i.path}: ${i.message}`).join('\n'));
28
+ this.issues = issues;
29
+ this.name = 'DigestLintError';
30
+ }
31
+ }
32
+ /** lint 오류가 남은 요약은 출처가 엉뚱한 문장을 가리킬 수 있어 렌더하지 않는다 */
33
+ export function loadForRender(store, slug, templates) {
34
+ const t = store.readTranscript(slug);
35
+ if (!t.ok)
36
+ throw new DigestLoadError(t.code, t.message);
37
+ const raw = store.readDigestFile(slug);
38
+ if (raw.kind === 'missing') {
39
+ throw new DigestLoadError('DIGEST_NOT_FOUND', `digest.json이 없어요. 먼저 요약을 써 주세요: ${slug}`);
40
+ }
41
+ if (raw.kind === 'invalid-json') {
42
+ throw new DigestLoadError('DIGEST_NOT_JSON', `digest.json을 JSON으로 못 읽었어요: ${raw.message}`);
43
+ }
44
+ const parsed = parseDigest(raw.value);
45
+ if (!parsed.ok)
46
+ throw new DigestLoadError('INVALID_DIGEST', parsed.error.message);
47
+ const errors = lintErrors(lintDigest(parsed.value, t.transcript, {
48
+ templates: templates ? templateMap(templates) : undefined,
49
+ // 댓글이나 후보 장면 파일을 나중에 지웠거나 깨졌어도 이미 합친 리포트는 그린다.
50
+ // 있을 때만 대조하고 없을 때 막는 일은 merge와 validate가 맡는다
51
+ comments: commentsMap(store, slug) ?? undefined,
52
+ scenes: sceneSecs(store, slug) ?? undefined,
53
+ }));
54
+ if (errors.length > 0)
55
+ throw new DigestLintError(errors);
56
+ return { transcript: t.transcript, digest: parsed.value };
57
+ }
58
+ /** digest.html과 digest.md를 한 번에 쓴다. 둘 다 같은 digest.json에서 나온다 */
59
+ export function renderToFiles(slug, opts) {
60
+ const store = new DigestStore(opts.repoRoot);
61
+ const { transcript, digest } = loadForRender(store, slug, opts.templates);
62
+ const framesDir = store.framesDir(slug);
63
+ const frameExists = (file) => existsSync(join(framesDir, file));
64
+ const { html, warnings } = renderDigestHtml(digest, transcript, {
65
+ frameExists,
66
+ inlineFrames: opts.inlineFrames,
67
+ templates: opts.templates,
68
+ loadFrame: (file) => (frameExists(file) ? readFileSync(join(framesDir, file)) : null),
69
+ });
70
+ return {
71
+ slug,
72
+ htmlPath: store.saveHtml(slug, html),
73
+ mdPath: store.saveMd(slug, renderDigestMarkdown(digest, transcript, { frameExists })),
74
+ warnings,
75
+ };
76
+ }
77
+ /**
78
+ * 마크다운만 원하는 자리에 쓴다. out이 없으면 저장 폴더에 쓴다.
79
+ * 첫 리포트는 digest.md, 다른 리포트와 확장은 digest.<key>.md다. render가 digest.md를 덮어쓰니 이름을 나눈다.
80
+ * 확장 키에는 늘 .이 들어가고 리포트 키에는 없어서 report 하나로 둘 다 받는다
81
+ */
82
+ export function exportMarkdown(slug, opts) {
83
+ const store = new DigestStore(opts.repoRoot);
84
+ const { transcript, digest } = loadForRender(store, slug);
85
+ const ext = opts.report
86
+ ? digest.extensions.find((x) => extensionKey(x) === opts.report)
87
+ : undefined;
88
+ if (ext) {
89
+ const md = renderExtensionMarkdown(ext, transcript);
90
+ const mdPath = opts.out
91
+ ? resolve(opts.out)
92
+ : join(store.dir(slug), `digest.${extensionKey(ext)}.md`);
93
+ mkdirSync(dirname(mdPath), { recursive: true });
94
+ writeFileSync(mdPath, md);
95
+ return { slug, mdPath };
96
+ }
97
+ const report = opts.report ? findReport(digest, opts.report) : digest.reports[0];
98
+ if (!report) {
99
+ const keys = [
100
+ ...digest.reports.map((r) => reportKey(r.options)),
101
+ ...digest.extensions.map(extensionKey),
102
+ ];
103
+ throw new DigestLoadError('REPORT_NOT_FOUND', `리포트 ${opts.report}가 없어요. 있는 리포트: ${keys.join(', ')}`);
104
+ }
105
+ const framesDir = store.framesDir(slug);
106
+ const frameExists = (file) => existsSync(join(framesDir, file));
107
+ if (!opts.out) {
108
+ const md = renderDigestMarkdown(digest, transcript, { frameExists, report });
109
+ if (report === digest.reports[0])
110
+ return { slug, mdPath: store.saveMd(slug, md) };
111
+ const mdPath = join(store.dir(slug), `digest.${reportKey(report.options)}.md`);
112
+ writeFileSync(mdPath, md);
113
+ return { slug, mdPath };
114
+ }
115
+ const mdPath = resolve(opts.out);
116
+ // 다른 폴더에 쓰면 frames/ 상대 경로가 깨지니 그 파일 기준으로 다시 잡는다
117
+ const framesHref = relative(dirname(mdPath), framesDir)
118
+ .split(sep)
119
+ .map(encodeURIComponent)
120
+ .join('/');
121
+ const md = renderDigestMarkdown(digest, transcript, { frameExists, framesHref, report });
122
+ mkdirSync(dirname(mdPath), { recursive: true });
123
+ writeFileSync(mdPath, md);
124
+ return { slug, mdPath };
125
+ }
126
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../../../src/digest/render.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { Digest, Report, ReportOptions, Segment } from './schema.js';
2
+ export declare const LENGTH_LABEL: Record<ReportOptions['length'], string>;
3
+ export declare const DIFFICULTY_LABEL: Record<ReportOptions['difficulty'], string>;
4
+ /**
5
+ * 리포트를 가르는 키. 뷰어 툴바와 merge가 이 값으로 같은 리포트인지 본다.
6
+ * prompt는 그대로 넣으면 길고 HTML 속성에 실리므로 해시 앞 8자만 쓴다
7
+ */
8
+ export declare function reportKey(options: ReportOptions): string;
9
+ /** 구간 리포트가 덮어야 하는 문장 번호. startSec이 [시작, 끝)에 드는 문장들이다 */
10
+ export declare function rangeSpan(range: readonly [number, number], segments: readonly Segment[]): {
11
+ from: number;
12
+ to: number;
13
+ } | null;
14
+ /**
15
+ * 툴바의 분량과 난이도 버튼으로 고를 수 있는 리포트인지 본다.
16
+ * 구간이나 프롬프트가 붙었거나 첫 리포트와 언어가 다르면 따로 고르는 리포트다
17
+ */
18
+ export declare function isPreset(options: ReportOptions, base: ReportOptions): boolean;
19
+ export declare function reportLabel(options: ReportOptions, withHours?: boolean): string;
20
+ export declare function findReport(digest: Digest, key: string): Report | undefined;
21
+ /** 같은 키의 리포트가 있으면 그 자리에 바꿔 끼우고 없으면 뒤에 붙인다 */
22
+ export declare function upsertReport(digest: Digest, report: Report): {
23
+ digest: Digest;
24
+ replaced: boolean;
25
+ };
26
+ //# sourceMappingURL=reports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reports.d.ts","sourceRoot":"","sources":["../../../src/digest/reports.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE1E,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE,MAAM,CAIhE,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,aAAa,CAAC,YAAY,CAAC,EAAE,MAAM,CAGxE,CAAC;AAEF;;;GAGG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CASxD;AAED,wDAAwD;AACxD,wBAAgB,SAAS,CACvB,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,QAAQ,EAAE,SAAS,OAAO,EAAE,GAC3B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CASrC;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAE7E;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,aAAa,EAAE,SAAS,UAAQ,GAAG,MAAM,CAU7E;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE1E;AAED,6CAA6C;AAC7C,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GACb;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAOvC"}
@@ -0,0 +1,70 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { formatTime } from './format.js';
3
+ export const LENGTH_LABEL = {
4
+ short: '짧게',
5
+ default: '기본',
6
+ long: '길게',
7
+ };
8
+ export const DIFFICULTY_LABEL = {
9
+ normal: '보통',
10
+ easy: '쉽게',
11
+ };
12
+ /**
13
+ * 리포트를 가르는 키. 뷰어 툴바와 merge가 이 값으로 같은 리포트인지 본다.
14
+ * prompt는 그대로 넣으면 길고 HTML 속성에 실리므로 해시 앞 8자만 쓴다
15
+ */
16
+ export function reportKey(options) {
17
+ const parts = [options.length, options.difficulty, options.lang];
18
+ if (options.range) {
19
+ parts.push(`r${options.range[0]}-${options.range[1]}`);
20
+ }
21
+ if (options.prompt) {
22
+ parts.push(`p${createHash('sha256').update(options.prompt).digest('hex').slice(0, 8)}`);
23
+ }
24
+ return parts.join('-');
25
+ }
26
+ /** 구간 리포트가 덮어야 하는 문장 번호. startSec이 [시작, 끝)에 드는 문장들이다 */
27
+ export function rangeSpan(range, segments) {
28
+ let from = -1;
29
+ let to = -1;
30
+ for (const s of segments) {
31
+ if (s.startSec < range[0] || s.startSec >= range[1])
32
+ continue;
33
+ if (from < 0)
34
+ from = s.i;
35
+ to = s.i;
36
+ }
37
+ return from < 0 ? null : { from, to };
38
+ }
39
+ /**
40
+ * 툴바의 분량과 난이도 버튼으로 고를 수 있는 리포트인지 본다.
41
+ * 구간이나 프롬프트가 붙었거나 첫 리포트와 언어가 다르면 따로 고르는 리포트다
42
+ */
43
+ export function isPreset(options, base) {
44
+ return !options.range && !options.prompt && options.lang === base.lang;
45
+ }
46
+ export function reportLabel(options, withHours = false) {
47
+ const parts = [`${LENGTH_LABEL[options.length]}, ${DIFFICULTY_LABEL[options.difficulty]}`];
48
+ if (options.range) {
49
+ parts.push(`${formatTime(options.range[0], withHours)}~${formatTime(options.range[1], withHours)}`);
50
+ }
51
+ parts.push(options.lang);
52
+ if (options.prompt)
53
+ parts.push('요청 반영');
54
+ return parts.join(' / ');
55
+ }
56
+ export function findReport(digest, key) {
57
+ return digest.reports.find((r) => reportKey(r.options) === key);
58
+ }
59
+ /** 같은 키의 리포트가 있으면 그 자리에 바꿔 끼우고 없으면 뒤에 붙인다 */
60
+ export function upsertReport(digest, report) {
61
+ const key = reportKey(report.options);
62
+ const at = digest.reports.findIndex((r) => reportKey(r.options) === key);
63
+ const reports = [...digest.reports];
64
+ if (at >= 0)
65
+ reports[at] = report;
66
+ else
67
+ reports.push(report);
68
+ return { digest: { ...digest, reports }, replaced: at >= 0 };
69
+ }
70
+ //# sourceMappingURL=reports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reports.js","sourceRoot":"","sources":["../../../src/digest/reports.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,92 @@
1
+ import { z } from 'zod';
2
+ import { type OcrFile } from './ocr.js';
3
+ import { DigestStore } from './store.js';
4
+ import { type RunCommand } from './ytdlp.js';
5
+ /**
6
+ * 소섹션 shots가 고를 수 있는 후보 장면. 슬라이드가 넘어가거나 화면이 바뀐 시각이다.
7
+ * 요약 작성자는 장면을 보지 못하니 OCR 글자 앞부분을 붙여 무엇이 화면에 있었는지 짐작하게 한다
8
+ */
9
+ export declare const SCENES_FILE = "scenes.json";
10
+ /** 이보다 글자가 적은 화면은 사람 얼굴이나 자막 한 줄일 때가 많아 슬라이드로 보지 않는다 */
11
+ export declare const SCENE_MIN_CHARS = 40;
12
+ /** 앞 항목과 단어가 이 비율보다 많이 달라야 다른 화면으로 본다 */
13
+ export declare const SCENE_CHANGE_RATIO = 0.5;
14
+ /** ffmpeg 장면 전환 점수 문턱 */
15
+ export declare const SCENE_THRESHOLD = 0.4;
16
+ export declare const SCENE_MIN_GAP_SEC = 3;
17
+ export declare const MAX_SCENES = 300;
18
+ export declare const scenesFileSchema: z.ZodObject<{
19
+ source: z.ZodEnum<["ocr", "scene"]>;
20
+ items: z.ZodArray<z.ZodObject<{
21
+ sec: z.ZodNumber;
22
+ i: z.ZodNumber;
23
+ text: z.ZodOptional<z.ZodString>;
24
+ }, "strip", z.ZodTypeAny, {
25
+ i: number;
26
+ sec: number;
27
+ text?: string | undefined;
28
+ }, {
29
+ i: number;
30
+ sec: number;
31
+ text?: string | undefined;
32
+ }>, "many">;
33
+ /** 상한을 넘어 솎았을 때만 있다. 솎기 전 후보 수다 */
34
+ total: z.ZodOptional<z.ZodNumber>;
35
+ }, "strip", z.ZodTypeAny, {
36
+ source: "ocr" | "scene";
37
+ items: {
38
+ i: number;
39
+ sec: number;
40
+ text?: string | undefined;
41
+ }[];
42
+ total?: number | undefined;
43
+ }, {
44
+ source: "ocr" | "scene";
45
+ items: {
46
+ i: number;
47
+ sec: number;
48
+ text?: string | undefined;
49
+ }[];
50
+ total?: number | undefined;
51
+ }>;
52
+ export type ScenesFile = z.infer<typeof scenesFileSchema>;
53
+ export type SceneItem = ScenesFile['items'][number];
54
+ /** cur의 단어 가운데 prev에 없는 비율. 0이면 같은 화면, 1이면 전혀 다른 화면이다 */
55
+ export declare function textChange(prev: string, cur: string): number;
56
+ export declare function textHead(text: string, max?: number): string;
57
+ /** 글자가 넉넉하고 앞 항목과 절반 넘게 다른 화면만 남긴다. 슬라이드가 넘어간 자리다 */
58
+ export declare function ocrScenes(ocr: OcrFile): SceneItem[];
59
+ /** 앞 후보와 minGap초 안에 붙은 전환은 버린다. 카메라가 연달아 바뀌는 자리는 하나만 남는다 */
60
+ export declare function thinSceneTimes(secs: number[], minGap?: number): number[];
61
+ /** 시간순 후보가 max개를 넘으면 처음과 끝을 포함해 고르게 솎는다. 앞에서 자르면 영상 뒷부분 장면이 통째로 빠진다 */
62
+ export declare function spreadEvenly<T>(items: readonly T[], max?: number): T[];
63
+ export type ScenesRead = {
64
+ kind: 'missing';
65
+ } | {
66
+ kind: 'invalid';
67
+ message: string;
68
+ } | {
69
+ kind: 'ok';
70
+ value: ScenesFile;
71
+ };
72
+ export declare function readScenes(store: DigestStore, slug: string): ScenesRead;
73
+ /** lint가 받는 꼴. 파일이 없거나 깨졌으면 null이다 */
74
+ export declare function sceneSecs(store: DigestStore, slug: string): number[] | null;
75
+ export interface ScenesOptions {
76
+ repoRoot: string;
77
+ run?: RunCommand;
78
+ }
79
+ export interface ScenesResult {
80
+ path: string;
81
+ source: ScenesFile['source'];
82
+ items: number;
83
+ /** 솎기 전 후보 수. items보다 크면 상한에 걸려 고르게 솎은 것이다 */
84
+ total: number;
85
+ truncated: boolean;
86
+ }
87
+ /**
88
+ * 후보 장면을 scenes.json에 둔다. ocr.json이 있으면 글자로 고르고 없으면 ffmpeg 장면 전환을 쓴다.
89
+ * 장면 전환만으로는 슬라이드인지 얼굴인지 못 가려서 글자가 있으면 글자를 먼저 쓴다
90
+ */
91
+ export declare function pickScenes(slug: string, opts: ScenesOptions): Promise<ScenesResult>;
92
+ //# sourceMappingURL=scenes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scenes.d.ts","sourceRoot":"","sources":["../../../src/digest/scenes.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAA2B,KAAK,OAAO,EAAE,MAAM,UAAU,CAAC;AACjE,OAAO,EAAE,WAAW,EAAW,MAAM,YAAY,CAAC;AAElD,OAAO,EAA0B,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAErE;;;GAGG;AACH,eAAO,MAAM,WAAW,gBAAgB,CAAC;AAEzC,wDAAwD;AACxD,eAAO,MAAM,eAAe,KAAK,CAAC;AAClC,yCAAyC;AACzC,eAAO,MAAM,kBAAkB,MAAM,CAAC;AACtC,yBAAyB;AACzB,eAAO,MAAM,eAAe,MAAM,CAAC;AAEnC,eAAO,MAAM,iBAAiB,IAAI,CAAC;AACnC,eAAO,MAAM,UAAU,MAAM,CAAC;AAG9B,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;IAW3B,mCAAmC;;;;;;;;;;;;;;;;;;EAEnC,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC1D,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC;AAoBpD,yDAAyD;AACzD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAO5D;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,SAAY,GAAG,MAAM,CAG9D;AAED,qDAAqD;AACrD,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,SAAS,EAAE,CAWnD;AAED,4DAA4D;AAC5D,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,SAAoB,GAAG,MAAM,EAAE,CAOnF;AAED,uEAAuE;AACvE,wBAAgB,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,EAAE,GAAG,SAAa,GAAG,CAAC,EAAE,CAO1E;AAOD,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,UAAU,CAAA;CAAE,CAAC;AAEtC,wBAAgB,UAAU,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,CAYvE;AAED,sCAAsC;AACtC,wBAAgB,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAG3E;AAWD,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,UAAU,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;CACpB;AAcD;;;GAGG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAmCzF"}