@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.
- package/CLAUDE.md +1 -1
- package/dist/package.json +2 -2
- package/dist/plugin/research-sources/amplitude/SOURCE.md +28 -0
- package/dist/plugin/research-sources/app-store-reviews/SOURCE.md +25 -0
- package/dist/plugin/research-sources/app-store-search/SOURCE.md +25 -0
- package/dist/plugin/research-sources/tech-blog-search/SOURCE.md +27 -0
- package/dist/plugin/research-sources/uibowl/SOURCE.md +26 -0
- package/dist/plugin/research-sources/web-search/SOURCE.md +24 -0
- package/dist/plugin/skills/_shared/proactive-routing.md +5 -0
- package/dist/plugin/skills/market-scan/SKILL.md +61 -0
- package/dist/plugin/skills/research/SKILL.md +438 -0
- package/dist/plugin/skills/research/references/design.md +938 -0
- package/dist/plugin/skills/research/references/output-template.md +366 -0
- package/dist/plugin/skills/research/scripts/pii-vision.swift +327 -0
- package/dist/plugin/skills/research/scripts/slide-extract.mjs +223 -0
- package/dist/plugin/skills/reverse-plan/SKILL.md +52 -0
- package/dist/plugin/skills/screen-ref/SKILL.md +63 -0
- package/dist/plugin/skills/ux-research/SKILL.md +51 -0
- package/dist/src/core/config.d.ts +3 -0
- package/dist/src/core/config.d.ts.map +1 -1
- package/dist/src/core/config.js +4 -0
- package/dist/src/core/config.js.map +1 -1
- package/dist/src/mcp/schemas.d.ts +93 -0
- package/dist/src/mcp/schemas.d.ts.map +1 -1
- package/dist/src/mcp/schemas.js +110 -0
- package/dist/src/mcp/schemas.js.map +1 -1
- package/dist/src/mcp/server.d.ts.map +1 -1
- package/dist/src/mcp/server.js +11 -1
- package/dist/src/mcp/server.js.map +1 -1
- package/dist/src/mcp/tools/research-passthrough.d.ts +21 -0
- package/dist/src/mcp/tools/research-passthrough.d.ts.map +1 -0
- package/dist/src/mcp/tools/research-passthrough.js +566 -0
- package/dist/src/mcp/tools/research-passthrough.js.map +1 -0
- package/dist/src/research/canvas-view.d.ts +5 -0
- package/dist/src/research/canvas-view.d.ts.map +1 -0
- package/dist/src/research/canvas-view.js +274 -0
- package/dist/src/research/canvas-view.js.map +1 -0
- package/dist/src/research/evidence.d.ts +145 -0
- package/dist/src/research/evidence.d.ts.map +1 -0
- package/dist/src/research/evidence.js +50 -0
- package/dist/src/research/evidence.js.map +1 -0
- package/dist/src/research/figma-plan.d.ts +80 -0
- package/dist/src/research/figma-plan.d.ts.map +1 -0
- package/dist/src/research/figma-plan.js +527 -0
- package/dist/src/research/figma-plan.js.map +1 -0
- package/dist/src/research/figma-script.d.ts +19 -0
- package/dist/src/research/figma-script.d.ts.map +1 -0
- package/dist/src/research/figma-script.js +363 -0
- package/dist/src/research/figma-script.js.map +1 -0
- package/dist/src/research/index.d.ts +8 -0
- package/dist/src/research/index.d.ts.map +1 -0
- package/dist/src/research/index.js +8 -0
- package/dist/src/research/index.js.map +1 -0
- package/dist/src/research/pii-core.d.ts +16 -0
- package/dist/src/research/pii-core.d.ts.map +1 -0
- package/dist/src/research/pii-core.js +345 -0
- package/dist/src/research/pii-core.js.map +1 -0
- package/dist/src/research/pii-dom.d.ts +13 -0
- package/dist/src/research/pii-dom.d.ts.map +1 -0
- package/dist/src/research/pii-dom.js +208 -0
- package/dist/src/research/pii-dom.js.map +1 -0
- package/dist/src/research/pii-image.d.ts +71 -0
- package/dist/src/research/pii-image.d.ts.map +1 -0
- package/dist/src/research/pii-image.js +163 -0
- package/dist/src/research/pii-image.js.map +1 -0
- package/dist/src/research/pii.d.ts +21 -0
- package/dist/src/research/pii.d.ts.map +1 -0
- package/dist/src/research/pii.js +47 -0
- package/dist/src/research/pii.js.map +1 -0
- package/dist/src/research/product-profile.d.ts +108 -0
- package/dist/src/research/product-profile.d.ts.map +1 -0
- package/dist/src/research/product-profile.js +104 -0
- package/dist/src/research/product-profile.js.map +1 -0
- package/dist/src/research/render-html.d.ts +203 -0
- package/dist/src/research/render-html.d.ts.map +1 -0
- package/dist/src/research/render-html.js +901 -0
- package/dist/src/research/render-html.js.map +1 -0
- package/dist/src/research/schema-base.d.ts +5 -0
- package/dist/src/research/schema-base.d.ts.map +1 -0
- package/dist/src/research/schema-base.js +10 -0
- package/dist/src/research/schema-base.js.map +1 -0
- package/dist/src/research/schema.d.ts +5142 -0
- package/dist/src/research/schema.d.ts.map +1 -0
- package/dist/src/research/schema.js +991 -0
- package/dist/src/research/schema.js.map +1 -0
- package/dist/src/research/source-create.d.ts +79 -0
- package/dist/src/research/source-create.d.ts.map +1 -0
- package/dist/src/research/source-create.js +226 -0
- package/dist/src/research/source-create.js.map +1 -0
- package/dist/src/research/source-registry.d.ts +29 -0
- package/dist/src/research/source-registry.d.ts.map +1 -0
- package/dist/src/research/source-registry.js +74 -0
- package/dist/src/research/source-registry.js.map +1 -0
- package/dist/src/research/source-schema.d.ts +158 -0
- package/dist/src/research/source-schema.d.ts.map +1 -0
- package/dist/src/research/source-schema.js +215 -0
- package/dist/src/research/source-schema.js.map +1 -0
- package/dist/src/research/source-status.d.ts +40 -0
- package/dist/src/research/source-status.d.ts.map +1 -0
- package/dist/src/research/source-status.js +109 -0
- package/dist/src/research/source-status.js.map +1 -0
- package/dist/src/research/store.d.ts +24 -0
- package/dist/src/research/store.d.ts.map +1 -0
- package/dist/src/research/store.js +60 -0
- package/dist/src/research/store.js.map +1 -0
- package/dist/src/research/target-view.d.ts +33 -0
- package/dist/src/research/target-view.d.ts.map +1 -0
- package/dist/src/research/target-view.js +133 -0
- package/dist/src/research/target-view.js.map +1 -0
- package/dist/src/research/target.d.ts +883 -0
- package/dist/src/research/target.d.ts.map +1 -0
- package/dist/src/research/target.js +323 -0
- package/dist/src/research/target.js.map +1 -0
- package/dist/src/research/video-transfer.d.ts +28 -0
- package/dist/src/research/video-transfer.d.ts.map +1 -0
- package/dist/src/research/video-transfer.js +100 -0
- package/dist/src/research/video-transfer.js.map +1 -0
- package/package.json +2 -2
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/.mcp.json +1 -1
- package/plugin/mcp.json +1 -1
- package/plugin/research-sources/amplitude/SOURCE.md +28 -0
- package/plugin/research-sources/app-store-reviews/SOURCE.md +25 -0
- package/plugin/research-sources/app-store-search/SOURCE.md +25 -0
- package/plugin/research-sources/tech-blog-search/SOURCE.md +27 -0
- package/plugin/research-sources/uibowl/SOURCE.md +26 -0
- package/plugin/research-sources/web-search/SOURCE.md +24 -0
- package/plugin/skills/_shared/proactive-routing.md +5 -0
- package/plugin/skills/market-scan/SKILL.md +61 -0
- package/plugin/skills/research/SKILL.md +438 -0
- package/plugin/skills/research/references/design.md +938 -0
- package/plugin/skills/research/references/output-template.md +366 -0
- package/plugin/skills/research/scripts/pii-vision.swift +327 -0
- package/plugin/skills/research/scripts/slide-extract.mjs +223 -0
- package/plugin/skills/reverse-plan/SKILL.md +52 -0
- package/plugin/skills/screen-ref/SKILL.md +63 -0
- package/plugin/skills/ux-research/SKILL.md +51 -0
|
@@ -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"}
|