@tienne/gestalt 0.98.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 (175) hide show
  1. package/CLAUDE.md +2 -1
  2. package/README.md +3 -2
  3. package/dist/package.json +2 -2
  4. package/dist/plugin/research-sources/amplitude/SOURCE.md +28 -0
  5. package/dist/plugin/research-sources/app-store-reviews/SOURCE.md +25 -0
  6. package/dist/plugin/research-sources/app-store-search/SOURCE.md +25 -0
  7. package/dist/plugin/research-sources/tech-blog-search/SOURCE.md +27 -0
  8. package/dist/plugin/research-sources/uibowl/SOURCE.md +26 -0
  9. package/dist/plugin/research-sources/web-search/SOURCE.md +24 -0
  10. package/dist/plugin/skills/_shared/proactive-routing.md +5 -0
  11. package/dist/plugin/skills/architecture/SKILL.md +1 -1
  12. package/dist/plugin/skills/market-scan/SKILL.md +61 -0
  13. package/dist/plugin/skills/research/SKILL.md +438 -0
  14. package/dist/plugin/skills/research/references/design.md +938 -0
  15. package/dist/plugin/skills/research/references/output-template.md +366 -0
  16. package/dist/plugin/skills/research/scripts/pii-vision.swift +327 -0
  17. package/dist/plugin/skills/research/scripts/slide-extract.mjs +223 -0
  18. package/dist/plugin/skills/reverse-plan/SKILL.md +52 -0
  19. package/dist/plugin/skills/screen-ref/SKILL.md +63 -0
  20. package/dist/plugin/skills/ux-research/SKILL.md +51 -0
  21. package/dist/src/architecture/validator.d.ts +17 -1
  22. package/dist/src/architecture/validator.d.ts.map +1 -1
  23. package/dist/src/architecture/validator.js +61 -45
  24. package/dist/src/architecture/validator.js.map +1 -1
  25. package/dist/src/core/config.d.ts +17 -0
  26. package/dist/src/core/config.d.ts.map +1 -1
  27. package/dist/src/core/config.js +4 -0
  28. package/dist/src/core/config.js.map +1 -1
  29. package/dist/src/core/source-location.d.ts +32 -0
  30. package/dist/src/core/source-location.d.ts.map +1 -0
  31. package/dist/src/core/source-location.js +87 -0
  32. package/dist/src/core/source-location.js.map +1 -0
  33. package/dist/src/execute/orchestrators/evolution.d.ts +3 -1
  34. package/dist/src/execute/orchestrators/evolution.d.ts.map +1 -1
  35. package/dist/src/execute/orchestrators/evolution.js +4 -2
  36. package/dist/src/execute/orchestrators/evolution.js.map +1 -1
  37. package/dist/src/execute/orchestrators/execution.d.ts +3 -1
  38. package/dist/src/execute/orchestrators/execution.d.ts.map +1 -1
  39. package/dist/src/execute/orchestrators/execution.js +4 -2
  40. package/dist/src/execute/orchestrators/execution.js.map +1 -1
  41. package/dist/src/execute/passthrough-engine.d.ts +2 -1
  42. package/dist/src/execute/passthrough-engine.d.ts.map +1 -1
  43. package/dist/src/execute/passthrough-engine.js +3 -3
  44. package/dist/src/execute/passthrough-engine.js.map +1 -1
  45. package/dist/src/execute/prompts.d.ts.map +1 -1
  46. package/dist/src/execute/prompts.js +20 -2
  47. package/dist/src/execute/prompts.js.map +1 -1
  48. package/dist/src/mcp/schemas.d.ts +93 -0
  49. package/dist/src/mcp/schemas.d.ts.map +1 -1
  50. package/dist/src/mcp/schemas.js +110 -0
  51. package/dist/src/mcp/schemas.js.map +1 -1
  52. package/dist/src/mcp/server.d.ts.map +1 -1
  53. package/dist/src/mcp/server.js +12 -2
  54. package/dist/src/mcp/server.js.map +1 -1
  55. package/dist/src/mcp/tools/execute/evaluate.js +1 -1
  56. package/dist/src/mcp/tools/execute/evaluate.js.map +1 -1
  57. package/dist/src/mcp/tools/research-passthrough.d.ts +21 -0
  58. package/dist/src/mcp/tools/research-passthrough.d.ts.map +1 -0
  59. package/dist/src/mcp/tools/research-passthrough.js +566 -0
  60. package/dist/src/mcp/tools/research-passthrough.js.map +1 -0
  61. package/dist/src/memory/project-memory-store.d.ts +7 -0
  62. package/dist/src/memory/project-memory-store.d.ts.map +1 -1
  63. package/dist/src/memory/project-memory-store.js +17 -0
  64. package/dist/src/memory/project-memory-store.js.map +1 -1
  65. package/dist/src/research/canvas-view.d.ts +5 -0
  66. package/dist/src/research/canvas-view.d.ts.map +1 -0
  67. package/dist/src/research/canvas-view.js +274 -0
  68. package/dist/src/research/canvas-view.js.map +1 -0
  69. package/dist/src/research/evidence.d.ts +145 -0
  70. package/dist/src/research/evidence.d.ts.map +1 -0
  71. package/dist/src/research/evidence.js +50 -0
  72. package/dist/src/research/evidence.js.map +1 -0
  73. package/dist/src/research/figma-plan.d.ts +80 -0
  74. package/dist/src/research/figma-plan.d.ts.map +1 -0
  75. package/dist/src/research/figma-plan.js +527 -0
  76. package/dist/src/research/figma-plan.js.map +1 -0
  77. package/dist/src/research/figma-script.d.ts +19 -0
  78. package/dist/src/research/figma-script.d.ts.map +1 -0
  79. package/dist/src/research/figma-script.js +363 -0
  80. package/dist/src/research/figma-script.js.map +1 -0
  81. package/dist/src/research/index.d.ts +8 -0
  82. package/dist/src/research/index.d.ts.map +1 -0
  83. package/dist/src/research/index.js +8 -0
  84. package/dist/src/research/index.js.map +1 -0
  85. package/dist/src/research/pii-core.d.ts +16 -0
  86. package/dist/src/research/pii-core.d.ts.map +1 -0
  87. package/dist/src/research/pii-core.js +345 -0
  88. package/dist/src/research/pii-core.js.map +1 -0
  89. package/dist/src/research/pii-dom.d.ts +13 -0
  90. package/dist/src/research/pii-dom.d.ts.map +1 -0
  91. package/dist/src/research/pii-dom.js +208 -0
  92. package/dist/src/research/pii-dom.js.map +1 -0
  93. package/dist/src/research/pii-image.d.ts +71 -0
  94. package/dist/src/research/pii-image.d.ts.map +1 -0
  95. package/dist/src/research/pii-image.js +163 -0
  96. package/dist/src/research/pii-image.js.map +1 -0
  97. package/dist/src/research/pii.d.ts +21 -0
  98. package/dist/src/research/pii.d.ts.map +1 -0
  99. package/dist/src/research/pii.js +47 -0
  100. package/dist/src/research/pii.js.map +1 -0
  101. package/dist/src/research/product-profile.d.ts +108 -0
  102. package/dist/src/research/product-profile.d.ts.map +1 -0
  103. package/dist/src/research/product-profile.js +104 -0
  104. package/dist/src/research/product-profile.js.map +1 -0
  105. package/dist/src/research/render-html.d.ts +203 -0
  106. package/dist/src/research/render-html.d.ts.map +1 -0
  107. package/dist/src/research/render-html.js +901 -0
  108. package/dist/src/research/render-html.js.map +1 -0
  109. package/dist/src/research/schema-base.d.ts +5 -0
  110. package/dist/src/research/schema-base.d.ts.map +1 -0
  111. package/dist/src/research/schema-base.js +10 -0
  112. package/dist/src/research/schema-base.js.map +1 -0
  113. package/dist/src/research/schema.d.ts +5142 -0
  114. package/dist/src/research/schema.d.ts.map +1 -0
  115. package/dist/src/research/schema.js +991 -0
  116. package/dist/src/research/schema.js.map +1 -0
  117. package/dist/src/research/source-create.d.ts +79 -0
  118. package/dist/src/research/source-create.d.ts.map +1 -0
  119. package/dist/src/research/source-create.js +226 -0
  120. package/dist/src/research/source-create.js.map +1 -0
  121. package/dist/src/research/source-registry.d.ts +29 -0
  122. package/dist/src/research/source-registry.d.ts.map +1 -0
  123. package/dist/src/research/source-registry.js +74 -0
  124. package/dist/src/research/source-registry.js.map +1 -0
  125. package/dist/src/research/source-schema.d.ts +158 -0
  126. package/dist/src/research/source-schema.d.ts.map +1 -0
  127. package/dist/src/research/source-schema.js +215 -0
  128. package/dist/src/research/source-schema.js.map +1 -0
  129. package/dist/src/research/source-status.d.ts +40 -0
  130. package/dist/src/research/source-status.d.ts.map +1 -0
  131. package/dist/src/research/source-status.js +109 -0
  132. package/dist/src/research/source-status.js.map +1 -0
  133. package/dist/src/research/store.d.ts +24 -0
  134. package/dist/src/research/store.d.ts.map +1 -0
  135. package/dist/src/research/store.js +60 -0
  136. package/dist/src/research/store.js.map +1 -0
  137. package/dist/src/research/target-view.d.ts +33 -0
  138. package/dist/src/research/target-view.d.ts.map +1 -0
  139. package/dist/src/research/target-view.js +133 -0
  140. package/dist/src/research/target-view.js.map +1 -0
  141. package/dist/src/research/target.d.ts +883 -0
  142. package/dist/src/research/target.d.ts.map +1 -0
  143. package/dist/src/research/target.js +323 -0
  144. package/dist/src/research/target.js.map +1 -0
  145. package/dist/src/research/video-transfer.d.ts +28 -0
  146. package/dist/src/research/video-transfer.d.ts.map +1 -0
  147. package/dist/src/research/video-transfer.js +100 -0
  148. package/dist/src/research/video-transfer.js.map +1 -0
  149. package/dist/src/spec/generator.d.ts.map +1 -1
  150. package/dist/src/spec/generator.js +3 -2
  151. package/dist/src/spec/generator.js.map +1 -1
  152. package/dist/src/spec/passthrough-generator.d.ts.map +1 -1
  153. package/dist/src/spec/passthrough-generator.js +6 -4
  154. package/dist/src/spec/passthrough-generator.js.map +1 -1
  155. package/package.json +2 -2
  156. package/plugin/.codex-plugin/plugin.json +1 -1
  157. package/plugin/.mcp.json +1 -1
  158. package/plugin/mcp.json +1 -1
  159. package/plugin/research-sources/amplitude/SOURCE.md +28 -0
  160. package/plugin/research-sources/app-store-reviews/SOURCE.md +25 -0
  161. package/plugin/research-sources/app-store-search/SOURCE.md +25 -0
  162. package/plugin/research-sources/tech-blog-search/SOURCE.md +27 -0
  163. package/plugin/research-sources/uibowl/SOURCE.md +26 -0
  164. package/plugin/research-sources/web-search/SOURCE.md +24 -0
  165. package/plugin/skills/_shared/proactive-routing.md +5 -0
  166. package/plugin/skills/architecture/SKILL.md +1 -1
  167. package/plugin/skills/market-scan/SKILL.md +61 -0
  168. package/plugin/skills/research/SKILL.md +438 -0
  169. package/plugin/skills/research/references/design.md +938 -0
  170. package/plugin/skills/research/references/output-template.md +366 -0
  171. package/plugin/skills/research/scripts/pii-vision.swift +327 -0
  172. package/plugin/skills/research/scripts/slide-extract.mjs +223 -0
  173. package/plugin/skills/reverse-plan/SKILL.md +52 -0
  174. package/plugin/skills/screen-ref/SKILL.md +63 -0
  175. package/plugin/skills/ux-research/SKILL.md +51 -0
