@tienne/gestalt 0.99.0 → 0.100.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 (137) hide show
  1. package/CLAUDE.md +1 -1
  2. package/dist/package.json +2 -2
  3. package/dist/plugin/research-sources/amplitude/SOURCE.md +28 -0
  4. package/dist/plugin/research-sources/app-store-reviews/SOURCE.md +25 -0
  5. package/dist/plugin/research-sources/app-store-search/SOURCE.md +25 -0
  6. package/dist/plugin/research-sources/tech-blog-search/SOURCE.md +27 -0
  7. package/dist/plugin/research-sources/uibowl/SOURCE.md +26 -0
  8. package/dist/plugin/research-sources/web-search/SOURCE.md +24 -0
  9. package/dist/plugin/skills/_shared/proactive-routing.md +5 -0
  10. package/dist/plugin/skills/market-scan/SKILL.md +61 -0
  11. package/dist/plugin/skills/research/SKILL.md +438 -0
  12. package/dist/plugin/skills/research/references/design.md +938 -0
  13. package/dist/plugin/skills/research/references/output-template.md +366 -0
  14. package/dist/plugin/skills/research/scripts/pii-vision.swift +327 -0
  15. package/dist/plugin/skills/research/scripts/slide-extract.mjs +223 -0
  16. package/dist/plugin/skills/reverse-plan/SKILL.md +52 -0
  17. package/dist/plugin/skills/screen-ref/SKILL.md +63 -0
  18. package/dist/plugin/skills/ux-research/SKILL.md +51 -0
  19. package/dist/src/core/config.d.ts +3 -0
  20. package/dist/src/core/config.d.ts.map +1 -1
  21. package/dist/src/core/config.js +4 -0
  22. package/dist/src/core/config.js.map +1 -1
  23. package/dist/src/mcp/schemas.d.ts +93 -0
  24. package/dist/src/mcp/schemas.d.ts.map +1 -1
  25. package/dist/src/mcp/schemas.js +110 -0
  26. package/dist/src/mcp/schemas.js.map +1 -1
  27. package/dist/src/mcp/server.d.ts.map +1 -1
  28. package/dist/src/mcp/server.js +11 -1
  29. package/dist/src/mcp/server.js.map +1 -1
  30. package/dist/src/mcp/tools/research-passthrough.d.ts +21 -0
  31. package/dist/src/mcp/tools/research-passthrough.d.ts.map +1 -0
  32. package/dist/src/mcp/tools/research-passthrough.js +566 -0
  33. package/dist/src/mcp/tools/research-passthrough.js.map +1 -0
  34. package/dist/src/research/canvas-view.d.ts +5 -0
  35. package/dist/src/research/canvas-view.d.ts.map +1 -0
  36. package/dist/src/research/canvas-view.js +274 -0
  37. package/dist/src/research/canvas-view.js.map +1 -0
  38. package/dist/src/research/evidence.d.ts +145 -0
  39. package/dist/src/research/evidence.d.ts.map +1 -0
  40. package/dist/src/research/evidence.js +50 -0
  41. package/dist/src/research/evidence.js.map +1 -0
  42. package/dist/src/research/figma-plan.d.ts +80 -0
  43. package/dist/src/research/figma-plan.d.ts.map +1 -0
  44. package/dist/src/research/figma-plan.js +527 -0
  45. package/dist/src/research/figma-plan.js.map +1 -0
  46. package/dist/src/research/figma-script.d.ts +19 -0
  47. package/dist/src/research/figma-script.d.ts.map +1 -0
  48. package/dist/src/research/figma-script.js +363 -0
  49. package/dist/src/research/figma-script.js.map +1 -0
  50. package/dist/src/research/index.d.ts +8 -0
  51. package/dist/src/research/index.d.ts.map +1 -0
  52. package/dist/src/research/index.js +8 -0
  53. package/dist/src/research/index.js.map +1 -0
  54. package/dist/src/research/pii-core.d.ts +16 -0
  55. package/dist/src/research/pii-core.d.ts.map +1 -0
  56. package/dist/src/research/pii-core.js +345 -0
  57. package/dist/src/research/pii-core.js.map +1 -0
  58. package/dist/src/research/pii-dom.d.ts +13 -0
  59. package/dist/src/research/pii-dom.d.ts.map +1 -0
  60. package/dist/src/research/pii-dom.js +208 -0
  61. package/dist/src/research/pii-dom.js.map +1 -0
  62. package/dist/src/research/pii-image.d.ts +71 -0
  63. package/dist/src/research/pii-image.d.ts.map +1 -0
  64. package/dist/src/research/pii-image.js +163 -0
  65. package/dist/src/research/pii-image.js.map +1 -0
  66. package/dist/src/research/pii.d.ts +21 -0
  67. package/dist/src/research/pii.d.ts.map +1 -0
  68. package/dist/src/research/pii.js +47 -0
  69. package/dist/src/research/pii.js.map +1 -0
  70. package/dist/src/research/product-profile.d.ts +108 -0
  71. package/dist/src/research/product-profile.d.ts.map +1 -0
  72. package/dist/src/research/product-profile.js +104 -0
  73. package/dist/src/research/product-profile.js.map +1 -0
  74. package/dist/src/research/render-html.d.ts +203 -0
  75. package/dist/src/research/render-html.d.ts.map +1 -0
  76. package/dist/src/research/render-html.js +901 -0
  77. package/dist/src/research/render-html.js.map +1 -0
  78. package/dist/src/research/schema-base.d.ts +5 -0
  79. package/dist/src/research/schema-base.d.ts.map +1 -0
  80. package/dist/src/research/schema-base.js +10 -0
  81. package/dist/src/research/schema-base.js.map +1 -0
  82. package/dist/src/research/schema.d.ts +5142 -0
  83. package/dist/src/research/schema.d.ts.map +1 -0
  84. package/dist/src/research/schema.js +991 -0
  85. package/dist/src/research/schema.js.map +1 -0
  86. package/dist/src/research/source-create.d.ts +79 -0
  87. package/dist/src/research/source-create.d.ts.map +1 -0
  88. package/dist/src/research/source-create.js +226 -0
  89. package/dist/src/research/source-create.js.map +1 -0
  90. package/dist/src/research/source-registry.d.ts +29 -0
  91. package/dist/src/research/source-registry.d.ts.map +1 -0
  92. package/dist/src/research/source-registry.js +74 -0
  93. package/dist/src/research/source-registry.js.map +1 -0
  94. package/dist/src/research/source-schema.d.ts +158 -0
  95. package/dist/src/research/source-schema.d.ts.map +1 -0
  96. package/dist/src/research/source-schema.js +215 -0
  97. package/dist/src/research/source-schema.js.map +1 -0
  98. package/dist/src/research/source-status.d.ts +40 -0
  99. package/dist/src/research/source-status.d.ts.map +1 -0
  100. package/dist/src/research/source-status.js +109 -0
  101. package/dist/src/research/source-status.js.map +1 -0
  102. package/dist/src/research/store.d.ts +24 -0
  103. package/dist/src/research/store.d.ts.map +1 -0
  104. package/dist/src/research/store.js +60 -0
  105. package/dist/src/research/store.js.map +1 -0
  106. package/dist/src/research/target-view.d.ts +33 -0
  107. package/dist/src/research/target-view.d.ts.map +1 -0
  108. package/dist/src/research/target-view.js +133 -0
  109. package/dist/src/research/target-view.js.map +1 -0
  110. package/dist/src/research/target.d.ts +883 -0
  111. package/dist/src/research/target.d.ts.map +1 -0
  112. package/dist/src/research/target.js +323 -0
  113. package/dist/src/research/target.js.map +1 -0
  114. package/dist/src/research/video-transfer.d.ts +28 -0
  115. package/dist/src/research/video-transfer.d.ts.map +1 -0
  116. package/dist/src/research/video-transfer.js +100 -0
  117. package/dist/src/research/video-transfer.js.map +1 -0
  118. package/package.json +2 -2
  119. package/plugin/.codex-plugin/plugin.json +1 -1
  120. package/plugin/.mcp.json +1 -1
  121. package/plugin/mcp.json +1 -1
  122. package/plugin/research-sources/amplitude/SOURCE.md +28 -0
  123. package/plugin/research-sources/app-store-reviews/SOURCE.md +25 -0
  124. package/plugin/research-sources/app-store-search/SOURCE.md +25 -0
  125. package/plugin/research-sources/tech-blog-search/SOURCE.md +27 -0
  126. package/plugin/research-sources/uibowl/SOURCE.md +26 -0
  127. package/plugin/research-sources/web-search/SOURCE.md +24 -0
  128. package/plugin/skills/_shared/proactive-routing.md +5 -0
  129. package/plugin/skills/market-scan/SKILL.md +61 -0
  130. package/plugin/skills/research/SKILL.md +438 -0
  131. package/plugin/skills/research/references/design.md +938 -0
  132. package/plugin/skills/research/references/output-template.md +366 -0
  133. package/plugin/skills/research/scripts/pii-vision.swift +327 -0
  134. package/plugin/skills/research/scripts/slide-extract.mjs +223 -0
  135. package/plugin/skills/reverse-plan/SKILL.md +52 -0
  136. package/plugin/skills/screen-ref/SKILL.md +63 -0
  137. package/plugin/skills/ux-research/SKILL.md +51 -0
