agent-canvas 0.2.0 → 0.6.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/README.md +42 -17
- package/bin/agent-canvas.js +7 -7
- package/bin/agent-canvas.ts +48 -37
- package/bin/lib/commands/daemon.ts +46 -43
- package/bin/lib/commands/exec.ts +100 -100
- package/bin/lib/commands/fetch.ts +15 -15
- package/bin/lib/commands/install.ts +36 -36
- package/bin/lib/commands/instructions.ts +51 -51
- package/bin/lib/commands/push.ts +115 -97
- package/bin/lib/commands/review.ts +190 -0
- package/bin/lib/commands/watch.ts +24 -24
- package/bin/lib/config.ts +12 -12
- package/bin/lib/daemon-lifecycle.ts +47 -51
- package/bin/lib/helpers.ts +82 -76
- package/bin/lib/instructions/app-review.md +88 -0
- package/bin/lib/instructions/component-checklist.md +7 -5
- package/bin/lib/instructions/component-custom.md +20 -14
- package/bin/lib/instructions/component-diff.md +4 -3
- package/bin/lib/instructions/component-item.md +18 -8
- package/bin/lib/instructions/component-markdown.md +4 -2
- package/bin/lib/instructions/component-mermaid.md +12 -6
- package/bin/lib/instructions/component-multichoice.md +6 -2
- package/bin/lib/instructions/component-note.md +1 -1
- package/bin/lib/instructions/component-priority.md +1 -1
- package/bin/lib/instructions/component-rangeinput.md +9 -2
- package/bin/lib/instructions/component-secretinput.md +12 -11
- package/bin/lib/instructions/component-svg.md +62 -16
- package/bin/lib/instructions/component-table.md +6 -6
- package/bin/lib/instructions/component-usefeedback.md +8 -8
- package/bin/lib/instructions/component-userinput.md +6 -2
- package/bin/lib/instructions/flow-feature.md +22 -5
- package/bin/lib/instructions/flow-plan.md +5 -5
- package/bin/lib/instructions/flow-runbook.md +28 -25
- package/bin/lib/instructions/overview.md +27 -18
- package/bin/lib/instructions/theming.md +5 -0
- package/daemon/build.ts +171 -152
- package/daemon/client/App.tsx +1205 -1077
- package/daemon/client/CompareView.tsx +478 -458
- package/daemon/client/FileBrowser.tsx +241 -203
- package/daemon/client/FileViewer.tsx +220 -211
- package/daemon/client/OverviewView.tsx +283 -267
- package/daemon/client/ReviewerIdentityDialog.tsx +72 -71
- package/daemon/client/RevisionSelect.tsx +111 -95
- package/daemon/client/SessionSwitcher.tsx +81 -81
- package/daemon/client/ShareDialog.tsx +211 -212
- package/daemon/client/apiSchemas.ts +78 -0
- package/daemon/client/canvas-runtime.ts +1 -1
- package/daemon/client/clientApi.ts +194 -206
- package/daemon/client/components/SecretInput.tsx +138 -138
- package/daemon/client/components/index.ts +2 -2
- package/daemon/client/domDiff.ts +341 -333
- package/daemon/client/errorMessage.ts +3 -0
- package/daemon/client/exportMarkdown.ts +222 -201
- package/daemon/client/lcs.ts +19 -14
- package/daemon/client/shareCrypto.ts +57 -57
- package/daemon/client/unifiedDiff.ts +660 -649
- package/daemon/dist/annotations-runtime.js +1 -1
- package/daemon/dist/client.css +2 -2
- package/daemon/dist/client.js +77 -80
- package/daemon/dist/components.js +59 -59
- package/daemon/dist/preact-compat.js +1 -1
- package/daemon/dist/review.js +361 -0
- package/daemon/dist/runtime.js +1 -1
- package/daemon/langMap.ts +24 -6
- package/daemon/review-widget/anchor.ts +187 -0
- package/daemon/review-widget/client.ts +95 -0
- package/daemon/review-widget/index.tsx +36 -0
- package/daemon/review-widget/markup.ts +71 -0
- package/daemon/review-widget/screenshot.ts +91 -0
- package/daemon/review-widget/source.ts +103 -0
- package/daemon/review-widget/styles.css +359 -0
- package/daemon/review-widget/text-modules.d.ts +4 -0
- package/daemon/review-widget/tsconfig.json +16 -0
- package/daemon/review-widget/widget.tsx +436 -0
- package/daemon/src/components.ts +3 -3
- package/daemon/src/handlers/api.ts +330 -328
- package/daemon/src/handlers/files.ts +112 -105
- package/daemon/src/handlers/static.ts +47 -46
- package/daemon/src/handlers/uploads.ts +61 -56
- package/daemon/src/paths.ts +13 -10
- package/daemon/src/remote-feedback.ts +192 -192
- package/daemon/src/review/markdown.ts +52 -0
- package/daemon/src/review/model.ts +132 -0
- package/daemon/src/review/proxy.ts +177 -0
- package/daemon/src/review/server.ts +199 -0
- package/daemon/src/review/store.ts +151 -0
- package/daemon/src/server.ts +127 -86
- package/daemon/src/share.ts +163 -169
- package/daemon/src/stubFetch.ts +7 -0
- package/daemon/tsconfig.json +19 -19
- package/package.json +47 -47
- package/skills/canvas/SKILL.md +9 -1
- package/bin/lib/commands/exec.test.ts +0 -27
- package/bin/lib/helpers.test.ts +0 -79
- package/daemon/src/remote-feedback.test.ts +0 -165
- package/daemon/src/secrets-api.test.ts +0 -123
- package/daemon/src/share-e2e.test.ts +0 -277
- package/daemon/src/share.test.ts +0 -289
|
@@ -1,27 +1,16 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
shareId: string;
|
|
5
|
-
url: string;
|
|
6
|
-
revision: number;
|
|
7
|
-
createdAt: string;
|
|
8
|
-
ownerToken?: string;
|
|
9
|
-
expiresAt?: string;
|
|
10
|
-
lastFeedbackAt?: string;
|
|
11
|
-
/** Present iff the share is end-to-end encrypted; the URL itself
|
|
12
|
-
* carries the key in the `#k=...` fragment. */
|
|
13
|
-
encryptionKey?: string;
|
|
14
|
-
}
|
|
1
|
+
import { useCallback, useEffect, useState } from 'react'
|
|
2
|
+
import { shareCreatedResponseSchema, type ShareEntry, shareRevokedResponseSchema } from './apiSchemas'
|
|
3
|
+
import { errorMessage } from './errorMessage'
|
|
15
4
|
|
|
16
5
|
interface ShareDialogProps {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
6
|
+
sessionId: string
|
|
7
|
+
revision: number
|
|
8
|
+
open: boolean
|
|
9
|
+
shareEnabled: boolean
|
|
10
|
+
existingShares: ShareEntry[]
|
|
11
|
+
onClose: () => void
|
|
12
|
+
onShareCreated: (share: ShareEntry) => void
|
|
13
|
+
onShareRevoked: (shareId: string) => void
|
|
25
14
|
}
|
|
26
15
|
|
|
27
16
|
/**
|
|
@@ -36,207 +25,217 @@ interface ShareDialogProps {
|
|
|
36
25
|
* stored ownerToken.
|
|
37
26
|
*/
|
|
38
27
|
export function ShareDialog({
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
28
|
+
sessionId,
|
|
29
|
+
revision,
|
|
30
|
+
open,
|
|
31
|
+
shareEnabled,
|
|
32
|
+
existingShares,
|
|
33
|
+
onClose,
|
|
34
|
+
onShareCreated,
|
|
35
|
+
onShareRevoked,
|
|
47
36
|
}: ShareDialogProps) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
37
|
+
const [loading, setLoading] = useState(false)
|
|
38
|
+
const [revoking, setRevoking] = useState(false)
|
|
39
|
+
const [error, setError] = useState<string | null>(null)
|
|
40
|
+
const [copied, setCopied] = useState(false)
|
|
52
41
|
|
|
53
|
-
|
|
42
|
+
const shareForRev = existingShares.find((s) => s.revision === revision)
|
|
54
43
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (!open) {
|
|
46
|
+
setLoading(false)
|
|
47
|
+
setRevoking(false)
|
|
48
|
+
setError(null)
|
|
49
|
+
setCopied(false)
|
|
50
|
+
}
|
|
51
|
+
}, [open])
|
|
63
52
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}, [sessionId, revision, onShareCreated]);
|
|
53
|
+
const createShare = useCallback(async () => {
|
|
54
|
+
setLoading(true)
|
|
55
|
+
setError(null)
|
|
56
|
+
try {
|
|
57
|
+
const res = await fetch(`/api/session/${sessionId}/revision/${revision}/share`, {
|
|
58
|
+
method: 'POST',
|
|
59
|
+
})
|
|
60
|
+
const data = shareCreatedResponseSchema.parse(await res.json())
|
|
61
|
+
if (!data.ok) throw new Error(data.error || `HTTP ${res.status}`)
|
|
62
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`)
|
|
63
|
+
onShareCreated(data.share)
|
|
64
|
+
} catch (e) {
|
|
65
|
+
setError(errorMessage(e))
|
|
66
|
+
} finally {
|
|
67
|
+
setLoading(false)
|
|
68
|
+
}
|
|
69
|
+
}, [sessionId, revision, onShareCreated])
|
|
82
70
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}, [sessionId, onShareRevoked]);
|
|
71
|
+
const revoke = useCallback(async (shareId: string) => {
|
|
72
|
+
if (!window.confirm('Revoke this share link? Reviewers will no longer be able to view or comment.')) return
|
|
73
|
+
setRevoking(true)
|
|
74
|
+
setError(null)
|
|
75
|
+
try {
|
|
76
|
+
const res = await fetch(`/api/session/${sessionId}/shares/${shareId}/revoke`, {
|
|
77
|
+
method: 'POST',
|
|
78
|
+
})
|
|
79
|
+
const data = shareRevokedResponseSchema.parse(await res.json())
|
|
80
|
+
if (!data.ok) throw new Error(data.error || `HTTP ${res.status}`)
|
|
81
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`)
|
|
82
|
+
onShareRevoked(shareId)
|
|
83
|
+
} catch (e) {
|
|
84
|
+
setError(errorMessage(e))
|
|
85
|
+
} finally {
|
|
86
|
+
setRevoking(false)
|
|
87
|
+
}
|
|
88
|
+
}, [sessionId, onShareRevoked])
|
|
102
89
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
90
|
+
const copyUrl = useCallback((url: string) => {
|
|
91
|
+
navigator.clipboard.writeText(url).then(() => {
|
|
92
|
+
setCopied(true)
|
|
93
|
+
setTimeout(() => setCopied(false), 1500)
|
|
94
|
+
})
|
|
95
|
+
}, [])
|
|
109
96
|
|
|
110
|
-
|
|
97
|
+
if (!open) return null
|
|
111
98
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
99
|
+
return (
|
|
100
|
+
<div
|
|
101
|
+
className="fixed inset-0 z-50 flex items-center justify-center bg-black/50"
|
|
102
|
+
onClick={onClose}
|
|
103
|
+
>
|
|
104
|
+
<div
|
|
105
|
+
className="bg-bg-surface border border-border-medium rounded-lg shadow-xl w-full max-w-md mx-4 p-5"
|
|
106
|
+
onClick={(e) => e.stopPropagation()}
|
|
107
|
+
>
|
|
108
|
+
<div className="flex items-center justify-between mb-3">
|
|
109
|
+
<h2 className="text-sm font-medium text-text-primary font-body">
|
|
110
|
+
Share this revision
|
|
111
|
+
</h2>
|
|
112
|
+
<button
|
|
113
|
+
onClick={onClose}
|
|
114
|
+
className="w-6 h-6 flex items-center justify-center rounded text-text-tertiary hover:text-text-secondary hover:bg-bg-elevated transition-colors"
|
|
115
|
+
>
|
|
116
|
+
<span className="text-xs">✕</span>
|
|
117
|
+
</button>
|
|
118
|
+
</div>
|
|
132
119
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
120
|
+
{!shareEnabled
|
|
121
|
+
? (
|
|
122
|
+
<div className="text-[13px] text-text-secondary font-body space-y-2">
|
|
123
|
+
<p>Canvas sharing is not configured.</p>
|
|
124
|
+
<p className="text-text-tertiary">
|
|
125
|
+
Set <code className="px-1 py-0.5 rounded bg-bg-elevated text-text-primary text-[11px]">CANVAS_SHARE_ENDPOINT</code> to the URL of a deployed
|
|
126
|
+
{' '}
|
|
127
|
+
<code className="px-1 py-0.5 rounded bg-bg-elevated text-text-primary text-[11px]">canvas-share</code> worker and restart the daemon.
|
|
128
|
+
</p>
|
|
129
|
+
</div>
|
|
130
|
+
)
|
|
131
|
+
: shareForRev
|
|
132
|
+
? (
|
|
133
|
+
<div className="space-y-3">
|
|
134
|
+
<p className="text-[13px] text-text-secondary font-body">
|
|
135
|
+
This revision is shared:
|
|
136
|
+
</p>
|
|
137
|
+
<div className="flex gap-2">
|
|
138
|
+
<input
|
|
139
|
+
type="text"
|
|
140
|
+
readOnly
|
|
141
|
+
value={shareForRev.url}
|
|
142
|
+
className="flex-1 px-2 py-1.5 text-[12px] font-mono bg-bg-input border border-border-medium rounded text-text-primary"
|
|
143
|
+
onFocus={(e) => e.currentTarget.select()}
|
|
144
|
+
/>
|
|
145
|
+
<button
|
|
146
|
+
onClick={() => copyUrl(shareForRev.url)}
|
|
147
|
+
className="px-3 py-1.5 text-[12px] font-body font-medium rounded bg-accent-blue text-white hover:opacity-90 transition-opacity whitespace-nowrap"
|
|
148
|
+
>
|
|
149
|
+
{copied ? 'Copied!' : 'Copy'}
|
|
150
|
+
</button>
|
|
151
|
+
</div>
|
|
152
|
+
{shareForRev.expiresAt && (
|
|
153
|
+
<p className="text-[11px] text-text-tertiary font-body">
|
|
154
|
+
Expires {new Date(shareForRev.expiresAt).toLocaleDateString()}.
|
|
155
|
+
</p>
|
|
156
|
+
)}
|
|
157
|
+
{shareForRev.encryptionKey && (
|
|
158
|
+
<p className="text-[11px] text-accent-green font-body flex items-center gap-1.5">
|
|
159
|
+
<svg
|
|
160
|
+
width="11"
|
|
161
|
+
height="11"
|
|
162
|
+
viewBox="0 0 24 24"
|
|
163
|
+
fill="none"
|
|
164
|
+
stroke="currentColor"
|
|
165
|
+
strokeWidth="2"
|
|
166
|
+
strokeLinecap="round"
|
|
167
|
+
strokeLinejoin="round"
|
|
168
|
+
>
|
|
169
|
+
<rect x="3" y="11" width="18" height="11" rx="2" ry="2" />
|
|
170
|
+
<path d="M7 11V7a5 5 0 0 1 10 0v4" />
|
|
171
|
+
</svg>
|
|
172
|
+
End-to-end encrypted — the key is part of the link and never leaves your browser.
|
|
173
|
+
</p>
|
|
174
|
+
)}
|
|
175
|
+
<p className="text-[11px] text-text-tertiary font-body">
|
|
176
|
+
Anyone with this link can view and leave feedback. Reviewer feedback will appear in your annotation sidebar within seconds.
|
|
177
|
+
</p>
|
|
178
|
+
<div className="flex items-center justify-between pt-1">
|
|
179
|
+
<button
|
|
180
|
+
onClick={createShare}
|
|
181
|
+
disabled={loading || revoking}
|
|
182
|
+
className="text-[11px] text-text-tertiary hover:text-text-secondary font-body underline disabled:opacity-50"
|
|
183
|
+
>
|
|
184
|
+
{loading ? 'Creating...' : 'Create a new link'}
|
|
185
|
+
</button>
|
|
186
|
+
{shareForRev.ownerToken && (
|
|
187
|
+
<button
|
|
188
|
+
onClick={() => revoke(shareForRev.shareId)}
|
|
189
|
+
disabled={loading || revoking}
|
|
190
|
+
className="text-[11px] text-accent-red hover:opacity-80 font-body underline disabled:opacity-50"
|
|
191
|
+
>
|
|
192
|
+
{revoking ? 'Revoking...' : 'Revoke link'}
|
|
193
|
+
</button>
|
|
194
|
+
)}
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
)
|
|
198
|
+
: (
|
|
199
|
+
<div className="space-y-3">
|
|
200
|
+
<p className="text-[13px] text-text-secondary font-body">
|
|
201
|
+
Create a public link to this revision. Anyone with the link can view the canvas and leave feedback. Reviewer feedback will sync back here
|
|
202
|
+
automatically.
|
|
203
|
+
</p>
|
|
204
|
+
<button
|
|
205
|
+
onClick={createShare}
|
|
206
|
+
disabled={loading}
|
|
207
|
+
className="w-full px-3 py-2 text-[13px] font-body font-medium rounded bg-accent-blue text-white hover:opacity-90 transition-opacity disabled:opacity-50"
|
|
208
|
+
>
|
|
209
|
+
{loading ? 'Creating share link...' : 'Create share link'}
|
|
210
|
+
</button>
|
|
211
|
+
</div>
|
|
212
|
+
)}
|
|
214
213
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
214
|
+
{error && (
|
|
215
|
+
<div className="mt-3 px-3 py-2 rounded bg-accent-red/10 border border-accent-red/20 text-[12px] text-accent-red font-body">
|
|
216
|
+
{error}
|
|
217
|
+
</div>
|
|
218
|
+
)}
|
|
219
|
+
</div>
|
|
220
|
+
</div>
|
|
221
|
+
)
|
|
223
222
|
}
|
|
224
223
|
|
|
225
224
|
/** Small share icon button for toolbar use. */
|
|
226
|
-
export function ShareButton({ onClick, title =
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
225
|
+
export function ShareButton({ onClick, title = 'Share this revision', hasShare }: { onClick: () => void; title?: string; hasShare?: boolean }) {
|
|
226
|
+
return (
|
|
227
|
+
<button
|
|
228
|
+
onClick={onClick}
|
|
229
|
+
className={`group p-1.5 transition-colors ${hasShare ? 'text-accent-blue hover:opacity-80' : 'text-text-tertiary hover:text-text-secondary'}`}
|
|
230
|
+
title={title}
|
|
231
|
+
>
|
|
232
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
233
|
+
<circle cx="18" cy="5" r="3" />
|
|
234
|
+
<circle cx="6" cy="12" r="3" />
|
|
235
|
+
<circle cx="18" cy="19" r="3" />
|
|
236
|
+
<line x1="8.59" y1="13.51" x2="15.42" y2="17.49" />
|
|
237
|
+
<line x1="15.41" y1="6.51" x2="8.59" y2="10.49" />
|
|
238
|
+
</svg>
|
|
239
|
+
</button>
|
|
240
|
+
)
|
|
242
241
|
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { annotationAuthorSchema, annotationSchema } from '@fabrika/annotations/schemas'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
|
|
4
|
+
export const shareEntrySchema = z.object({
|
|
5
|
+
shareId: z.string(),
|
|
6
|
+
url: z.string(),
|
|
7
|
+
revision: z.number(),
|
|
8
|
+
createdAt: z.string(),
|
|
9
|
+
ownerToken: z.string().optional(),
|
|
10
|
+
expiresAt: z.string().optional(),
|
|
11
|
+
lastFeedbackAt: z.string().optional(),
|
|
12
|
+
/** Present iff the share is end-to-end encrypted; the URL itself
|
|
13
|
+
* carries the key in the `#k=...` fragment. */
|
|
14
|
+
encryptionKey: z.string().optional(),
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
export type ShareEntry = z.infer<typeof shareEntrySchema>
|
|
18
|
+
|
|
19
|
+
const revisionInfoSchema = z.object({
|
|
20
|
+
revision: z.number(),
|
|
21
|
+
label: z.string().optional(),
|
|
22
|
+
canvasFiles: z.array(z.object({
|
|
23
|
+
filename: z.string(),
|
|
24
|
+
diffStats: z.object({ added: z.number(), removed: z.number() }).optional(),
|
|
25
|
+
})),
|
|
26
|
+
createdAt: z.string(),
|
|
27
|
+
hasFeedback: z.boolean(),
|
|
28
|
+
feedbackConsumed: z.boolean(),
|
|
29
|
+
response: z.string().optional(),
|
|
30
|
+
}).passthrough()
|
|
31
|
+
|
|
32
|
+
/** Served by the daemon for a session and by the share worker for a share, which leaves `projectRoot` out. */
|
|
33
|
+
export const sessionMetaSchema = z.object({
|
|
34
|
+
encryption: z.object({ alg: z.literal('AES-GCM'), v: z.literal(1) }).optional(),
|
|
35
|
+
origin: z.object({
|
|
36
|
+
sessionId: z.string().optional(),
|
|
37
|
+
label: z.string().optional(),
|
|
38
|
+
revision: z.number().optional(),
|
|
39
|
+
createdAt: z.string().optional(),
|
|
40
|
+
}).optional(),
|
|
41
|
+
currentRevision: z.number().optional(),
|
|
42
|
+
revisions: z.array(revisionInfoSchema).optional(),
|
|
43
|
+
canvasFiles: z.array(z.string()).optional(),
|
|
44
|
+
projectRoot: z.string().optional(),
|
|
45
|
+
shares: z.array(shareEntrySchema).optional(),
|
|
46
|
+
shareEnabled: z.boolean().optional(),
|
|
47
|
+
}).passthrough()
|
|
48
|
+
|
|
49
|
+
export type SessionMeta = z.infer<typeof sessionMetaSchema>
|
|
50
|
+
|
|
51
|
+
export const treeResponseSchema = z.object({
|
|
52
|
+
entries: z.array(z.object({ name: z.string(), type: z.enum(['file', 'dir']) })),
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
/** Mirrors the daemon's RemoteFeedbackEntry. */
|
|
56
|
+
export const remoteFeedbackEntrySchema = z.object({
|
|
57
|
+
id: z.string(),
|
|
58
|
+
shareId: z.string(),
|
|
59
|
+
revision: z.number(),
|
|
60
|
+
submittedAt: z.string(),
|
|
61
|
+
author: annotationAuthorSchema,
|
|
62
|
+
annotations: z.array(annotationSchema.omit({ source: true, author: true })),
|
|
63
|
+
generalNote: z.string().optional(),
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
export type RemoteFeedbackEntry = z.infer<typeof remoteFeedbackEntrySchema>
|
|
67
|
+
|
|
68
|
+
export const remoteFeedbackResponseSchema = z.object({ entries: z.array(remoteFeedbackEntrySchema) })
|
|
69
|
+
|
|
70
|
+
export const shareCreatedResponseSchema = z.union([
|
|
71
|
+
z.object({ ok: z.literal(true), share: shareEntrySchema }),
|
|
72
|
+
z.object({ ok: z.literal(false).optional(), error: z.string().optional() }),
|
|
73
|
+
])
|
|
74
|
+
|
|
75
|
+
export const shareRevokedResponseSchema = z.union([
|
|
76
|
+
z.object({ ok: z.literal(true) }),
|
|
77
|
+
z.object({ ok: z.literal(false).optional(), error: z.string().optional() }),
|
|
78
|
+
])
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from
|
|
1
|
+
export * from '@fabrika/canvas-kernel/runtime'
|