@@ -1 +1 @@
1
- {"version":3,"file":"project-memory-store.js","sourceRoot":"","sources":["../../../src/memory/project-memory-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"project-memory-store.js","sourceRoot":"","sources":["../../../src/memory/project-memory-store.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"}
@@ -0,0 +1,274 @@
1
+ // 리서치 HTML의 캔버스 보기. 섹션 하나가 보드 하나이고 피그마처럼 펼쳐 놓고 끌고 확대한다.
2
+ // 문서 보기는 보드 클래스에 아무 모양도 없어서 감싸기 전과 똑같이 보인다.
3
+ /** h2마다 섹션을 끊어 보드로 감싼다. 첫 h2 앞의 제목과 안내는 첫 보드로 묶는다 */
4
+ export function boardMarkup(parts, firstTitle) {
5
+ const out = [`<section class="board" data-title="${firstTitle}">`];
6
+ for (const p of parts) {
7
+ const m = /^<h2>(.*)<\/h2>$/s.exec(p);
8
+ if (m)
9
+ out.push('</section>', `<section class="board" data-title="${m[1]}">`);
10
+ out.push(p);
11
+ }
12
+ out.push('</section>');
13
+ return out.join('\n');
14
+ }
15
+ export const CANVAS_CSS = `
16
+ :root{--canvas:#e7e5e4}
17
+ @media (prefers-color-scheme:dark){:root:not([data-theme="light"]){--canvas:#000}}
18
+ :root[data-theme="dark"]{--canvas:#000}
19
+ .board-label{display:none}
20
+ .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}
21
+ .cv-bar select{max-width:140px}
22
+ .cv-bar button,.cv-bar select{font:inherit;color:var(--fg);background:transparent;border:0;border-radius:6px;padding:4px 8px;cursor:pointer}
23
+ .cv-bar button:hover,.cv-bar select:hover{background:var(--chip)}
24
+ .cv-bar .cv-only{display:none}
25
+ .cv-zoom{min-width:48px;text-align:center;color:var(--muted)}
26
+ body.canvas{overflow:hidden;height:100vh;background:var(--canvas);overscroll-behavior:none}
27
+ body.canvas .cv-bar .cv-only{display:inline-block}
28
+ body.canvas main{position:absolute;left:0;top:0;max-width:none;margin:0;padding:0;transform-origin:0 0}
29
+ body.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)}
30
+ body.canvas .board>h2:first-of-type{border-top:0;margin-top:0;padding-top:0}
31
+ body.canvas .wrap{overflow:visible}
32
+ body.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}
33
+ body.canvas .board-label:hover{color:var(--accent)}
34
+ body.canvas.grab,body.canvas.grab *{cursor:grab!important;user-select:none}
35
+ body.canvas.grabbing,body.canvas.grabbing *{cursor:grabbing!important}
36
+ @media print{.cv-bar{display:none}}
37
+ `;
38
+ // 템플릿 리터럴 안이라 백틱과 달러 중괄호를 쓰지 않는다
39
+ export const CANVAS_JS = `
40
+ (function(){
41
+ var main = document.querySelector('main');
42
+ var boards = Array.prototype.slice.call(document.querySelectorAll('.board'));
43
+ if (!main || boards.length === 0) return;
44
+ var body = document.body;
45
+ var GAP_X = 160, GAP_Y = 200, COLS = 4, MIN_Z = 0.05, MAX_Z = 4;
46
+ var view = { x: 0, y: 0, z: 1 };
47
+ var bounds = { w: 0, h: 0 };
48
+ var on = false, laidOut = false;
49
+
50
+ var bar = document.createElement('div');
51
+ bar.className = 'cv-bar';
52
+ bar.innerHTML =
53
+ '<button type="button" data-act="toggle"></button>' +
54
+ '<select class="cv-only" data-act="jump" title="보드로 이동"></select>' +
55
+ '<button type="button" class="cv-only" data-act="out" title="축소 (-)">−</button>' +
56
+ '<span class="cv-only cv-zoom"></span>' +
57
+ '<button type="button" class="cv-only" data-act="in" title="확대 (+)">+</button>' +
58
+ '<button type="button" class="cv-only" data-act="fit" title="전체 보기 (Shift+1)">전체</button>';
59
+ body.appendChild(bar);
60
+ var toggleBtn = bar.querySelector('[data-act="toggle"]');
61
+ var jump = bar.querySelector('[data-act="jump"]');
62
+ var zoomText = bar.querySelector('.cv-zoom');
63
+ jump.innerHTML = '<option value="">보드</option>' + boards.map(function (b, i) {
64
+ var o = document.createElement('option');
65
+ o.value = String(i);
66
+ o.textContent = b.dataset.title || '';
67
+ return o.outerHTML;
68
+ }).join('');
69
+
70
+ boards.forEach(function (b, i) {
71
+ var label = document.createElement('div');
72
+ label.className = 'board-label';
73
+ label.textContent = b.dataset.title || '';
74
+ label.dataset.board = String(i);
75
+ b.insertBefore(label, b.firstChild);
76
+ });
77
+
78
+ function apply() {
79
+ main.style.transform = 'translate(' + view.x + 'px,' + view.y + 'px) scale(' + view.z + ')';
80
+ main.style.setProperty('--z', String(view.z));
81
+ zoomText.textContent = Math.round(view.z * 100) + '%';
82
+ }
83
+
84
+ // 첫 보드(제목과 안내)는 맨 위에 혼자 두고 나머지는 가장 짧은 열에 차례로 채운다
85
+ function layout() {
86
+ var head = boards[0], rest = boards.slice(1);
87
+ head.style.left = '0px';
88
+ head.style.top = '0px';
89
+ var top = head.offsetHeight + GAP_Y;
90
+ var colW = 0;
91
+ rest.forEach(function (b) { colW = Math.max(colW, b.offsetWidth); });
92
+ var cols = [];
93
+ for (var c = 0; c < Math.min(COLS, rest.length); c++) cols.push(top);
94
+ rest.forEach(function (b) {
95
+ var i = cols.indexOf(Math.min.apply(null, cols));
96
+ b.style.left = i * (colW + GAP_X) + 'px';
97
+ b.style.top = cols[i] + 'px';
98
+ cols[i] += b.offsetHeight + GAP_Y;
99
+ });
100
+ bounds = {
101
+ w: Math.max(head.offsetWidth, cols.length * (colW + GAP_X) - GAP_X),
102
+ h: Math.max.apply(null, cols.concat(top)) - GAP_Y,
103
+ };
104
+ }
105
+
106
+ function widen() {
107
+ boards.forEach(function (b) {
108
+ b.style.width = '';
109
+ var need = 0;
110
+ b.querySelectorAll('table').forEach(function (t) { need = Math.max(need, t.scrollWidth); });
111
+ if (need > 1000) b.style.width = (need + 80) + 'px';
112
+ });
113
+ }
114
+
115
+ function zoomAt(z, cx, cy) {
116
+ z = Math.min(MAX_Z, Math.max(MIN_Z, z));
117
+ view.x = cx - (cx - view.x) * (z / view.z);
118
+ view.y = cy - (cy - view.y) * (z / view.z);
119
+ view.z = z;
120
+ apply();
121
+ }
122
+
123
+ function frame(x, y, w, h, maxZ) {
124
+ var pad = 64, top = 56;
125
+ var vw = window.innerWidth - pad * 2, vh = window.innerHeight - pad - top;
126
+ var z = Math.min(maxZ, vw / w, vh / h);
127
+ view.z = Math.max(MIN_Z, z);
128
+ view.x = pad + (vw - w * view.z) / 2 - x * view.z;
129
+ view.y = top + Math.max(0, (vh - h * view.z) / 2) - y * view.z;
130
+ apply();
131
+ }
132
+
133
+ function fit() { frame(0, 0, bounds.w, bounds.h, 1); }
134
+
135
+ function focusBoard(b) { frame(b.offsetLeft, b.offsetTop, b.offsetWidth, Math.min(b.offsetHeight, 900), 1); }
136
+
137
+ function focusEl(el) {
138
+ var b = el.closest('.board');
139
+ if (!b) return;
140
+ var r = el.getBoundingClientRect();
141
+ var z = Math.max(view.z, 0.8);
142
+ var wx = (r.left - view.x) / view.z, wy = (r.top - view.y) / view.z, ww = r.width / view.z;
143
+ view.z = z;
144
+ view.x = window.innerWidth / 2 - (wx + ww / 2) * z;
145
+ view.y = window.innerHeight / 3 - wy * z;
146
+ apply();
147
+ }
148
+
149
+ var relayoutQueued = false;
150
+ function relayout() {
151
+ if (!on || relayoutQueued) return;
152
+ relayoutQueued = true;
153
+ requestAnimationFrame(function () { relayoutQueued = false; if (on) layout(); });
154
+ }
155
+ var ro = typeof ResizeObserver === 'function' ? new ResizeObserver(relayout) : null;
156
+
157
+ function enter() {
158
+ on = true;
159
+ body.classList.add('canvas');
160
+ toggleBtn.textContent = '문서로 보기';
161
+ document.querySelectorAll('img[loading="lazy"]').forEach(function (img) { img.loading = 'eager'; });
162
+ widen();
163
+ layout();
164
+ if (ro) boards.forEach(function (b) { ro.observe(b); });
165
+ if (!laidOut) { fit(); laidOut = true; } else apply();
166
+ history.replaceState(null, '', '#canvas');
167
+ }
168
+
169
+ function leave() {
170
+ on = false;
171
+ if (ro) ro.disconnect();
172
+ body.classList.remove('canvas', 'grab', 'grabbing');
173
+ toggleBtn.textContent = '캔버스로 보기';
174
+ main.style.transform = '';
175
+ boards.forEach(function (b) { b.style.left = ''; b.style.top = ''; b.style.width = ''; });
176
+ history.replaceState(null, '', location.pathname + location.search);
177
+ }
178
+
179
+ bar.addEventListener('click', function (ev) {
180
+ var act = ev.target.closest('[data-act]');
181
+ if (!act) return;
182
+ var a = act.dataset.act;
183
+ var cx = window.innerWidth / 2, cy = window.innerHeight / 2;
184
+ if (a === 'toggle') { if (on) leave(); else enter(); }
185
+ else if (a === 'in') zoomAt(view.z * 1.25, cx, cy);
186
+ else if (a === 'out') zoomAt(view.z / 1.25, cx, cy);
187
+ else if (a === 'fit') fit();
188
+ });
189
+ jump.addEventListener('change', function () {
190
+ var b = boards[Number(jump.value)];
191
+ if (b) focusBoard(b);
192
+ jump.value = '';
193
+ });
194
+
195
+ window.addEventListener('wheel', function (ev) {
196
+ if (!on || ev.target.closest('.cv-bar')) return;
197
+ ev.preventDefault();
198
+ if (ev.ctrlKey || ev.metaKey) {
199
+ zoomAt(view.z * Math.exp(-ev.deltaY * 0.01), ev.clientX, ev.clientY);
200
+ } else {
201
+ var dx = ev.deltaX, dy = ev.deltaY;
202
+ if (ev.shiftKey && dx === 0) { dx = dy; dy = 0; }
203
+ view.x -= dx;
204
+ view.y -= dy;
205
+ apply();
206
+ }
207
+ }, { passive: false });
208
+
209
+ // 사파리 트랙패드 핀치는 wheel이 아니라 gesture 이벤트로 온다
210
+ var gestureZ = 1;
211
+ window.addEventListener('gesturestart', function (ev) { if (on) { ev.preventDefault(); gestureZ = view.z; } });
212
+ window.addEventListener('gesturechange', function (ev) {
213
+ if (on) { ev.preventDefault(); zoomAt(gestureZ * ev.scale, ev.clientX, ev.clientY); }
214
+ });
215
+
216
+ var space = false, drag = null;
217
+ window.addEventListener('keydown', function (ev) {
218
+ if (!on) return;
219
+ var tag = (ev.target.tagName || '').toLowerCase();
220
+ if (tag === 'select' || tag === 'input' || tag === 'textarea') return;
221
+ var cx = window.innerWidth / 2, cy = window.innerHeight / 2;
222
+ if (ev.code === 'Space') { ev.preventDefault(); if (!space) { space = true; body.classList.add('grab'); } }
223
+ else if (ev.shiftKey && ev.code === 'Digit1') fit();
224
+ else if (ev.shiftKey && ev.code === 'Digit0') zoomAt(1, cx, cy);
225
+ else if (ev.key === '+' || ev.key === '=') { ev.preventDefault(); zoomAt(view.z * 1.25, cx, cy); }
226
+ else if (ev.key === '-' || ev.key === '_') { ev.preventDefault(); zoomAt(view.z / 1.25, cx, cy); }
227
+ });
228
+ window.addEventListener('keyup', function (ev) {
229
+ if (ev.code === 'Space') { space = false; body.classList.remove('grab'); }
230
+ });
231
+
232
+ window.addEventListener('pointerdown', function (ev) {
233
+ if (!on || ev.target.closest('.cv-bar')) return;
234
+ var label = ev.target.closest('.board-label');
235
+ var onBoard = ev.target.closest('.board') && !label;
236
+ if (onBoard && !space && ev.button !== 1) return;
237
+ ev.preventDefault();
238
+ drag = { x: ev.clientX, y: ev.clientY, vx: view.x, vy: view.y, moved: false, label: label };
239
+ body.classList.add('grabbing');
240
+ });
241
+ window.addEventListener('pointermove', function (ev) {
242
+ if (!drag) return;
243
+ var dx = ev.clientX - drag.x, dy = ev.clientY - drag.y;
244
+ if (Math.abs(dx) + Math.abs(dy) > 3) drag.moved = true;
245
+ view.x = drag.vx + dx;
246
+ view.y = drag.vy + dy;
247
+ apply();
248
+ });
249
+ window.addEventListener('pointerup', function () {
250
+ if (!drag) return;
251
+ if (drag.label && !drag.moved) focusBoard(boards[Number(drag.label.dataset.board)]);
252
+ drag = null;
253
+ body.classList.remove('grabbing');
254
+ });
255
+
256
+ // 캡처 썸네일처럼 문서 안 앵커는 캔버스에서 스크롤 대신 그 자리로 화면을 옮긴다
257
+ document.addEventListener('click', function (ev) {
258
+ if (!on) return;
259
+ var a = ev.target.closest('a[href^="#"]');
260
+ if (!a) return;
261
+ var id = decodeURIComponent(a.getAttribute('href').slice(1));
262
+ var el = id && document.getElementById(id);
263
+ if (!el) return;
264
+ ev.preventDefault();
265
+ focusEl(el);
266
+ });
267
+
268
+ window.addEventListener('resize', function () { if (on) apply(); });
269
+
270
+ toggleBtn.textContent = '캔버스로 보기';
271
+ if (location.hash === '#canvas') enter();
272
+ })();
273
+ `;
274
+ //# sourceMappingURL=canvas-view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvas-view.js","sourceRoot":"","sources":["../../../src/research/canvas-view.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,4CAA4C;AAE5C,qDAAqD;AACrD,MAAM,UAAU,WAAW,CAAC,KAAe,EAAE,UAAkB;IAC7D,MAAM,GAAG,GAAG,CAAC,sCAAsC,UAAU,IAAI,CAAC,CAAC;IACnE,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,MAAM,CAAC,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACtC,IAAI,CAAC;YAAE,GAAG,CAAC,IAAI,CAAC,YAAY,EAAE,sCAAsC,CAAC,CAAC,CAAC,CAAE,IAAI,CAAC,CAAC;QAC/E,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACd,CAAC;IACD,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACvB,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACxB,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;;;;CAsBzB,CAAC;AAEF,iCAAiC;AACjC,MAAM,CAAC,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0OxB,CAAC"}
@@ -0,0 +1,145 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * 한 칸이나 한 문장의 근거 상태.
4
+ * observation: 화면이나 영상에서 본 것. company-statement: 회사가 직접 말한 것. interpretation: 우리 해석.
5
+ * unconfirmed: 못 봐서 비워 둔 칸. absent: 없다고 확인한 것. 둘 다 이유(note)를 적는다
6
+ */
7
+ export declare const BASIS_KINDS: readonly ["observation", "company-statement", "interpretation", "unconfirmed", "absent"];
8
+ export type BasisKind = (typeof BASIS_KINDS)[number];
9
+ /** 출처를 가리키는 한 줄. 캡처와 영상은 같은 문서 안의 id로, 내부 자료는 사람이 적은 이름으로 건다 */
10
+ export declare const evidenceRefSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
11
+ type: z.ZodLiteral<"capture">;
12
+ captureId: z.ZodString;
13
+ }, "strip", z.ZodTypeAny, {
14
+ type: "capture";
15
+ captureId: string;
16
+ }, {
17
+ type: "capture";
18
+ captureId: string;
19
+ }>, z.ZodObject<{
20
+ type: z.ZodLiteral<"video">;
21
+ clipId: z.ZodString;
22
+ at: z.ZodNumber;
23
+ }, "strip", z.ZodTypeAny, {
24
+ at: number;
25
+ type: "video";
26
+ clipId: string;
27
+ }, {
28
+ at: number;
29
+ type: "video";
30
+ clipId: string;
31
+ }>, z.ZodObject<{
32
+ type: z.ZodLiteral<"internal">;
33
+ source: z.ZodString;
34
+ }, "strip", z.ZodTypeAny, {
35
+ type: "internal";
36
+ source: string;
37
+ }, {
38
+ type: "internal";
39
+ source: string;
40
+ }>, z.ZodObject<{
41
+ type: z.ZodLiteral<"url">;
42
+ url: z.ZodEffects<z.ZodString, string, string>;
43
+ }, "strip", z.ZodTypeAny, {
44
+ type: "url";
45
+ url: string;
46
+ }, {
47
+ type: "url";
48
+ url: string;
49
+ }>]>;
50
+ export type EvidenceRef = z.infer<typeof evidenceRefSchema>;
51
+ export declare const evidenceListSchema: z.ZodDefault<z.ZodArray<z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
52
+ type: z.ZodLiteral<"capture">;
53
+ captureId: z.ZodString;
54
+ }, "strip", z.ZodTypeAny, {
55
+ type: "capture";
56
+ captureId: string;
57
+ }, {
58
+ type: "capture";
59
+ captureId: string;
60
+ }>, z.ZodObject<{
61
+ type: z.ZodLiteral<"video">;
62
+ clipId: z.ZodString;
63
+ at: z.ZodNumber;
64
+ }, "strip", z.ZodTypeAny, {
65
+ at: number;
66
+ type: "video";
67
+ clipId: string;
68
+ }, {
69
+ at: number;
70
+ type: "video";
71
+ clipId: string;
72
+ }>, z.ZodObject<{
73
+ type: z.ZodLiteral<"internal">;
74
+ source: z.ZodString;
75
+ }, "strip", z.ZodTypeAny, {
76
+ type: "internal";
77
+ source: string;
78
+ }, {
79
+ type: "internal";
80
+ source: string;
81
+ }>, z.ZodObject<{
82
+ type: z.ZodLiteral<"url">;
83
+ url: z.ZodEffects<z.ZodString, string, string>;
84
+ }, "strip", z.ZodTypeAny, {
85
+ type: "url";
86
+ url: string;
87
+ }, {
88
+ type: "url";
89
+ url: string;
90
+ }>]>, "many">>;
91
+ /** 근거를 달고 다니는 칸이 공통으로 가지는 필드 */
92
+ export declare const basisFields: {
93
+ basis: z.ZodEnum<["observation", "company-statement", "interpretation", "unconfirmed", "absent"]>;
94
+ evidence: z.ZodDefault<z.ZodArray<z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
95
+ type: z.ZodLiteral<"capture">;
96
+ captureId: z.ZodString;
97
+ }, "strip", z.ZodTypeAny, {
98
+ type: "capture";
99
+ captureId: string;
100
+ }, {
101
+ type: "capture";
102
+ captureId: string;
103
+ }>, z.ZodObject<{
104
+ type: z.ZodLiteral<"video">;
105
+ clipId: z.ZodString;
106
+ at: z.ZodNumber;
107
+ }, "strip", z.ZodTypeAny, {
108
+ at: number;
109
+ type: "video";
110
+ clipId: string;
111
+ }, {
112
+ at: number;
113
+ type: "video";
114
+ clipId: string;
115
+ }>, z.ZodObject<{
116
+ type: z.ZodLiteral<"internal">;
117
+ source: z.ZodString;
118
+ }, "strip", z.ZodTypeAny, {
119
+ type: "internal";
120
+ source: string;
121
+ }, {
122
+ type: "internal";
123
+ source: string;
124
+ }>, z.ZodObject<{
125
+ type: z.ZodLiteral<"url">;
126
+ url: z.ZodEffects<z.ZodString, string, string>;
127
+ }, "strip", z.ZodTypeAny, {
128
+ type: "url";
129
+ url: string;
130
+ }, {
131
+ type: "url";
132
+ url: string;
133
+ }>]>, "many">>;
134
+ /** 못 본 이유나 없다고 확인한 방법 */
135
+ note: z.ZodOptional<z.ZodString>;
136
+ };
137
+ interface BasisCarrier {
138
+ basis?: BasisKind | undefined;
139
+ evidence: EvidenceRef[];
140
+ note?: string | undefined;
141
+ }
142
+ /** basis별 필수 조건. 관찰과 회사 발언은 출처가 있어야 하고 못 봤거나 없다는 칸은 이유가 있어야 한다 */
143
+ export declare function checkBasis(value: BasisCarrier, ctx: z.RefinementCtx): void;
144
+ export {};
145
+ //# sourceMappingURL=evidence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evidence.d.ts","sourceRoot":"","sources":["../../../src/research/evidence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;GAIG;AACH,eAAO,MAAM,WAAW,0FAMd,CAAC;AACX,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAErD,gEAAgE;AAChE,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAK5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAAyC,CAAC;AAEzE,gCAAgC;AAChC,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAGtB,yBAAyB;;CAE1B,CAAC;AAEF,UAAU,YAAY;IACpB,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAC9B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B;AAED,kEAAkE;AAClE,wBAAgB,UAAU,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC,CAAC,aAAa,GAAG,IAAI,CAmB1E"}
@@ -0,0 +1,50 @@
1
+ import { z } from 'zod';
2
+ import { httpsUrlSchema, idSchema, textSchema } from './schema-base.js';
3
+ /**
4
+ * 한 칸이나 한 문장의 근거 상태.
5
+ * observation: 화면이나 영상에서 본 것. company-statement: 회사가 직접 말한 것. interpretation: 우리 해석.
6
+ * unconfirmed: 못 봐서 비워 둔 칸. absent: 없다고 확인한 것. 둘 다 이유(note)를 적는다
7
+ */
8
+ export const BASIS_KINDS = [
9
+ 'observation',
10
+ 'company-statement',
11
+ 'interpretation',
12
+ 'unconfirmed',
13
+ 'absent',
14
+ ];
15
+ /** 출처를 가리키는 한 줄. 캡처와 영상은 같은 문서 안의 id로, 내부 자료는 사람이 적은 이름으로 건다 */
16
+ export const evidenceRefSchema = z.discriminatedUnion('type', [
17
+ z.object({ type: z.literal('capture'), captureId: idSchema }),
18
+ z.object({ type: z.literal('video'), clipId: idSchema, at: z.number().nonnegative() }),
19
+ z.object({ type: z.literal('internal'), source: textSchema }),
20
+ z.object({ type: z.literal('url'), url: httpsUrlSchema }),
21
+ ]);
22
+ export const evidenceListSchema = z.array(evidenceRefSchema).default([]);
23
+ /** 근거를 달고 다니는 칸이 공통으로 가지는 필드 */
24
+ export const basisFields = {
25
+ basis: z.enum(BASIS_KINDS),
26
+ evidence: evidenceListSchema,
27
+ /** 못 본 이유나 없다고 확인한 방법 */
28
+ note: z.string().optional(),
29
+ };
30
+ /** basis별 필수 조건. 관찰과 회사 발언은 출처가 있어야 하고 못 봤거나 없다는 칸은 이유가 있어야 한다 */
31
+ export function checkBasis(value, ctx) {
32
+ if (!value.basis)
33
+ return;
34
+ if ((value.basis === 'observation' || value.basis === 'company-statement') &&
35
+ value.evidence.length === 0) {
36
+ ctx.addIssue({
37
+ code: z.ZodIssueCode.custom,
38
+ path: ['evidence'],
39
+ message: '관찰과 회사 발언에는 출처(evidence)를 하나 이상 건다',
40
+ });
41
+ }
42
+ if ((value.basis === 'unconfirmed' || value.basis === 'absent') && !value.note?.trim()) {
43
+ ctx.addIssue({
44
+ code: z.ZodIssueCode.custom,
45
+ path: ['note'],
46
+ message: '확인 못함과 없음에는 이유(note)를 적는다',
47
+ });
48
+ }
49
+ }
50
+ //# sourceMappingURL=evidence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evidence.js","sourceRoot":"","sources":["../../../src/research/evidence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAExE;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,aAAa;IACb,mBAAmB;IACnB,gBAAgB;IAChB,aAAa;IACb,QAAQ;CACA,CAAC;AAGX,gEAAgE;AAChE,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,kBAAkB,CAAC,MAAM,EAAE;IAC5D,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC;IAC7D,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;IACtF,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAC7D,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,cAAc,EAAE,CAAC;CAC1D,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;AAEzE,gCAAgC;AAChC,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC;IAC1B,QAAQ,EAAE,kBAAkB;IAC5B,yBAAyB;IACzB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC5B,CAAC;AAQF,kEAAkE;AAClE,MAAM,UAAU,UAAU,CAAC,KAAmB,EAAE,GAAoB;IAClE,IAAI,CAAC,KAAK,CAAC,KAAK;QAAE,OAAO;IACzB,IACE,CAAC,KAAK,CAAC,KAAK,KAAK,aAAa,IAAI,KAAK,CAAC,KAAK,KAAK,mBAAmB,CAAC;QACtE,KAAK,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAC3B,CAAC;QACD,GAAG,CAAC,QAAQ,CAAC;YACX,IAAI,EAAE,CAAC,CAAC,YAAY,CAAC,MAAM;YAC3B,IAAI,EAAE,CAAC,UAAU,CAAC;YAClB,OAAO,EAAE,oCAAoC;SAC9C,CAAC,CAAC;IACL,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,KAAK,KAAK,aAAa,IAAI,KAAK,CAAC,KAAK,KAAK,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC;QACvF,GAAG,CAAC,QAAQ,CAAC;YACX,IAAI,EAAE,CAAC,CAAC,YAAY,CAAC,MAAM;YAC3B,IAAI,EAAE,CAAC,MAAM,CAAC;YACd,OAAO,EAAE,2BAA2B;SACrC,CAAC,CAAC;IACL,CAAC;AACH,CAAC"}
@@ -0,0 +1,80 @@
1
+ import { ANNOTATION_LABEL } from './render-html.js';
2
+ import { type Research } from './schema.js';
3
+ import type { ResearchAudience } from './store.js';
4
+ export type TextStyle = 'title' | 'h2' | 'h3' | 'body' | 'caption' | 'notice';
5
+ /** 구획 배경색. 판단은 초록, 검증은 파랑, 와우와 페인은 주황이라 한눈에 갈린다 */
6
+ export type SectionTint = 'green' | 'blue' | 'orange';
7
+ export type PlanNode = {
8
+ type: 'text';
9
+ style: TextStyle;
10
+ text: string;
11
+ } | {
12
+ type: 'table';
13
+ head: string[];
14
+ rows: string[][];
15
+ } | {
16
+ type: 'images';
17
+ items: {
18
+ imageId: string;
19
+ caption: string;
20
+ }[];
21
+ } | {
22
+ type: 'section';
23
+ title: string;
24
+ tint?: SectionTint;
25
+ children: PlanNode[];
26
+ };
27
+ export interface RefNote {
28
+ kind: keyof typeof ANNOTATION_LABEL;
29
+ label: string;
30
+ text: string;
31
+ }
32
+ /** 화면 캡처 열의 한 덩어리. 제품 하나의 캡처 줄과 그 옆에 붙는 와우와 페인 메모 */
33
+ export interface RefGroup {
34
+ title: string;
35
+ description?: string;
36
+ images: {
37
+ imageId: string;
38
+ caption: string;
39
+ }[];
40
+ notes: RefNote[];
41
+ }
42
+ export type BoardColumnKind = 'captures' | 'conclusion' | 'evidence';
43
+ /** board 배치에서 열이 어떤 구획을 어떤 순서로 담는지. sections는 plan.nodes의 구획 제목이다 */
44
+ export interface FigmaBoard {
45
+ columns: {
46
+ kind: BoardColumnKind;
47
+ title: string;
48
+ sections: string[];
49
+ }[];
50
+ groups: RefGroup[];
51
+ }
52
+ export type FigmaLayout = 'board' | 'page';
53
+ export interface FigmaPlan {
54
+ version: 1;
55
+ pageName: string;
56
+ width: number;
57
+ layout: FigmaLayout;
58
+ nodes: PlanNode[];
59
+ /** layout이 board일 때만 있다. 캡처는 nodes가 아니라 board.groups에 담는다 */
60
+ board?: FigmaBoard;
61
+ /** 스크립트가 이미지 fill로 붙일 캡처. data는 계획에 싣지 않는다 */
62
+ images: {
63
+ id: string;
64
+ path: string;
65
+ }[];
66
+ }
67
+ export type FigmaPlanResult = {
68
+ ok: true;
69
+ plan: FigmaPlan;
70
+ } | {
71
+ ok: false;
72
+ code: 'SHARE_BLOCKED';
73
+ message: string;
74
+ captureIds: string[];
75
+ };
76
+ export declare function buildFigmaPlan(research: Research, opts: {
77
+ audience: ResearchAudience;
78
+ layout?: FigmaLayout;
79
+ }): FigmaPlanResult;
80
+ //# sourceMappingURL=figma-plan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"figma-plan.d.ts","sourceRoot":"","sources":["../../../src/research/figma-plan.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,gBAAgB,EAqBjB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAwB,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGnD,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE9E,mDAAmD;AACnD,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEtD,MAAM,MAAM,QAAQ,GAChB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAChD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAA;CAAE,GACnD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAAE,GACjE;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,QAAQ,EAAE,CAAA;CAAE,CAAC;AAEjF,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,OAAO,gBAAgB,CAAC;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qDAAqD;AACrD,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC/C,KAAK,EAAE,OAAO,EAAE,CAAC;CAClB;AAED,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,YAAY,GAAG,UAAU,CAAC;AAErE,qEAAqE;AACrE,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE;QAAE,IAAI,EAAE,eAAe,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC;IACxE,MAAM,EAAE,QAAQ,EAAE,CAAC;CACpB;AAED,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,MAAM,CAAC;AAE3C,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,CAAC,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,WAAW,CAAC;IACpB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,8CAA8C;IAC9C,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACxC;AAED,MAAM,MAAM,eAAe,GACvB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,SAAS,CAAA;CAAE,GAC7B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAiBhF,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE;IAAE,QAAQ,EAAE,gBAAgB,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,GACzD,eAAe,CAmjBjB"}