@@ -0,0 +1,566 @@
1
+ import { copyFileSync, existsSync, mkdirSync, readFileSync, realpathSync, rmSync, statSync, writeFileSync, } from 'node:fs';
2
+ import { tmpdir } from 'node:os';
3
+ import { basename, dirname, extname, isAbsolute, join, resolve, sep } from 'node:path';
4
+ import { gestaltPath } from '../../core/home.js';
5
+ import { buildDomRedactionScript } from '../../research/pii-dom.js';
6
+ import { isVisionAvailable, redactImageFile, swiftRunner, } from '../../research/pii-image.js';
7
+ import { redactorFor, seedFor } from '../../research/pii.js';
8
+ import { buildFigmaPlan } from '../../research/figma-plan.js';
9
+ import { buildFigmaFitScript, buildFigmaScript, buildFigmaVerifyScript, } from '../../research/figma-script.js';
10
+ import { renderResearchHtml } from '../../research/render-html.js';
11
+ import { lintResearch, parseResearch } from '../../research/schema.js';
12
+ import { isValidSlug, ResearchStore } from '../../research/store.js';
13
+ import { currentCommit, isStale, listProfiles, loadProfile, productProfileSchema, saveProfile, } from '../../research/product-profile.js';
14
+ import { mergeVideo, splitVideo } from '../../research/video-transfer.js';
15
+ import { ResearchSourceRegistry } from '../../research/source-registry.js';
16
+ import { buildSourceMd, createSourceInputSchema, personalSourceExists, probeSource, SOURCE_EXAMPLE, SOURCE_QUESTIONS, writeSourceMd, } from '../../research/source-create.js';
17
+ import { buildSourceReport, evaluateSource } from '../../research/source-status.js';
18
+ function fail(code, message) {
19
+ return { ok: false, errors: [{ code, message }] };
20
+ }
21
+ export function personalSourcesDir(opts) {
22
+ return opts.gestaltHome
23
+ ? join(opts.gestaltHome, 'research-sources')
24
+ : gestaltPath('research-sources');
25
+ }
26
+ function loadRegistry(opts) {
27
+ const registry = new ResearchSourceRegistry(opts.builtinDir, personalSourcesDir(opts));
28
+ registry.loadAll();
29
+ return registry;
30
+ }
31
+ function handleSources(input, opts) {
32
+ const registry = loadRegistry(opts);
33
+ const report = buildSourceReport(registry.getAll(), input.available ?? {}, opts.env ?? process.env, input.stage);
34
+ const base = { ok: true, action: 'sources', ...report, diagnostics: registry.getDiagnostics() };
35
+ if (!input.name)
36
+ return base;
37
+ const source = registry.getByName(input.name);
38
+ if (!source)
39
+ return fail('SOURCE_NOT_FOUND', `없는 출처: ${input.name}`);
40
+ return {
41
+ ...base,
42
+ detail: {
43
+ name: source.frontmatter.name,
44
+ origin: source.origin,
45
+ access: source.frontmatter.access,
46
+ body: source.body,
47
+ },
48
+ };
49
+ }
50
+ const IMAGE_EXTS = new Set(['.png', '.jpg', '.jpeg', '.webp']);
51
+ const MAX_IMAGE_BYTES = 30 * 1024 * 1024;
52
+ const ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
53
+ async function resolveRunner(opts) {
54
+ if (opts.visionRunner !== undefined)
55
+ return opts.visionRunner ?? undefined;
56
+ if (!opts.visionScript || !existsSync(opts.visionScript))
57
+ return undefined;
58
+ return (await isVisionAvailable()) ? swiftRunner(opts.visionScript) : undefined;
59
+ }
60
+ async function handleRedactImage(input, cwd, opts) {
61
+ if (!input.slug || !isValidSlug(input.slug)) {
62
+ return fail('INVALID_SLUG', 'slug는 소문자, 숫자, -만 쓴다(최대 64자)');
63
+ }
64
+ if (!input.captureId || !ID_PATTERN.test(input.captureId)) {
65
+ return fail('INVALID_CAPTURE_ID', 'captureId는 소문자, 숫자, -만 쓴다(최대 64자)');
66
+ }
67
+ if (!input.imagePath)
68
+ return fail('IMAGE_PATH_REQUIRED', 'imagePath가 필요하다');
69
+ const repoRoot = resolve(cwd, input.repoRoot ?? '.');
70
+ const source = isAbsolute(input.imagePath) ? input.imagePath : resolve(repoRoot, input.imagePath);
71
+ if (!IMAGE_EXTS.has(extname(source).toLowerCase())) {
72
+ return fail('UNSUPPORTED_IMAGE', 'png, jpg, webp 파일만 받는다');
73
+ }
74
+ if (!existsSync(source) || !statSync(source).isFile()) {
75
+ return fail('IMAGE_NOT_FOUND', `이미지가 없다: ${input.imagePath}`);
76
+ }
77
+ if (statSync(source).size > MAX_IMAGE_BYTES) {
78
+ return fail('IMAGE_TOO_LARGE', '30MB를 넘는 이미지는 받지 않는다');
79
+ }
80
+ const store = new ResearchStore(repoRoot);
81
+ const capturesDir = store.capturesDir(input.slug);
82
+ mkdirSync(capturesDir, { recursive: true });
83
+ const result = await redactImageFile({
84
+ inputPath: source,
85
+ outputBase: join(capturesDir, input.captureId),
86
+ pii: redactorFor(input.slug),
87
+ run: await resolveRunner(opts),
88
+ });
89
+ const warnings = [...result.warnings];
90
+ if (result.pii === 'none') {
91
+ warnings.push('none은 탐지기가 돌았고 0건이라는 뜻이다. 라벨 없는 이름처럼 못 잡는 것이 있어서 개인정보가 없다는 보증은 아니다.');
92
+ }
93
+ const sourceIsOutput = resolve(result.outputPath) === resolve(source);
94
+ if (input.deleteSource && result.pii !== 'unchecked' && !sourceIsOutput) {
95
+ rmSync(source, { force: true });
96
+ }
97
+ else if (result.pii === 'replaced' && !sourceIsOutput) {
98
+ warnings.push(`원본(${basename(source)})에 개인정보가 그대로 남아 있다. 필요 없으면 지운다.`);
99
+ }
100
+ return {
101
+ ok: true,
102
+ action: 'redact_image',
103
+ captureId: input.captureId,
104
+ path: `captures/${basename(result.outputPath)}`,
105
+ pii: result.pii,
106
+ counts: result.counts,
107
+ warnings,
108
+ };
109
+ }
110
+ function handleRedactScript(input, opts) {
111
+ if (!input.slug || !isValidSlug(input.slug)) {
112
+ return fail('INVALID_SLUG', 'slug는 소문자, 숫자, -만 쓴다(최대 64자)');
113
+ }
114
+ const script = buildDomRedactionScript(seedFor(input.slug), { observe: input.observe });
115
+ const dir = opts.tmpDir ?? tmpdir();
116
+ mkdirSync(dir, { recursive: true });
117
+ const scriptPath = join(dir, `ges-research-${input.slug}-dom-redact.js`);
118
+ writeFileSync(scriptPath, `() => ${script}`, { mode: 0o600 });
119
+ return {
120
+ ok: true,
121
+ action: 'redact_script',
122
+ scriptPath,
123
+ bytes: Buffer.byteLength(script),
124
+ usage: [
125
+ 'chrome-devtools: evaluate_script에 sourcePath로 이 파일을 주고 format은 function으로 둔다. 캡처 직전에 실행한다.',
126
+ 'aside: inline을 true로 다시 불러 본문을 받고 page.addInitScript로 한 번 심는다. observe를 켜면 이동한 뒤에도 자동으로 돈다.',
127
+ '반환값은 바꾼 개수뿐이다. 실행 뒤 개수가 모두 0이어도 pii를 none으로 적지 말고 화면을 눈으로 확인한 뒤 정한다.',
128
+ '닫힌 shadow root, 다른 출처의 iframe, canvas 속 글자는 못 바꾼다. 화면에 있으면 unchecked로 남긴다.',
129
+ ],
130
+ ...(input.inline ? { script } : {}),
131
+ };
132
+ }
133
+ const MAX_IR_BYTES = 5 * 1024 * 1024;
134
+ const MIME_BY_EXT = {
135
+ '.png': 'image/png',
136
+ '.jpg': 'image/jpeg',
137
+ '.jpeg': 'image/jpeg',
138
+ '.webp': 'image/webp',
139
+ };
140
+ /** irPath가 있으면 그 파일을, 없으면 slug의 저장본을 읽어 검증한다 */
141
+ function loadIr(input, repoRoot) {
142
+ let raw;
143
+ let fromFile = false;
144
+ if (input.irPath) {
145
+ const path = isAbsolute(input.irPath) ? input.irPath : resolve(repoRoot, input.irPath);
146
+ if (!existsSync(path) || !statSync(path).isFile()) {
147
+ return fail('IR_NOT_FOUND', `research.json이 없다: ${input.irPath}`);
148
+ }
149
+ if (statSync(path).size > MAX_IR_BYTES)
150
+ return fail('IR_TOO_LARGE', 'research.json이 5MB를 넘는다');
151
+ try {
152
+ raw = JSON.parse(readFileSync(path, 'utf-8'));
153
+ }
154
+ catch (err) {
155
+ return fail('IR_NOT_JSON', `JSON을 읽지 못했다: ${err instanceof Error ? err.message : String(err)}`);
156
+ }
157
+ fromFile = true;
158
+ }
159
+ else {
160
+ if (!input.slug || !isValidSlug(input.slug)) {
161
+ return fail('INVALID_SLUG', 'irPath나 올바른 slug가 필요하다');
162
+ }
163
+ const path = new ResearchStore(repoRoot).jsonPath(input.slug);
164
+ if (!existsSync(path))
165
+ return fail('IR_NOT_FOUND', `저장된 리서치가 없다: ${input.slug}`);
166
+ try {
167
+ raw = JSON.parse(readFileSync(path, 'utf-8'));
168
+ }
169
+ catch (err) {
170
+ return fail('IR_NOT_JSON', `JSON을 읽지 못했다: ${err instanceof Error ? err.message : String(err)}`);
171
+ }
172
+ }
173
+ const parsed = parseResearch(raw);
174
+ if (!parsed.ok) {
175
+ return {
176
+ ok: false,
177
+ errors: parsed.error.issues.map((m) => ({ code: 'INVALID_RESEARCH', message: m })),
178
+ };
179
+ }
180
+ if (input.slug && input.slug !== parsed.value.meta.slug) {
181
+ return fail('SLUG_MISMATCH', `slug(${input.slug})와 meta.slug(${parsed.value.meta.slug})가 다르다`);
182
+ }
183
+ return { research: parsed.value, fromFile };
184
+ }
185
+ function handleValidate(input, cwd) {
186
+ const repoRoot = resolve(cwd, input.repoRoot ?? '.');
187
+ const loaded = loadIr(input, repoRoot);
188
+ if ('ok' in loaded)
189
+ return loaded;
190
+ return {
191
+ ok: true,
192
+ action: 'validate',
193
+ slug: loaded.research.meta.slug,
194
+ warnings: lintResearch(loaded.research),
195
+ };
196
+ }
197
+ /** 저장 폴더 밖으로 나가는 심볼릭 링크는 따라가지 않는다 */
198
+ function resolveInside(researchDir, rel) {
199
+ const full = join(researchDir, rel);
200
+ if (!existsSync(full))
201
+ return null;
202
+ const real = realpathSync(full);
203
+ return real.startsWith(realpathSync(researchDir) + sep) ? real : null;
204
+ }
205
+ function imageLoader(researchDir) {
206
+ return (rel) => {
207
+ const mime = MIME_BY_EXT[extname(rel).toLowerCase()];
208
+ const real = mime ? resolveInside(researchDir, rel) : null;
209
+ return mime && real ? { mime, data: readFileSync(real) } : null;
210
+ };
211
+ }
212
+ function handleRender(input, cwd) {
213
+ const repoRoot = resolve(cwd, input.repoRoot ?? '.');
214
+ const loaded = loadIr(input, repoRoot);
215
+ if ('ok' in loaded)
216
+ return loaded;
217
+ const { research } = loaded;
218
+ const slug = research.meta.slug;
219
+ const store = new ResearchStore(repoRoot);
220
+ const jsonPath = store.save(research);
221
+ const loadImage = imageLoader(store.dir(slug));
222
+ const warnings = lintResearch(research).map((w) => `${w.path}: ${w.message}`);
223
+ const priv = renderResearchHtml(research, { audience: 'private', loadImage });
224
+ if (!priv.ok)
225
+ return fail(priv.code, priv.message);
226
+ warnings.push(...priv.warnings);
227
+ const htmlPath = store.saveHtml(slug, 'private', priv.html);
228
+ const shared = renderResearchHtml(research, { audience: 'shared', loadImage });
229
+ let sharedHtmlPath = null;
230
+ let sharedBlocked;
231
+ if (shared.ok) {
232
+ sharedHtmlPath = store.saveHtml(slug, 'shared', shared.html);
233
+ }
234
+ else {
235
+ sharedBlocked = { message: shared.message, captureIds: shared.captureIds };
236
+ }
237
+ const audience = input.audience ?? 'private';
238
+ if (audience === 'shared' && !sharedHtmlPath) {
239
+ return {
240
+ ok: false,
241
+ errors: [{ code: 'SHARE_BLOCKED', message: sharedBlocked?.message ?? '' }],
242
+ captureIds: sharedBlocked?.captureIds,
243
+ htmlPath,
244
+ };
245
+ }
246
+ return {
247
+ ok: true,
248
+ action: 'render',
249
+ slug,
250
+ jsonPath,
251
+ htmlPath,
252
+ sharedHtmlPath,
253
+ openPath: audience === 'shared' ? sharedHtmlPath : htmlPath,
254
+ ...(sharedBlocked ? { sharedBlocked } : {}),
255
+ warnings,
256
+ };
257
+ }
258
+ const FIGMA_IMAGE_MIMES = new Set(['image/png', 'image/jpeg', 'image/gif']);
259
+ const MAX_FIGMA_IMAGE_BYTES = 8 * 1024 * 1024;
260
+ const MAX_INLINE_SCRIPT_BYTES = 200 * 1024;
261
+ const USE_FIGMA_CODE_LIMIT = 50_000;
262
+ function handleFigmaPlan(input, cwd) {
263
+ const repoRoot = resolve(cwd, input.repoRoot ?? '.');
264
+ const loaded = loadIr(input, repoRoot);
265
+ if ('ok' in loaded)
266
+ return loaded;
267
+ const { research } = loaded;
268
+ const slug = research.meta.slug;
269
+ const audience = input.audience ?? 'private';
270
+ const built = buildFigmaPlan(research, { audience, layout: input.figmaLayout });
271
+ if (!built.ok) {
272
+ return {
273
+ ok: false,
274
+ errors: [{ code: built.code, message: built.message }],
275
+ captureIds: built.captureIds,
276
+ };
277
+ }
278
+ const store = new ResearchStore(repoRoot);
279
+ store.save(research);
280
+ const warnings = [];
281
+ let images;
282
+ if (input.withImages) {
283
+ images = {};
284
+ const loadImage = imageLoader(store.dir(slug));
285
+ let total = 0;
286
+ for (const img of built.plan.images) {
287
+ const data = loadImage(img.path);
288
+ if (!data) {
289
+ warnings.push(`${img.id}: 이미지를 읽지 못해 회색 칸으로 둔다`);
290
+ }
291
+ else if (!FIGMA_IMAGE_MIMES.has(data.mime)) {
292
+ warnings.push(`${img.id}: Figma가 받는 형식(PNG, JPEG, GIF)이 아니라 회색 칸으로 둔다`);
293
+ }
294
+ else if (total + data.data.length > MAX_FIGMA_IMAGE_BYTES) {
295
+ warnings.push(`${img.id}: 이미지 합계 8MB를 넘어 회색 칸으로 둔다`);
296
+ }
297
+ else {
298
+ total += data.data.length;
299
+ images[img.id] = { base64: data.data.toString('base64') };
300
+ }
301
+ }
302
+ }
303
+ const runner = input.runner ?? 'function';
304
+ const script = buildFigmaScript(built.plan, images, runner);
305
+ const verifyScript = buildFigmaVerifyScript('__PAGE_ID__', built.plan.images.map((i) => i.id), runner);
306
+ const fitScript = buildFigmaFitScript('__PAGE_ID__', runner);
307
+ const planPath = store.saveFile(slug, 'figma-plan.json', JSON.stringify(built.plan, null, 2));
308
+ const scriptPath = store.saveFile(slug, 'figma-render.js', script);
309
+ const verifyPath = store.saveFile(slug, 'figma-verify.js', verifyScript);
310
+ const fitPath = store.saveFile(slug, 'figma-fit.js', fitScript);
311
+ if (runner === 'body' && script.length > USE_FIGMA_CODE_LIMIT) {
312
+ warnings.push(`스크립트가 ${script.length}자라 use_figma 코드 한도(5만 자)를 넘는다. withImages를 끄고 이미지는 upload_assets로 올린다`);
313
+ }
314
+ const researchDir = store.dir(slug);
315
+ const imageFiles = built.plan.images.flatMap((img) => {
316
+ const contentType = MIME_BY_EXT[extname(img.path).toLowerCase()];
317
+ const real = contentType && FIGMA_IMAGE_MIMES.has(contentType)
318
+ ? resolveInside(researchDir, img.path)
319
+ : null;
320
+ return real && contentType ? [{ id: img.id, path: real, contentType }] : [];
321
+ });
322
+ const inline = input.inline === true;
323
+ if (inline && Buffer.byteLength(script) > MAX_INLINE_SCRIPT_BYTES) {
324
+ warnings.push('스크립트가 200KB를 넘어 본문을 싣지 않는다. withImages를 끄거나 파일로 읽는 실행기를 쓴다');
325
+ }
326
+ const attach = inline && Buffer.byteLength(script) <= MAX_INLINE_SCRIPT_BYTES;
327
+ return {
328
+ ok: true,
329
+ action: 'figma_plan',
330
+ slug,
331
+ audience,
332
+ layout: built.plan.layout,
333
+ planPath,
334
+ scriptPath,
335
+ verifyScriptPath: verifyPath,
336
+ fitScriptPath: fitPath,
337
+ runner,
338
+ imageCount: built.plan.images.length,
339
+ embeddedImages: images ? Object.keys(images).length : 0,
340
+ imageFiles,
341
+ ...(attach ? { script, verifyScript, fitScript } : {}),
342
+ warnings,
343
+ usage: runner === 'body'
344
+ ? '편집 권한이 있는 Figma 파일에서 scriptPath의 본문을 use_figma로 실행한다. 반환한 imageSlots의 nodeId를 같은 순서로 upload_assets의 nodeIds에 넣고, 받은 주소마다 imageFiles의 파일을 올린다. 다 올렸으면 pageId를 __PAGE_ID__ 자리에 넣어 fitScriptPath로 높이와 캡션을 맞추고 verifyScriptPath로 이미지가 남았는지 확인한다'
345
+ : '편집 권한이 있는 Figma 파일에서 scriptPath의 함수를 실행한다. 반환한 pageId를 verify 스크립트의 __PAGE_ID__ 자리에 넣어 이미지가 남았는지 확인한다. 이미지 업로드가 끝나기 전에 탭을 닫지 않는다',
346
+ };
347
+ }
348
+ async function handleCreateSource(input, opts) {
349
+ if (!input.source) {
350
+ return {
351
+ ok: true,
352
+ action: 'create_source',
353
+ needsInput: true,
354
+ questions: SOURCE_QUESTIONS,
355
+ example: SOURCE_EXAMPLE,
356
+ usage: '사용자에게 questions를 물어 답을 모은 뒤 source에 같은 꼴로 담아 다시 부른다. 비밀번호와 키 값은 받지 않고 환경변수 이름이나 MCP 서버 이름만 받는다',
357
+ };
358
+ }
359
+ const parsed = createSourceInputSchema.safeParse(input.source);
360
+ if (!parsed.success) {
361
+ const issues = parsed.error.issues.map((i) => `${i.path.join('.') || '(root)'}: ${i.message}`);
362
+ return { ok: false, errors: issues.map((message) => ({ code: 'INVALID_SOURCE', message })) };
363
+ }
364
+ const built = buildSourceMd(parsed.data);
365
+ if (!built.ok)
366
+ return fail('INVALID_SOURCE', built.error.message);
367
+ const name = built.value.definition.frontmatter.name;
368
+ const personalDir = personalSourcesDir(opts);
369
+ const before = loadRegistry(opts).getByName(name);
370
+ const exists = personalSourceExists(personalDir, name);
371
+ if ((exists || before) && !input.overwrite) {
372
+ const where = exists ? '개인 디렉토리에 같은 이름이 있다' : '같은 이름의 내장 출처가 있다';
373
+ return fail('SOURCE_EXISTS', `${where}(${name}). 덮어쓰려면 overwrite를 켜고, 아니면 다른 이름을 쓴다`);
374
+ }
375
+ const written = writeSourceMd(personalDir, name, built.value.content);
376
+ if (!written.ok)
377
+ return fail('WRITE_FAILED', written.error.message);
378
+ const registry = loadRegistry(opts);
379
+ const loaded = registry.getByName(name);
380
+ if (!loaded || loaded.origin !== 'personal') {
381
+ return fail('NOT_LOADED', `쓴 파일을 다시 읽지 못했다: ${registry
382
+ .getDiagnostics()
383
+ .map((d) => d.message)
384
+ .join('; ')}`);
385
+ }
386
+ const trial = evaluateSource(loaded, input.available ?? {}, opts.env ?? process.env);
387
+ const probes = [];
388
+ for (const access of loaded.frontmatter.access) {
389
+ if (access.kind === 'http' && access.probe) {
390
+ probes.push(await probeSource(access.probe, opts.probe));
391
+ }
392
+ }
393
+ const warnings = [];
394
+ if (trial.status === 'skipped')
395
+ warnings.push(`시험 결과 ${trial.note}. 연결하고 sources로 다시 확인한다`);
396
+ for (const p of probes)
397
+ if (!p.ok)
398
+ warnings.push(`probe 실패(${p.url}): ${p.note}`);
399
+ if (before)
400
+ warnings.push(`같은 이름의 내장 출처를 개인 출처가 덮어쓴다(${name})`);
401
+ return {
402
+ ok: true,
403
+ action: 'create_source',
404
+ name,
405
+ path: written.value,
406
+ overridesBuiltin: before?.origin === 'builtin',
407
+ trial: { ...trial, probes },
408
+ warnings,
409
+ };
410
+ }
411
+ /** 한 리서치 폴더의 캡처 이미지를 다른 폴더로 복사한다. 이미 있으면 건너뛰고 원본이 없으면 목록에 올린다 */
412
+ function copyCaptureImages(from, to, paths) {
413
+ const copied = [];
414
+ const missing = [];
415
+ for (const rel of paths) {
416
+ const src = resolveInside(from, rel);
417
+ if (!src) {
418
+ missing.push(rel);
419
+ continue;
420
+ }
421
+ const dst = join(to, rel);
422
+ if (existsSync(dst))
423
+ continue;
424
+ mkdirSync(dirname(dst), { recursive: true });
425
+ copyFileSync(src, dst);
426
+ copied.push(rel);
427
+ }
428
+ return { copied, missing };
429
+ }
430
+ function loadBySlug(input, repoRoot, slug) {
431
+ return loadIr({ ...input, slug, irPath: undefined }, repoRoot);
432
+ }
433
+ function handleSplitVideo(input, cwd) {
434
+ const repoRoot = resolve(cwd, input.repoRoot ?? '.');
435
+ if (!input.slug)
436
+ return fail('INVALID_SLUG', 'split_video는 본문 slug가 필요하다');
437
+ const loaded = loadBySlug(input, repoRoot, input.slug);
438
+ if ('ok' in loaded)
439
+ return loaded;
440
+ const videoSlug = input.videoSlug ?? `${input.slug}-video`;
441
+ if (!isValidSlug(videoSlug))
442
+ return fail('INVALID_SLUG', `videoSlug가 올바르지 않다: ${videoSlug}`);
443
+ const store = new ResearchStore(repoRoot);
444
+ if (existsSync(store.jsonPath(videoSlug)) && !input.overwrite) {
445
+ return fail('TARGET_EXISTS', `이미 있는 영상 문서다: ${videoSlug}. 덮어쓰려면 overwrite를 켠다`);
446
+ }
447
+ const result = splitVideo(loaded.research, videoSlug, { removeFromMain: input.removeFromMain });
448
+ if (!result.ok)
449
+ return fail('SPLIT_FAILED', result.error.message);
450
+ const images = copyCaptureImages(store.dir(input.slug), store.dir(videoSlug), result.value.imagePaths);
451
+ const videoJson = store.save(result.value.video);
452
+ if (input.removeFromMain)
453
+ store.save(result.value.main);
454
+ return {
455
+ ok: true,
456
+ action: 'split_video',
457
+ slug: input.slug,
458
+ videoSlug,
459
+ videoJsonPath: videoJson,
460
+ removedFromMain: input.removeFromMain === true,
461
+ clips: result.value.video.video?.clips.length ?? 0,
462
+ findings: result.value.video.video?.findings.length ?? 0,
463
+ copiedImages: images.copied,
464
+ missingImages: images.missing,
465
+ warnings: lintResearch(result.value.video).map((w) => `${w.path}: ${w.message}`),
466
+ };
467
+ }
468
+ function handleMergeVideo(input, cwd) {
469
+ const repoRoot = resolve(cwd, input.repoRoot ?? '.');
470
+ if (!input.slug)
471
+ return fail('INVALID_SLUG', 'merge_video는 본문 slug가 필요하다');
472
+ const videoSlug = input.videoSlug ?? `${input.slug}-video`;
473
+ if (!isValidSlug(videoSlug))
474
+ return fail('INVALID_SLUG', `videoSlug가 올바르지 않다: ${videoSlug}`);
475
+ const main = loadBySlug(input, repoRoot, input.slug);
476
+ if ('ok' in main)
477
+ return main;
478
+ const videoDoc = loadBySlug(input, repoRoot, videoSlug);
479
+ if ('ok' in videoDoc)
480
+ return videoDoc;
481
+ const result = mergeVideo(main.research, videoDoc.research);
482
+ if (!result.ok)
483
+ return fail('MERGE_FAILED', result.error.message);
484
+ const store = new ResearchStore(repoRoot);
485
+ const images = copyCaptureImages(store.dir(videoSlug), store.dir(input.slug), result.value.imagePaths);
486
+ const jsonPath = store.save(result.value.main);
487
+ return {
488
+ ok: true,
489
+ action: 'merge_video',
490
+ slug: input.slug,
491
+ videoSlug,
492
+ jsonPath,
493
+ clips: result.value.main.video?.clips.length ?? 0,
494
+ findings: result.value.main.video?.findings.length ?? 0,
495
+ copiedImages: images.copied,
496
+ missingImages: images.missing,
497
+ warnings: lintResearch(result.value.main).map((w) => `${w.path}: ${w.message}`),
498
+ };
499
+ }
500
+ function handleProfile(input, cwd) {
501
+ const repoRoot = resolve(input.repoRoot ?? cwd);
502
+ if (input.profile) {
503
+ const parsed = productProfileSchema.safeParse(input.profile);
504
+ if (!parsed.success) {
505
+ return fail('INVALID_PROFILE', parsed.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`).join('\n'));
506
+ }
507
+ const path = saveProfile(repoRoot, parsed.data);
508
+ return {
509
+ ok: true,
510
+ action: 'profile',
511
+ product: parsed.data.product,
512
+ path,
513
+ events: parsed.data.events.length,
514
+ };
515
+ }
516
+ if (!input.product) {
517
+ return { ok: true, action: 'profile', products: listProfiles(repoRoot) };
518
+ }
519
+ if (!isValidSlug(input.product)) {
520
+ return fail('INVALID_PRODUCT', '제품 이름은 소문자, 숫자, -만 쓴다(최대 64자)');
521
+ }
522
+ const profile = loadProfile(repoRoot, input.product);
523
+ if (!profile) {
524
+ return fail('PROFILE_NOT_FOUND', `저장된 프로필이 없다: ${input.product}. 제품 코드를 훑어서 profile로 저장한다`);
525
+ }
526
+ const head = currentCommit(repoRoot, profile.repo);
527
+ const stale = isStale(profile, head);
528
+ return {
529
+ ok: true,
530
+ action: 'profile',
531
+ profile,
532
+ ...(head ? { head } : {}),
533
+ stale: stale ?? null,
534
+ ...(stale
535
+ ? {
536
+ notice: `프로필은 ${profile.commit}에서 훑었는데 지금 HEAD는 ${head}다. 이벤트가 바뀌었을 수 있다`,
537
+ }
538
+ : {}),
539
+ ...(stale === undefined ? { notice: `제품 코드(${profile.repo})의 HEAD를 못 읽었다` } : {}),
540
+ };
541
+ }
542
+ export async function handleResearchPassthrough(input, cwd = process.cwd(), opts) {
543
+ switch (input.action) {
544
+ case 'sources':
545
+ return handleSources(input, opts);
546
+ case 'redact_image':
547
+ return handleRedactImage(input, cwd, opts);
548
+ case 'redact_script':
549
+ return handleRedactScript(input, opts);
550
+ case 'validate':
551
+ return handleValidate(input, cwd);
552
+ case 'render':
553
+ return handleRender(input, cwd);
554
+ case 'figma_plan':
555
+ return handleFigmaPlan(input, cwd);
556
+ case 'create_source':
557
+ return handleCreateSource(input, opts);
558
+ case 'split_video':
559
+ return handleSplitVideo(input, cwd);
560
+ case 'merge_video':
561
+ return handleMergeVideo(input, cwd);
562
+ case 'profile':
563
+ return handleProfile(input, cwd);
564
+ }
565
+ }
566
+ //# sourceMappingURL=research-passthrough.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"research-passthrough.js","sourceRoot":"","sources":["../../../../src/mcp/tools/research-passthrough.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /** h2마다 섹션을 끊어 보드로 감싼다. 첫 h2 앞의 제목과 안내는 첫 보드로 묶는다 */
2
+ export declare function boardMarkup(parts: string[], firstTitle: string): string;
3
+ export declare const CANVAS_CSS = "\n:root{--canvas:#e7e5e4}\n@media (prefers-color-scheme:dark){:root:not([data-theme=\"light\"]){--canvas:#000}}\n:root[data-theme=\"dark\"]{--canvas:#000}\n.board-label{display:none}\n.cv-bar{position:fixed;top:12px;right:12px;z-index:10;display:flex;gap:4px;align-items:center;background:var(--card);border:1px solid var(--line);border-radius:10px;padding:4px;box-shadow:0 2px 8px rgba(0,0,0,.08);font-size:13px}\n.cv-bar select{max-width:140px}\n.cv-bar button,.cv-bar select{font:inherit;color:var(--fg);background:transparent;border:0;border-radius:6px;padding:4px 8px;cursor:pointer}\n.cv-bar button:hover,.cv-bar select:hover{background:var(--chip)}\n.cv-bar .cv-only{display:none}\n.cv-zoom{min-width:48px;text-align:center;color:var(--muted)}\nbody.canvas{overflow:hidden;height:100vh;background:var(--canvas);overscroll-behavior:none}\nbody.canvas .cv-bar .cv-only{display:inline-block}\nbody.canvas main{position:absolute;left:0;top:0;max-width:none;margin:0;padding:0;transform-origin:0 0}\nbody.canvas .board{position:absolute;width:1080px;background:var(--card);border-radius:12px;padding:28px 40px 40px;box-shadow:0 1px 3px rgba(0,0,0,.08),0 0 0 1px var(--line)}\nbody.canvas .board>h2:first-of-type{border-top:0;margin-top:0;padding-top:0}\nbody.canvas .wrap{overflow:visible}\nbody.canvas .board-label{display:block;position:absolute;bottom:100%;left:0;padding-bottom:calc(8px / var(--z,1));font-size:calc(13px / var(--z,1));line-height:1.2;color:var(--muted);white-space:nowrap;max-width:100%;overflow:hidden;text-overflow:ellipsis;cursor:pointer;user-select:none}\nbody.canvas .board-label:hover{color:var(--accent)}\nbody.canvas.grab,body.canvas.grab *{cursor:grab!important;user-select:none}\nbody.canvas.grabbing,body.canvas.grabbing *{cursor:grabbing!important}\n@media print{.cv-bar{display:none}}\n";
4
+ export declare const CANVAS_JS = "\n(function(){\n var main = document.querySelector('main');\n var boards = Array.prototype.slice.call(document.querySelectorAll('.board'));\n if (!main || boards.length === 0) return;\n var body = document.body;\n var GAP_X = 160, GAP_Y = 200, COLS = 4, MIN_Z = 0.05, MAX_Z = 4;\n var view = { x: 0, y: 0, z: 1 };\n var bounds = { w: 0, h: 0 };\n var on = false, laidOut = false;\n\n var bar = document.createElement('div');\n bar.className = 'cv-bar';\n bar.innerHTML =\n '<button type=\"button\" data-act=\"toggle\"></button>' +\n '<select class=\"cv-only\" data-act=\"jump\" title=\"\uBCF4\uB4DC\uB85C \uC774\uB3D9\"></select>' +\n '<button type=\"button\" class=\"cv-only\" data-act=\"out\" title=\"\uCD95\uC18C (-)\">\u2212</button>' +\n '<span class=\"cv-only cv-zoom\"></span>' +\n '<button type=\"button\" class=\"cv-only\" data-act=\"in\" title=\"\uD655\uB300 (+)\">+</button>' +\n '<button type=\"button\" class=\"cv-only\" data-act=\"fit\" title=\"\uC804\uCCB4 \uBCF4\uAE30 (Shift+1)\">\uC804\uCCB4</button>';\n body.appendChild(bar);\n var toggleBtn = bar.querySelector('[data-act=\"toggle\"]');\n var jump = bar.querySelector('[data-act=\"jump\"]');\n var zoomText = bar.querySelector('.cv-zoom');\n jump.innerHTML = '<option value=\"\">\uBCF4\uB4DC</option>' + boards.map(function (b, i) {\n var o = document.createElement('option');\n o.value = String(i);\n o.textContent = b.dataset.title || '';\n return o.outerHTML;\n }).join('');\n\n boards.forEach(function (b, i) {\n var label = document.createElement('div');\n label.className = 'board-label';\n label.textContent = b.dataset.title || '';\n label.dataset.board = String(i);\n b.insertBefore(label, b.firstChild);\n });\n\n function apply() {\n main.style.transform = 'translate(' + view.x + 'px,' + view.y + 'px) scale(' + view.z + ')';\n main.style.setProperty('--z', String(view.z));\n zoomText.textContent = Math.round(view.z * 100) + '%';\n }\n\n // \uCCAB \uBCF4\uB4DC(\uC81C\uBAA9\uACFC \uC548\uB0B4)\uB294 \uB9E8 \uC704\uC5D0 \uD63C\uC790 \uB450\uACE0 \uB098\uBA38\uC9C0\uB294 \uAC00\uC7A5 \uC9E7\uC740 \uC5F4\uC5D0 \uCC28\uB840\uB85C \uCC44\uC6B4\uB2E4\n function layout() {\n var head = boards[0], rest = boards.slice(1);\n head.style.left = '0px';\n head.style.top = '0px';\n var top = head.offsetHeight + GAP_Y;\n var colW = 0;\n rest.forEach(function (b) { colW = Math.max(colW, b.offsetWidth); });\n var cols = [];\n for (var c = 0; c < Math.min(COLS, rest.length); c++) cols.push(top);\n rest.forEach(function (b) {\n var i = cols.indexOf(Math.min.apply(null, cols));\n b.style.left = i * (colW + GAP_X) + 'px';\n b.style.top = cols[i] + 'px';\n cols[i] += b.offsetHeight + GAP_Y;\n });\n bounds = {\n w: Math.max(head.offsetWidth, cols.length * (colW + GAP_X) - GAP_X),\n h: Math.max.apply(null, cols.concat(top)) - GAP_Y,\n };\n }\n\n function widen() {\n boards.forEach(function (b) {\n b.style.width = '';\n var need = 0;\n b.querySelectorAll('table').forEach(function (t) { need = Math.max(need, t.scrollWidth); });\n if (need > 1000) b.style.width = (need + 80) + 'px';\n });\n }\n\n function zoomAt(z, cx, cy) {\n z = Math.min(MAX_Z, Math.max(MIN_Z, z));\n view.x = cx - (cx - view.x) * (z / view.z);\n view.y = cy - (cy - view.y) * (z / view.z);\n view.z = z;\n apply();\n }\n\n function frame(x, y, w, h, maxZ) {\n var pad = 64, top = 56;\n var vw = window.innerWidth - pad * 2, vh = window.innerHeight - pad - top;\n var z = Math.min(maxZ, vw / w, vh / h);\n view.z = Math.max(MIN_Z, z);\n view.x = pad + (vw - w * view.z) / 2 - x * view.z;\n view.y = top + Math.max(0, (vh - h * view.z) / 2) - y * view.z;\n apply();\n }\n\n function fit() { frame(0, 0, bounds.w, bounds.h, 1); }\n\n function focusBoard(b) { frame(b.offsetLeft, b.offsetTop, b.offsetWidth, Math.min(b.offsetHeight, 900), 1); }\n\n function focusEl(el) {\n var b = el.closest('.board');\n if (!b) return;\n var r = el.getBoundingClientRect();\n var z = Math.max(view.z, 0.8);\n var wx = (r.left - view.x) / view.z, wy = (r.top - view.y) / view.z, ww = r.width / view.z;\n view.z = z;\n view.x = window.innerWidth / 2 - (wx + ww / 2) * z;\n view.y = window.innerHeight / 3 - wy * z;\n apply();\n }\n\n var relayoutQueued = false;\n function relayout() {\n if (!on || relayoutQueued) return;\n relayoutQueued = true;\n requestAnimationFrame(function () { relayoutQueued = false; if (on) layout(); });\n }\n var ro = typeof ResizeObserver === 'function' ? new ResizeObserver(relayout) : null;\n\n function enter() {\n on = true;\n body.classList.add('canvas');\n toggleBtn.textContent = '\uBB38\uC11C\uB85C \uBCF4\uAE30';\n document.querySelectorAll('img[loading=\"lazy\"]').forEach(function (img) { img.loading = 'eager'; });\n widen();\n layout();\n if (ro) boards.forEach(function (b) { ro.observe(b); });\n if (!laidOut) { fit(); laidOut = true; } else apply();\n history.replaceState(null, '', '#canvas');\n }\n\n function leave() {\n on = false;\n if (ro) ro.disconnect();\n body.classList.remove('canvas', 'grab', 'grabbing');\n toggleBtn.textContent = '\uCE94\uBC84\uC2A4\uB85C \uBCF4\uAE30';\n main.style.transform = '';\n boards.forEach(function (b) { b.style.left = ''; b.style.top = ''; b.style.width = ''; });\n history.replaceState(null, '', location.pathname + location.search);\n }\n\n bar.addEventListener('click', function (ev) {\n var act = ev.target.closest('[data-act]');\n if (!act) return;\n var a = act.dataset.act;\n var cx = window.innerWidth / 2, cy = window.innerHeight / 2;\n if (a === 'toggle') { if (on) leave(); else enter(); }\n else if (a === 'in') zoomAt(view.z * 1.25, cx, cy);\n else if (a === 'out') zoomAt(view.z / 1.25, cx, cy);\n else if (a === 'fit') fit();\n });\n jump.addEventListener('change', function () {\n var b = boards[Number(jump.value)];\n if (b) focusBoard(b);\n jump.value = '';\n });\n\n window.addEventListener('wheel', function (ev) {\n if (!on || ev.target.closest('.cv-bar')) return;\n ev.preventDefault();\n if (ev.ctrlKey || ev.metaKey) {\n zoomAt(view.z * Math.exp(-ev.deltaY * 0.01), ev.clientX, ev.clientY);\n } else {\n var dx = ev.deltaX, dy = ev.deltaY;\n if (ev.shiftKey && dx === 0) { dx = dy; dy = 0; }\n view.x -= dx;\n view.y -= dy;\n apply();\n }\n }, { passive: false });\n\n // \uC0AC\uD30C\uB9AC \uD2B8\uB799\uD328\uB4DC \uD540\uCE58\uB294 wheel\uC774 \uC544\uB2C8\uB77C gesture \uC774\uBCA4\uD2B8\uB85C \uC628\uB2E4\n var gestureZ = 1;\n window.addEventListener('gesturestart', function (ev) { if (on) { ev.preventDefault(); gestureZ = view.z; } });\n window.addEventListener('gesturechange', function (ev) {\n if (on) { ev.preventDefault(); zoomAt(gestureZ * ev.scale, ev.clientX, ev.clientY); }\n });\n\n var space = false, drag = null;\n window.addEventListener('keydown', function (ev) {\n if (!on) return;\n var tag = (ev.target.tagName || '').toLowerCase();\n if (tag === 'select' || tag === 'input' || tag === 'textarea') return;\n var cx = window.innerWidth / 2, cy = window.innerHeight / 2;\n if (ev.code === 'Space') { ev.preventDefault(); if (!space) { space = true; body.classList.add('grab'); } }\n else if (ev.shiftKey && ev.code === 'Digit1') fit();\n else if (ev.shiftKey && ev.code === 'Digit0') zoomAt(1, cx, cy);\n else if (ev.key === '+' || ev.key === '=') { ev.preventDefault(); zoomAt(view.z * 1.25, cx, cy); }\n else if (ev.key === '-' || ev.key === '_') { ev.preventDefault(); zoomAt(view.z / 1.25, cx, cy); }\n });\n window.addEventListener('keyup', function (ev) {\n if (ev.code === 'Space') { space = false; body.classList.remove('grab'); }\n });\n\n window.addEventListener('pointerdown', function (ev) {\n if (!on || ev.target.closest('.cv-bar')) return;\n var label = ev.target.closest('.board-label');\n var onBoard = ev.target.closest('.board') && !label;\n if (onBoard && !space && ev.button !== 1) return;\n ev.preventDefault();\n drag = { x: ev.clientX, y: ev.clientY, vx: view.x, vy: view.y, moved: false, label: label };\n body.classList.add('grabbing');\n });\n window.addEventListener('pointermove', function (ev) {\n if (!drag) return;\n var dx = ev.clientX - drag.x, dy = ev.clientY - drag.y;\n if (Math.abs(dx) + Math.abs(dy) > 3) drag.moved = true;\n view.x = drag.vx + dx;\n view.y = drag.vy + dy;\n apply();\n });\n window.addEventListener('pointerup', function () {\n if (!drag) return;\n if (drag.label && !drag.moved) focusBoard(boards[Number(drag.label.dataset.board)]);\n drag = null;\n body.classList.remove('grabbing');\n });\n\n // \uCEA1\uCC98 \uC378\uB124\uC77C\uCC98\uB7FC \uBB38\uC11C \uC548 \uC575\uCEE4\uB294 \uCE94\uBC84\uC2A4\uC5D0\uC11C \uC2A4\uD06C\uB864 \uB300\uC2E0 \uADF8 \uC790\uB9AC\uB85C \uD654\uBA74\uC744 \uC62E\uAE34\uB2E4\n document.addEventListener('click', function (ev) {\n if (!on) return;\n var a = ev.target.closest('a[href^=\"#\"]');\n if (!a) return;\n var id = decodeURIComponent(a.getAttribute('href').slice(1));\n var el = id && document.getElementById(id);\n if (!el) return;\n ev.preventDefault();\n focusEl(el);\n });\n\n window.addEventListener('resize', function () { if (on) apply(); });\n\n toggleBtn.textContent = '\uCE94\uBC84\uC2A4\uB85C \uBCF4\uAE30';\n if (location.hash === '#canvas') enter();\n})();\n";
5
+ //# sourceMappingURL=canvas-view.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvas-view.d.ts","sourceRoot":"","sources":["../../../src/research/canvas-view.ts"],"names":[],"mappings":"AAGA,qDAAqD;AACrD,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CASvE;AAED,eAAO,MAAM,UAAU,myDAsBtB,CAAC;AAGF,eAAO,MAAM,SAAS,i8SA0OrB,CAAC"}