drafted 1.1.2 → 1.1.3

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.
@@ -0,0 +1,213 @@
1
+ <style>
2
+ :root {
3
+ --bg: #fafafa;
4
+ --card: #fff;
5
+ --border: #e5e7eb;
6
+ --muted: #6b7280;
7
+ --text: #1a1a2e;
8
+ --accent: #533afd;
9
+ --accent-light: #ede9fe;
10
+ }
11
+ * { box-sizing: border-box; margin: 0; padding: 0; }
12
+ html, body { height: 100%; }
13
+ body {
14
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
15
+ background: var(--bg);
16
+ color: var(--text);
17
+ padding: 16px 18px;
18
+ }
19
+ .header {
20
+ display: flex;
21
+ justify-content: space-between;
22
+ align-items: baseline;
23
+ margin-bottom: 14px;
24
+ }
25
+ .header .title { font-size: 14px; font-weight: 600; }
26
+ .header .meta { font-size: 12px; color: var(--muted); }
27
+ .header a {
28
+ color: var(--accent);
29
+ text-decoration: none;
30
+ font-weight: 500;
31
+ font-size: 12px;
32
+ margin-left: 12px;
33
+ }
34
+ .header a:hover { text-decoration: underline; }
35
+
36
+ .layer {
37
+ background: var(--card);
38
+ border: 1px solid var(--border);
39
+ border-radius: 10px;
40
+ padding: 12px 14px;
41
+ margin-bottom: 10px;
42
+ }
43
+ .layer h3 {
44
+ font-size: 12px;
45
+ font-weight: 600;
46
+ color: var(--muted);
47
+ text-transform: uppercase;
48
+ letter-spacing: 0.04em;
49
+ margin-bottom: 8px;
50
+ display: flex;
51
+ justify-content: space-between;
52
+ }
53
+ .layer h3 .count {
54
+ background: var(--accent-light);
55
+ color: var(--accent);
56
+ padding: 1px 8px;
57
+ border-radius: 999px;
58
+ font-size: 10px;
59
+ }
60
+ .frames {
61
+ display: flex;
62
+ flex-wrap: wrap;
63
+ gap: 6px;
64
+ }
65
+ .frame {
66
+ background: var(--bg);
67
+ border: 1px solid var(--border);
68
+ border-radius: 6px;
69
+ padding: 6px 10px;
70
+ font-size: 12px;
71
+ color: var(--text);
72
+ text-decoration: none;
73
+ display: inline-flex;
74
+ align-items: center;
75
+ gap: 6px;
76
+ transition: border-color 0.15s, color 0.15s;
77
+ }
78
+ .frame:hover {
79
+ border-color: var(--accent);
80
+ color: var(--accent);
81
+ }
82
+ .frame .lane {
83
+ color: var(--muted);
84
+ font-size: 11px;
85
+ }
86
+ .empty {
87
+ text-align: center;
88
+ color: var(--muted);
89
+ font-size: 13px;
90
+ padding: 32px;
91
+ }
92
+ .project {
93
+ background: var(--card);
94
+ border: 1px solid var(--border);
95
+ border-radius: 10px;
96
+ padding: 12px 14px;
97
+ margin-bottom: 8px;
98
+ display: flex;
99
+ justify-content: space-between;
100
+ align-items: center;
101
+ text-decoration: none;
102
+ color: inherit;
103
+ transition: border-color 0.15s;
104
+ }
105
+ .project:hover { border-color: var(--accent); }
106
+ .project .name { font-weight: 500; font-size: 14px; }
107
+ .project .desc { font-size: 12px; color: var(--muted); margin-top: 2px; }
108
+ .project .badge {
109
+ font-size: 11px;
110
+ background: var(--accent-light);
111
+ color: var(--accent);
112
+ padding: 2px 8px;
113
+ border-radius: 999px;
114
+ }
115
+ </style>
116
+
117
+ <div class="header">
118
+ <div>
119
+ <div class="title" id="title">Loading…</div>
120
+ <div class="meta" id="subtitle"></div>
121
+ </div>
122
+ <a id="open" href="#" target="_blank" rel="noopener" hidden>Open in canvas →</a>
123
+ </div>
124
+ <div id="root"></div>
125
+
126
+ <script>
127
+ function escapeHtml(s) {
128
+ return String(s).replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
129
+ }
130
+
131
+ function renderProjects(projects, activeId) {
132
+ if (!projects?.length) return '<div class="empty">No projects yet. Use project(action="create") to start one.</div>';
133
+ return projects.slice(0, 30).map(p => {
134
+ const isActive = p.id === activeId;
135
+ const url = p.slug ? `https://drafted.live/project/${escapeHtml(p.slug)}` : '#';
136
+ return `
137
+ <a class="project" href="${url}" target="_blank" rel="noopener">
138
+ <div>
139
+ <div class="name">${escapeHtml(p.name || p.slug || 'Untitled')}</div>
140
+ ${p.description ? `<div class="desc">${escapeHtml(p.description)}</div>` : ''}
141
+ </div>
142
+ ${isActive ? '<span class="badge">active</span>' : ''}
143
+ </a>
144
+ `;
145
+ }).join('');
146
+ }
147
+
148
+ function renderLayers(byLayer) {
149
+ if (!byLayer || !Object.keys(byLayer).length) return '<div class="empty">No frames in this view.</div>';
150
+ return Object.entries(byLayer).map(([layer, frames]) => `
151
+ <div class="layer">
152
+ <h3>${escapeHtml(layer)} <span class="count">${frames.length}</span></h3>
153
+ <div class="frames">
154
+ ${frames.slice(0, 24).map(f => `
155
+ <a class="frame" href="${escapeHtml(f.frameUrl || '#')}" target="_blank" rel="noopener">
156
+ <span>${escapeHtml(f.label || f.filename || f.path || '?')}</span>
157
+ ${f.lane && f.lane !== 'default' ? `<span class="lane">${escapeHtml(f.lane)}</span>` : ''}
158
+ </a>
159
+ `).join('')}
160
+ </div>
161
+ </div>
162
+ `).join('');
163
+ }
164
+
165
+ function render(payload) {
166
+ const sc = payload?.structuredContent || {};
167
+ const root = document.getElementById('root');
168
+
169
+ // project(action="list") shape
170
+ if (Array.isArray(sc.projects)) {
171
+ document.getElementById('title').textContent = `${sc.projects.length} project${sc.projects.length === 1 ? '' : 's'}`;
172
+ document.getElementById('subtitle').textContent = sc.activeProject ? 'One active' : 'None active — use project(action="open") to switch';
173
+ root.innerHTML = renderProjects(sc.projects, sc.activeProject);
174
+ return;
175
+ }
176
+
177
+ // ls shape
178
+ if (sc.byLayer || sc.entries) {
179
+ const byLayer = sc.byLayer || groupByLayer(sc.entries || []);
180
+ const total = Object.values(byLayer).reduce((sum, arr) => sum + arr.length, 0);
181
+ document.getElementById('title').textContent = sc.project || 'Project canvas';
182
+ document.getElementById('subtitle').textContent = `${total} frame${total === 1 ? '' : 's'} · ${Object.keys(byLayer).length} layer${Object.keys(byLayer).length === 1 ? '' : 's'}`;
183
+ if (sc.canvasUrl) {
184
+ const link = document.getElementById('open');
185
+ link.href = sc.canvasUrl;
186
+ link.hidden = false;
187
+ }
188
+ root.innerHTML = renderLayers(byLayer);
189
+ return;
190
+ }
191
+
192
+ root.innerHTML = '<div class="empty">No data to display.</div>';
193
+ }
194
+
195
+ function groupByLayer(entries) {
196
+ const out = {};
197
+ for (const e of entries) {
198
+ const layer = e.layer || 'unsorted';
199
+ (out[layer] ??= []).push(e);
200
+ }
201
+ return out;
202
+ }
203
+
204
+ window.addEventListener('message', (event) => {
205
+ const msg = event.data;
206
+ if (!msg || typeof msg !== 'object') return;
207
+ if (msg.method === 'ui/notifications/tool-result' && msg.params?.result) {
208
+ render(msg.params.result);
209
+ }
210
+ });
211
+
212
+ if (window.openai?.toolResult) render(window.openai.toolResult);
213
+ </script>
@@ -0,0 +1,162 @@
1
+ <style>
2
+ :root {
3
+ --bg: #fafafa;
4
+ --card: #fff;
5
+ --border: #e5e7eb;
6
+ --muted: #6b7280;
7
+ --text: #1a1a2e;
8
+ --accent: #533afd;
9
+ }
10
+ * { box-sizing: border-box; margin: 0; padding: 0; }
11
+ html, body { height: 100%; }
12
+ body {
13
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
14
+ background: var(--bg);
15
+ color: var(--text);
16
+ display: flex;
17
+ flex-direction: column;
18
+ }
19
+ .header {
20
+ display: flex;
21
+ justify-content: space-between;
22
+ align-items: center;
23
+ padding: 10px 14px;
24
+ border-bottom: 1px solid var(--border);
25
+ background: var(--card);
26
+ font-size: 13px;
27
+ }
28
+ .header .path {
29
+ font-family: 'SF Mono', 'Menlo', monospace;
30
+ color: var(--muted);
31
+ font-size: 12px;
32
+ }
33
+ .header .meta {
34
+ color: var(--muted);
35
+ font-size: 11px;
36
+ }
37
+ .header a {
38
+ color: var(--accent);
39
+ text-decoration: none;
40
+ font-weight: 500;
41
+ margin-left: 12px;
42
+ }
43
+ .header a:hover { text-decoration: underline; }
44
+ .stage {
45
+ flex: 1;
46
+ overflow: auto;
47
+ background: var(--card);
48
+ }
49
+ iframe {
50
+ width: 100%;
51
+ min-height: 480px;
52
+ border: 0;
53
+ display: block;
54
+ }
55
+ img.preview {
56
+ max-width: 100%;
57
+ height: auto;
58
+ display: block;
59
+ margin: 0 auto;
60
+ }
61
+ .empty {
62
+ padding: 40px;
63
+ text-align: center;
64
+ color: var(--muted);
65
+ font-size: 13px;
66
+ }
67
+ .markdown-body {
68
+ padding: 24px 32px;
69
+ max-width: 760px;
70
+ margin: 0 auto;
71
+ line-height: 1.6;
72
+ }
73
+ .markdown-body pre {
74
+ background: #f3f4f6;
75
+ padding: 12px 16px;
76
+ border-radius: 6px;
77
+ overflow-x: auto;
78
+ font-size: 13px;
79
+ }
80
+ </style>
81
+
82
+ <div class="header">
83
+ <span class="path" id="path">Loading frame…</span>
84
+ <span>
85
+ <span class="meta" id="meta"></span>
86
+ <a id="open" href="#" target="_blank" rel="noopener" hidden>Open in canvas →</a>
87
+ </span>
88
+ </div>
89
+ <div class="stage" id="stage">
90
+ <div class="empty">Waiting for frame data…</div>
91
+ </div>
92
+
93
+ <script>
94
+ function escapeHtml(s) {
95
+ return String(s).replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
96
+ }
97
+
98
+ function render(payload) {
99
+ const sc = payload?.structuredContent || {};
100
+ const meta = payload?._meta || {};
101
+ const path = sc.path || sc.frameId || sc.id || 'Frame';
102
+ const sizeBits = [];
103
+ if (sc.width && sc.height) sizeBits.push(`${sc.width}×${sc.height}`);
104
+ if (sc.contentType) sizeBits.push(sc.contentType.split('/').pop());
105
+ if (sc.layer) sizeBits.push(sc.layer);
106
+
107
+ document.getElementById('path').textContent = path;
108
+ document.getElementById('meta').textContent = sizeBits.join(' · ');
109
+ const openLink = document.getElementById('open');
110
+ if (sc.frameUrl) {
111
+ openLink.href = sc.frameUrl;
112
+ openLink.hidden = false;
113
+ }
114
+
115
+ const stage = document.getElementById('stage');
116
+ stage.innerHTML = '';
117
+
118
+ // Image (PNG screenshot, asset)
119
+ if (sc.imageUrl || meta.imageUrl) {
120
+ const img = document.createElement('img');
121
+ img.className = 'preview';
122
+ img.src = sc.imageUrl || meta.imageUrl;
123
+ img.alt = path;
124
+ stage.appendChild(img);
125
+ return;
126
+ }
127
+
128
+ // HTML frame — render in sandboxed srcdoc iframe
129
+ const html = meta.frameHtml || sc.content;
130
+ const ctype = (sc.contentType || '').toLowerCase();
131
+ if (html) {
132
+ if (ctype.includes('markdown') || path.toLowerCase().endsWith('.md')) {
133
+ // Render markdown as preformatted text — keeps it readable without a parser dep
134
+ const div = document.createElement('div');
135
+ div.className = 'markdown-body';
136
+ div.innerHTML = '<pre>' + escapeHtml(html) + '</pre>';
137
+ stage.appendChild(div);
138
+ } else {
139
+ const iframe = document.createElement('iframe');
140
+ iframe.setAttribute('sandbox', 'allow-same-origin');
141
+ iframe.srcdoc = html;
142
+ if (sc.height && Number(sc.height) > 480) iframe.style.minHeight = sc.height + 'px';
143
+ stage.appendChild(iframe);
144
+ }
145
+ return;
146
+ }
147
+
148
+ stage.innerHTML = '<div class="empty">No content to preview. The tool result did not include frame HTML.</div>';
149
+ }
150
+
151
+ // MCP Apps bridge: listen for ui/notifications/tool-result
152
+ window.addEventListener('message', (event) => {
153
+ const msg = event.data;
154
+ if (!msg || typeof msg !== 'object') return;
155
+ if (msg.method === 'ui/notifications/tool-result' && msg.params?.result) {
156
+ render(msg.params.result);
157
+ }
158
+ });
159
+
160
+ // Some hosts send the initial payload synchronously — try reading it now too
161
+ if (window.openai?.toolResult) render(window.openai.toolResult);
162
+ </script>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drafted",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "description": "Drafted CLI - Design preview server for Claude agents",
5
5
  "type": "module",
6
6
  "files": [
@@ -30,6 +30,10 @@
30
30
  "dependencies": {
31
31
  "@aws-sdk/client-s3": "^3.1007.0",
32
32
  "@aws-sdk/s3-request-presigner": "^3.1008.0",
33
+ "@clerk/backend": "^3.2.12",
34
+ "@clerk/express": "^2.1.4",
35
+ "@clerk/mcp-tools": "^0.3.1",
36
+ "@modelcontextprotocol/ext-apps": "^1.6.0",
33
37
  "@modelcontextprotocol/sdk": "^1.27.1",
34
38
  "commander": "^11.1.0",
35
39
  "dagre": "^0.8.5",