use-chisel 0.1.8 → 0.1.10

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 CHANGED
@@ -1,5 +1,43 @@
1
1
  # use-chisel
2
2
 
3
+ Hosted website reviews and visual editing for Next.js.
4
+
5
+ ## Quickstart
6
+
7
+ ```bash
8
+ npm install use-chisel
9
+ npx chisel
10
+ ```
11
+
12
+ Sign in with Google, connect the project, restart its development server, and
13
+ open `/chisel`. Chisel stores shared feedback in its hosted service; client
14
+ projects do not configure a database.
15
+
16
+ Before inviting clients, deploy the site with `CHISEL_ENABLED=1`, open its public
17
+ HTTPS `/chisel` page, then use **Share review link**. Chisel remembers the deployed
18
+ URL for sharing from local development. Localhost links cannot be shared with clients.
19
+ Return to your projects from the account menu or the hosted `/chisel/projects` page.
20
+
21
+ ## Review workflow
22
+
23
+ - Copy a thread link to reopen its page, breakpoint, and exact captured width,
24
+ including after sign-in. Links require existing review access.
25
+ - The thread header's status pill shows **Open**, **Ready**, or **Resolved**.
26
+ Owners choose Ready for review; the comment author confirms by selecting
27
+ Resolved, or selects Open if more work is needed. Changes appear immediately
28
+ while saving; failed saves restore the previous status.
29
+ - Open and unread counts appear on the breakpoint controls and page picker.
30
+ - Comment and reply text, rich formatting, and inline edit drafts recover locally
31
+ after refresh. Drafts expire after seven days and are isolated by project/user.
32
+ Uploaded files must be reattached after refresh.
33
+ - A screenshot and viewport context are captured when composing a new comment.
34
+ Its image uploads after the comment posts. Failed captures do not block feedback;
35
+ failed uploads offer retry. Password/payment-code fields and elements marked
36
+ `data-chisel-private` are excluded. Cross-origin media or browser restrictions
37
+ can prevent a complete capture; a screenshot is a reference, not a browser recording.
38
+ - Owners can copy selected feedback for Codex, including context, replies, proposed
39
+ changes, and private attachment links. Media links expire; thread links remain.
40
+
3
41
  ## Combined Workspace
4
42
 
5
43
  `use-chisel/workspace` exports `ChiselWorkspace`, `ChiselPreview`,
@@ -12,6 +50,18 @@ with Google.
12
50
 
13
51
  Edit, Comment, and View share one toolbar. Threads expand from element pins;
14
52
  history appears in the sidebar.
53
+ The mode toolbar sits at bottom left; the breakpoint switcher sits at bottom
54
+ right. **D**, **T**, and **M** select exact iframe widths of 1440px, 768px, and
55
+ 390px. Drag either preview edge to resize, use arrow keys on the edge, or type a
56
+ pixel width and press Enter, up to the space available beside the sidebar.
57
+ Typed widths also apply when leaving the field; Escape cancels the edit.
58
+ Wider previews scroll horizontally rather than shrinking the selected width.
59
+ Comments cascade to narrower previews: Desktop feedback appears on all three
60
+ breakpoints, Tablet feedback on Tablet and Mobile, and Mobile feedback only on
61
+ Mobile. Mobile is below 768px, Tablet is 768–1023px, and Desktop starts at 1024px;
62
+ comments without a `breakpoint` belong to Desktop. Opening inherited feedback
63
+ keeps the current preview width. Custom stores
64
+ should persist the optional `WorkspaceComment.breakpoint` field on creation.
15
65
  Use **E** for Edit, **C** for Comment, and **V** for View. **Shift+C** toggles
16
66
  all comment pins, threads, and the comments sidebar. Shortcuts are ignored
17
67
  while typing in form fields or editable content. Comments are browser-local by
@@ -53,6 +103,11 @@ hosted service; project credentials and Supabase service keys are not required
53
103
  in the client project. The hosted workspace supports rich text, attachments,
54
104
  replies, resolution, review links, reviewer presence, and unread indicators.
55
105
 
106
+ Review sign-in is remembered across tabs and browser restarts on the same
107
+ website. Sessions expire after 30 days of inactivity and renew during use.
108
+ Temporary connection failures keep the saved sign-in; signing out clears it
109
+ across tabs. Project access and website origin are still checked on every request.
110
+
56
111
  The generated route defaults off in production. To use it on a deployed review
57
112
  site, set `CHISEL_ENABLED=1` for that deployment and redeploy. Hosted connections
58
113
  require HTTPS, except on localhost. Automatic setup currently requires an App
@@ -121,5 +176,24 @@ dictation, and explicit submit in the same form. Custom rich text can be passed
121
176
  optional mutation `body`, with validated upload IDs in `attachmentTokens`. Adapters
122
177
  must validate both before storing them and bind uploads to the authenticated user
123
178
  and site. Keep the editor and attachment draft when `onSubmit` or `onReply` returns
124
- `false`; release it only after a successful save. Slate and Supabase remain demo
125
- dependencies. The built-in local UI remains available without either dependency.
179
+ `false`; release it only after a successful save. The shared rich-text review UI
180
+ uses Slate. Supabase stays in the hosted service or the host's adapter; client
181
+ projects do not need its SDK. Icons are bundled at build time.
182
+
183
+ ### Custom host adapters
184
+
185
+ Optional workspace props keep custom hosts on the same review workflow:
186
+ `projectId` isolates local drafts; `manageReview(action, payload)` loads project
187
+ settings and handles `settings`, `remove_reviewer`, `start_round`, and `approve`;
188
+ `accountURL` links to the host's project directory. The exported `ManageReview`
189
+ and `ReviewProject` types describe this contract.
190
+
191
+ `saveSnapshot(commentId, file)` uploads the captured PNG after the comment saves.
192
+ The shared `uploadSnapshot` helper reuses the attachment transport and preserves
193
+ upload IDs on retry. Bind the upload through the `snapshot` mutation, checking
194
+ membership, author permissions, file ownership/type, and one snapshot per thread.
195
+ `getAttachmentURL(id)` supplies private media links for the Codex export.
196
+ Stores should persist `context`, `status`, status author/time, and review-round IDs.
197
+ The `status` mutation supports `open`, `ready`, and `resolved`; keep `resolved`
198
+ consistent for older clients. `commentURL`, `feedbackMarkdown`, and
199
+ `publicReviewURL` are exported from `use-chisel/review-content` for custom hosts.
@@ -1,134 +1,5 @@
1
- "use client";import{jsx as s,jsxs as g,Fragment as me}from"react/jsx-runtime";import{useCallback as x,useEffect as O,useMemo as U,useRef as w,useState as h}from"react";import{ChiselWorkspace as ue,ChiselWorkspaceStyles as _}from"use-chisel/workspace";import{ReviewComposer as ve,ReviewThread as xe,useReviewAttachments as we,useReadQueue as ge}from"use-chisel/review";var W=`.chisel-review{font-family:var(--chisel-ui-font);-webkit-font-smoothing:antialiased}
2
- .chisel-review .comment-thread[data-native-ui=true]{padding:0;gap:0;border:0;border-radius:16px;background:var(--chisel-ui-surface);box-shadow:0 0 0 1px var(--chisel-ui-border),0 8px 32px #00000014;font-size:14px}
3
- .chisel-review .comment-thread[data-native-ui=true] .feedback-preview{margin:4px 12px 0;border-radius:8px}
4
- .chisel-review .comment-thread[data-native-ui=true] form{width:100%}
5
- .chisel-review-thread-content{width:100%;min-width:0}
6
- .chisel-review-message{display:flex;gap:12px;padding:16px}
7
- .chisel-review-message-content{min-width:0;flex:1}
8
- .chisel-review-avatar{width:32px;height:32px;flex-shrink:0}
9
- .chisel-review-avatar img,.chisel-review .feedback-reply-avatar img{width:100%;height:100%;object-fit:cover;border-radius:inherit}
10
- .chisel-review-byline{display:flex;gap:8px;flex-wrap:wrap;align-items:baseline;line-height:1.5}
11
- .chisel-review-byline strong{font-weight:500}
12
- .chisel-review-byline time{color:var(--chisel-ui-secondary);font-size:12px;font-variant-numeric:tabular-nums}
13
- .chisel-rich-body{white-space:pre-wrap;overflow-wrap:anywhere;line-height:1.5;color:var(--chisel-ui-text)}
14
- .chisel-rich-body p,.chisel-rich-editor p{margin:0;min-height:1.5em}
15
- .chisel-rich-body a,.chisel-rich-editor a{color:var(--chisel-ui-accent-text);text-decoration:underline;text-underline-offset:2px}
16
- .chisel-rich-body code,.chisel-rich-editor code{font:0.9em ui-monospace,monospace;background:var(--chisel-ui-inset);padding:1px 3px;border-radius:4px}
17
- .chisel-review .chisel-expand-thread{margin:0 16px 12px 60px;min-height:40px;padding:0;background:transparent;color:var(--chisel-ui-accent-text)}
18
- .chisel-review-reply{display:flex;align-items:flex-start;gap:12px;padding:16px;border-top:1px solid var(--chisel-ui-border)}
19
- .chisel-rich-editor{min-width:0;min-height:40px;max-height:min(180px,30dvh);overflow-y:auto;padding:8px;font-size:14px;line-height:1.5;outline:none;overflow-wrap:anywhere}
20
- .chisel-review-reply .chisel-rich-editor{padding:12px 14px;min-height:44px}
21
- .chisel-review .comment-box-native{border:0;border-radius:var(--chisel-ui-radius);box-shadow:0 0 0 1px var(--chisel-ui-border),0 8px 32px #00000014;font:inherit}
22
- .chisel-review .comment-box-native>form{display:flex;flex-direction:column;min-height:0;height:100%;flex:1}
23
- .chisel-review .comment-box-native .comment-box-prompt{align-items:flex-start;padding:8px;gap:0}
24
- .chisel-review .comment-box-native .comment-box-symbol{margin-top:0;background:transparent;border-radius:8px}
25
- .chisel-review .comment-box-native .comment-box-footer{background:var(--chisel-ui-surface);border-top:1px solid var(--chisel-ui-border)}
26
- .chisel-review .comment-box-native .comment-box-cancel{border:0;border-radius:8px;background:transparent;font:inherit}
27
- .chisel-review .comment-box-native .comment-box-submit{border-radius:8px;background:var(--chisel-ui-action);color:var(--chisel-ui-action-text)}
28
- .chisel-review .comment-box-native .comment-box-element{background:var(--chisel-ui-inset)}
29
- .chisel-review .comment-box-native .comment-box-properties{background:var(--chisel-ui-surface)}
30
- .chisel-review .feedback-comment{border-radius:var(--chisel-ui-radius)}
31
- .chisel-review .comment-thread[data-expanded=false] .chisel-rich-body{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3;max-height:4.5em;overflow:hidden}
32
- .chisel-review [data-slate-placeholder]{color:var(--chisel-ui-secondary);opacity:1!important}
33
- .chisel-review .chisel-reply-field{position:relative;flex:1;min-width:0;border:1px solid var(--chisel-ui-border);border-radius:16px;background:var(--chisel-ui-raised)}
34
- .chisel-review .chisel-reply-submit{position:absolute;right:4px;bottom:4px;display:grid;place-items:center;width:40px;height:40px;padding:0;border:0;border-radius:50%;background:var(--chisel-ui-action);color:var(--chisel-ui-action-text)}
35
- .chisel-review .feedback-reply-avatar{width:32px;height:32px;margin-top:8px}
36
- .chisel-composer-input{flex:1;min-width:0;max-height:min(320px,42dvh);overflow:auto}
37
- .chisel-attachment-controls{display:flex;align-items:center;flex-wrap:wrap;gap:8px;padding:0 8px;font-size:12px;color:var(--chisel-ui-secondary)}
38
- .chisel-review-reply .chisel-attachment-controls{padding-right:48px}
39
- .chisel-formatting-tools button{display:grid;place-items:center;width:40px;height:40px;flex-shrink:0;padding:0;border:0;border-radius:8px;background:transparent;color:var(--chisel-ui-secondary);transition:background 150ms,scale 150ms}
40
- .chisel-formatting-tools button:hover{background:var(--chisel-ui-hover)}
41
- .chisel-formatting-tools button[aria-pressed=true]{color:var(--chisel-ui-accent-text);background:var(--chisel-ui-accent-surface)}
42
- .chisel-formatting-tools{display:flex;gap:0;flex-wrap:wrap;padding:0 4px}
43
- .chisel-formatting-tools button:active{scale:0.96}
44
- .chisel-link-dialog{display:flex;flex-wrap:wrap;gap:6px;padding:8px;border-top:1px solid var(--chisel-ui-border);background:var(--chisel-ui-surface)}
45
- .chisel-link-dialog label{flex:1;min-width:120px;font-size:12px;color:var(--chisel-ui-secondary)}
46
- .chisel-link-dialog input{display:block;width:100%;height:40px;background:var(--chisel-ui-inset);border:1px solid var(--chisel-ui-border);border-radius:6px;color:var(--chisel-ui-text);padding:6px}
47
- .chisel-link-dialog button{align-self:flex-end;min-height:40px;min-width:40px;padding:6px;border:0;border-radius:6px;background:var(--chisel-ui-hover);color:var(--chisel-ui-text)}
48
- .chisel-link-dialog [role=alert]{width:100%;color:var(--chisel-ui-danger);font-size:12px}
49
- .chisel-attachment-error{color:var(--chisel-ui-danger);line-height:1.5}
50
- .chisel-attachment-previews{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;padding:8px;max-height:240px;overflow:auto}
51
- .chisel-attachment-preview{position:relative;min-width:0;overflow:hidden;border:1px solid var(--chisel-ui-border);border-radius:8px;background:var(--chisel-ui-surface)}
52
- .chisel-attachment-preview>img,.chisel-attachment-preview>a>img,.chisel-attachment-preview>video{display:block;width:100%;height:100px;object-fit:contain;background:var(--chisel-ui-inset);outline:1px solid #0000001a;outline-offset:-1px}
53
- .chisel-attachment-preview>span{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding:8px;font-size:12px}
54
- .chisel-attachment-preview>button:not(.review-media-thumbnail){position:absolute;top:0;right:0;display:grid;place-items:center;width:40px;height:40px;padding:0;border:0;border-radius:0 8px;background:var(--chisel-ui-surface);color:var(--chisel-ui-text)}
55
- .chisel-attachment-preview>.chisel-attachment-retry{right:40px;border-radius:0 0 0 8px}
56
- .chisel-attachment-preview>.chisel-attachment-load{position:static;width:100%;height:auto;min-height:100px;padding:8px}
57
- .comment-thread .feedback-thread-header{position:sticky;top:0;z-index:1;background:var(--chisel-ui-surface)}
58
- .chisel-review .comment-thread[data-native-ui=true][data-expanded=false]{border-radius:16px 16px 16px 0}
59
- @media(prefers-color-scheme:dark){.chisel-attachment-preview>img,.chisel-attachment-preview>a>img,.chisel-attachment-preview>video{outline-color:#ffffff1a}}
60
- @media(prefers-reduced-motion:reduce){.chisel-formatting-tools button{transition:none}}
61
- `;var X=`/* Shared presentation for the owned review UI. */
62
- .feedback-shell.chisel-review .comment-thread[data-native-ui=true],.feedback-shell.chisel-review .comment-box-native{border:0;border-radius:16px;background:var(--chisel-ui-surface);box-shadow:var(--chisel-ui-popover-shadow)}
63
- .feedback-shell.chisel-review .comment-thread[data-expanded=false]{border-radius:16px 16px 16px 0}
64
- .chisel-review-thread-content,.review-thread,.review-comment-content{width:100%;min-width:0}
65
- .review-comment,.feedback-shell.chisel-review .chisel-review-message{display:flex;align-items:flex-start;gap:12px;padding:12px 20px}
66
- .review-comment-content,.chisel-review-message-content{flex:1;min-width:0}
67
- .review-comment-avatar{display:grid;place-items:center;flex-shrink:0;width:28px;height:28px;border-radius:50%;overflow:hidden;background:var(--chisel-ui-avatar);color:var(--chisel-ui-avatar-text);font-size:12px}
68
- .review-comment-avatar img{width:100%;height:100%;object-fit:cover}
69
- .review-comment-header,.chisel-review-byline{display:flex;align-items:baseline;flex-wrap:wrap;gap:8px;line-height:20px;margin-bottom:4px}
70
- .review-comment-author{font-size:13px;font-weight:500;overflow-wrap:anywhere}
71
- .review-comment-time{font-size:12px;color:var(--chisel-ui-muted);font-variant-numeric:tabular-nums;white-space:nowrap}
72
- .review-comment-header{position:relative;padding-right:32px}
73
- .review-comment-edit{position:absolute;right:-8px;top:-10px;display:grid;place-items:center;width:40px;height:40px;border:0;border-radius:8px;background:transparent;color:var(--chisel-ui-muted);opacity:0;transition:opacity 120ms,background 120ms}
74
- .review-comment:hover .review-comment-edit,.review-comment:focus-within .review-comment-edit,.review-comment-edit:focus-visible{opacity:1}
75
- .review-comment-edit:hover{background:var(--chisel-ui-hover);color:var(--chisel-ui-text)}
76
- @media(hover:none){.review-comment-edit{opacity:1}}
77
- .review-inline-edit{border:1px solid var(--chisel-ui-border);border-radius:10px;padding:8px}
78
- .review-inline-edit-actions{display:flex;justify-content:flex-end;gap:6px;margin-top:8px}
79
- .review-inline-edit-actions button{min-height:32px;padding:0 10px;border:0;border-radius:6px;background:var(--chisel-ui-hover);color:var(--chisel-ui-text)}
80
- .review-comment-body,.chisel-rich-body{font-size:14px;line-height:21px;white-space:pre-wrap;overflow-wrap:anywhere}
81
- .review-comment-body p{margin:0;min-height:21px}.review-comment-body p+p{margin-top:8px}
82
- .review-comment-body a{color:var(--chisel-ui-accent-text);text-decoration:underline;text-underline-offset:3px}
83
- .review-comment-body code{font:12px/1.5 ui-monospace,SFMono-Regular,monospace;background:var(--chisel-ui-inset);border-radius:4px;padding:2px 4px}
84
- .review-composer{margin:0}
85
- .feedback-shell.chisel-review .chisel-review-reply{display:flex;align-items:flex-start;gap:12px;border:0;padding:20px}
86
- .feedback-shell.chisel-review .chisel-review-reply[hidden]{display:none}
87
- .feedback-shell.chisel-review .feedback-reply-avatar{width:28px;height:28px;margin-top:8px}
88
- .feedback-shell.chisel-review .chisel-reply-field{position:relative;flex:1;min-width:0;min-height:48px;border:0;border-radius:12px;background:var(--chisel-ui-raised)}
89
- .review-composer-editor{min-width:0;min-height:40px;max-height:min(180px,30dvh);overflow:auto;padding:10px 12px;font-size:14px;line-height:21px;overflow-wrap:anywhere;outline:none}
90
- .review-composer-editor p{margin:0}
91
- .review-composer-editor>p,.chisel-rich-editor>p{position:relative}
92
- .feedback-shell.chisel-review .chisel-review-reply :is(.review-composer-editor,.chisel-rich-editor){padding-right:56px}
93
- .feedback-shell.chisel-review .chisel-reply-submit{position:absolute;right:4px;bottom:4px;display:grid;place-items:center;width:40px;height:40px;min-height:40px;padding:0;border:0;border-radius:8px;background:var(--chisel-ui-action);color:var(--chisel-ui-action-text)}
94
- .chisel-reply-submit:disabled{opacity:.35}
95
- .feedback-shell.chisel-review .comment-box-native>form{display:flex;flex-direction:column;flex:1;min-height:0;height:100%}
96
- .feedback-shell.chisel-review .comment-box-native .comment-box-prompt{align-items:flex-start;padding:8px;gap:0}
97
- .feedback-shell.chisel-review .comment-box-native .comment-box-footer{border:0;background:var(--chisel-ui-surface)}
98
- .feedback-shell.chisel-review .comment-box-native .comment-box-element{background:var(--chisel-ui-raised)}
99
- .feedback-shell.chisel-review .comment-box-native .comment-box-cancel{border:0;border-radius:8px;background:transparent}
100
- .feedback-shell.chisel-review .comment-box-native .comment-box-cancel:hover{background:var(--chisel-ui-hover)}
101
- .feedback-shell.chisel-review .comment-box-native .comment-box-submit{border-radius:8px}
102
- .chisel-composer-input{flex:1;min-width:0;max-height:min(320px,42dvh);overflow:auto}
103
- .chisel-attachment-controls{display:flex;align-items:center;flex-wrap:wrap;gap:8px;padding:0 8px;color:var(--chisel-ui-secondary);font-size:12px;line-height:18px}
104
- .chisel-review-reply .chisel-attachment-controls{padding-right:48px}
105
- .chisel-attachment-error{color:var(--chisel-ui-danger);line-height:1.5}
106
- .chisel-attachment-previews,.review-attachments{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;padding:8px;max-height:240px;overflow:auto}
107
- .review-attachments{padding:12px 0 0;max-height:none}
108
- .feedback-shell.chisel-review .chisel-attachment-preview,.review-attachment{position:relative;min-width:0;margin:0;overflow:hidden;border:0;border-radius:8px;background:var(--chisel-ui-raised)}
109
- .chisel-attachment-preview>img,.chisel-attachment-preview>video,.review-media-thumbnail img,.review-media-thumbnail video{display:block;width:100%;height:120px;object-fit:contain;background:var(--chisel-ui-inset)}
110
- .review-attachment figcaption{padding:8px;font-size:12px;line-height:18px;overflow-wrap:anywhere}
111
- .review-attachment-placeholder{min-height:100px;display:grid;place-items:center;padding:12px;color:var(--chisel-ui-secondary);font-size:12px}
112
- .chisel-attachment-preview>button:not(.review-media-thumbnail){position:absolute;top:0;right:0;display:grid;place-items:center;width:40px;height:40px;padding:0;border:0;border-radius:0 8px;background:var(--chisel-ui-surface);color:var(--chisel-ui-text)}
113
- .review-retry-upload{display:inline-flex;align-items:center;gap:6px;min-height:40px;padding:0 8px;border:0;border-radius:8px;background:transparent;color:var(--chisel-ui-text);font:inherit}
114
- .review-retry-upload:hover{background:var(--chisel-ui-hover)}
115
- .chisel-attachment-preview>span{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding:8px;font-size:12px}
116
- .feedback-shell.chisel-review .chisel-expand-thread{display:inline-flex;align-items:center;margin:0 20px 8px 60px;min-height:40px;padding:0;border:0;background:transparent;color:var(--chisel-ui-secondary);font-size:13px}
117
- .chisel-review [data-slate-placeholder]{opacity:1!important;color:var(--chisel-ui-secondary)}
118
- @media(pointer:coarse){.feedback-shell.chisel-review .chisel-reply-submit{width:44px;height:44px}.chisel-review-reply .chisel-attachment-controls{padding-right:52px}.feedback-shell.chisel-review .chisel-reply-field{min-height:52px}.feedback-shell.chisel-review .chisel-review-reply :is(.review-composer-editor,.chisel-rich-editor){padding-right:60px}}
119
- @media(forced-colors:active){.feedback-shell.chisel-review .comment-thread[data-native-ui=true],.feedback-shell.chisel-review .comment-box-native,.feedback-shell.chisel-review .chisel-reply-field{border:1px solid CanvasText;box-shadow:none}.review-comment-avatar{border:1px solid CanvasText}}
120
- .review-media-thumbnail{position:relative;display:block;width:100%;padding:0;border:0;background:var(--chisel-ui-inset);cursor:pointer}
121
- .review-media-thumbnail img,.review-media-thumbnail video{display:block;width:100%;height:120px;object-fit:contain}
122
- .review-media-play{position:absolute;inset:50% auto auto 50%;display:grid;place-items:center;width:40px;height:40px;border-radius:50%;background:#0009;color:white;transform:translate(-50%,-50%)}
123
- .review-media-lightbox{position:fixed;inset:0;width:100vw;height:100dvh;max-width:none;max-height:none;margin:0;padding:24px;border:0;background:transparent;color:var(--chisel-ui-text)}
124
- .review-media-lightbox::backdrop{background:#000b}
125
- .review-media-lightbox-content{display:flex;flex-direction:column;width:min(100%,1000px);max-height:100%;margin:auto;border-radius:12px;overflow:hidden;background:var(--chisel-ui-surface);box-shadow:0 16px 48px #0006}
126
- .review-media-lightbox-content header{display:flex;align-items:center;gap:12px;min-height:56px;padding:8px 12px 8px 16px}
127
- .review-media-lightbox-content header span{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
128
- .review-media-lightbox-content header a{color:var(--chisel-ui-text);font-size:13px}
129
- .review-media-lightbox-content header button{display:grid;place-items:center;flex-shrink:0;width:40px;height:40px;padding:0;border:0;border-radius:8px;background:transparent;color:var(--chisel-ui-text)}
130
- .review-media-lightbox-content header button:hover{background:var(--chisel-ui-hover)}
131
- .review-media-lightbox-content>img,.review-media-lightbox-content>video{display:block;min-height:0;max-width:100%;max-height:calc(100dvh - 104px);margin:auto;object-fit:contain;background:#000}
132
- `;var b="https://chisel-review.vercel.app";function H(p){return Array.isArray(p)?p.filter(o=>o&&typeof o.id=="string"&&typeof o.name=="string"&&typeof o.path=="string").map(o=>({id:o.id,name:o.name.slice(0,100),path:o.path.slice(0,2048),image:typeof o.image=="string"&&/^https?:\/\//.test(o.image)?o.image:null})):[]}function Pe({projectId:p}){let o=`chisel:session:${p}`,c=w(""),[m,N]=h(null),[q,y]=h(!0),[B,I]=h(""),[Q,k]=h(!1),[M,S]=h([]),[Y,G]=h([]),[K,L]=h("connecting"),C=w("/"),T=w(!1),[Z,ee]=h(0),j=w(new Set),u=x(()=>{for(let e of j.current)e()},[]),E=w(null);E.current=m;let P=w(null),z=w(""),d=x(async(e,i,t,r,a=!1)=>{let n=await fetch(`${b}/chisel/api/hosted/${e}`,{method:i,headers:{Authorization:`Bearer ${c.current}`,...t?{"Content-Type":"application/json","X-Chisel-Protocol":"2"}:{}},...t?{body:JSON.stringify(t)}:{},signal:r,keepalive:a}),v=await n.json();if(!n.ok)throw n.status===401&&E.current&&k(!0),Object.assign(new Error(v.error||"Chisel is unavailable. Please try again."),{status:n.status});if(v.siteId&&v.siteId!==p)throw new Error("This session belongs to another project.");return v},[p]),l=x((e,i)=>d(e==="GET"?`comments?path=${encodeURIComponent(C.current)}`:"comments",e,i),[d]),ie=U(()=>{let e=0;return{async list(){let i=++e;try{let t=await l("GET");return i===e&&(S(t.comments),T.current||G(H(t.participants))),t.comments}catch(t){throw t}},async mutate(i){++e;let t=(await l("POST",i)).comment;return++e,S(r=>r.some(a=>a.id===t.id)?r.map(a=>a.id===t.id?t:a):[...r,t]),window.setTimeout(u,0),t},async delete(i){++e,await l("POST",{type:"delete",id:i}),++e,S(t=>t.filter(r=>r.id!==i)),window.setTimeout(u,0)},subscribe(i){return j.current.add(i),()=>{j.current.delete(i)}}}},[l,u]),te=x(async(e,i)=>{await l("POST",{type:"read",id:e,activity:i})},[l]),re=x((e,i)=>{S(t=>t.map(r=>r.id===e?{...r,readActivity:Math.max(r.readActivity??0,i)}:r))},[]),{flush:D,markDisplayed:ae}=ge(te,re);O(()=>{if(!m)return;let e=!0,i,t,r=new AbortController,a=()=>{document.visibilityState==="visible"&&(clearTimeout(t),t=setTimeout(()=>{u(),D()},40))};async function n(){try{let R=await fetch(`${b}/chisel/api/hosted/events?path=${encodeURIComponent(C.current)}`,{headers:{Authorization:`Bearer ${c.current}`},signal:r.signal});if(R.status===401&&k(!0),!R.ok||!R.body)throw new Error("Live updates unavailable");let se=R.body.getReader(),ce=new TextDecoder,f="";for(;e;){let{value:le,done:de}=await se.read();if(de)break;f+=ce.decode(le,{stream:!0});let $;for(;($=f.indexOf(`
1
+ "use client";import{useCallback as u,useEffect as F,useMemo as H,useRef as g,useState as v}from"react";import{ChiselWorkspace as ye,ChiselWorkspaceStyles as J}from"use-chisel/workspace";import{ReviewComposer as ke,ReviewThread as Se,useReviewAttachments as Te,useReadQueue as Ce,uploadSnapshot as Ee}from"use-chisel/review";import{publicReviewURL as Re}from"use-chisel/review-content";var ee=`.chisel-review{font-family:var(--chisel-ui-font);-webkit-font-smoothing:antialiased}.chisel-review .comment-thread[data-native-ui=true]{padding:0;gap:0;border:0;border-radius:16px;background:var(--chisel-ui-surface);box-shadow:0 0 0 1px var(--chisel-ui-border),0 8px 32px #00000014;font-size:14px}.chisel-review .comment-thread[data-native-ui=true] .feedback-preview{margin:4px 12px 0;border-radius:8px}.chisel-review .comment-thread[data-native-ui=true] form{width:100%}.chisel-review-thread-content{width:100%;min-width:0}.chisel-review-message{display:flex;gap:12px;padding:16px}.chisel-review-message-content{min-width:0;flex:1}.chisel-review-avatar{width:32px;height:32px;flex-shrink:0}.chisel-review-avatar img,.chisel-review .feedback-reply-avatar img{width:100%;height:100%;object-fit:cover;border-radius:inherit}.chisel-review-byline{display:flex;gap:8px;flex-wrap:wrap;align-items:baseline;line-height:1.5}.chisel-review-byline strong{font-weight:500}.chisel-review-byline time{color:var(--chisel-ui-secondary);font-size:12px;font-variant-numeric:tabular-nums}.chisel-rich-body{white-space:pre-wrap;overflow-wrap:anywhere;line-height:1.5;color:var(--chisel-ui-text)}.chisel-rich-body p,.chisel-rich-editor p{margin:0;min-height:1.5em}.chisel-rich-body a,.chisel-rich-editor a{color:var(--chisel-ui-accent-text);text-decoration:underline;text-underline-offset:2px}.chisel-rich-body code,.chisel-rich-editor code{font:.9em ui-monospace,monospace;background:var(--chisel-ui-inset);padding:1px 3px;border-radius:4px}.chisel-review .chisel-expand-thread{margin:0 16px 12px 60px;min-height:40px;padding:0;background:transparent;color:var(--chisel-ui-accent-text)}.chisel-review-reply{display:flex;align-items:flex-start;gap:12px;padding:16px;border-top:1px solid var(--chisel-ui-border)}.chisel-rich-editor{min-width:0;min-height:40px;max-height:min(180px,30dvh);overflow-y:auto;padding:8px;font-size:14px;line-height:1.5;outline:none;overflow-wrap:anywhere}.chisel-review-reply .chisel-rich-editor{padding:12px 14px;min-height:44px}.chisel-review .comment-box-native{border:0;border-radius:var(--chisel-ui-radius);box-shadow:0 0 0 1px var(--chisel-ui-border),0 8px 32px #00000014;font:inherit}.chisel-review .comment-box-native>form{display:flex;flex-direction:column;min-height:0;height:100%;flex:1}.chisel-review .comment-box-native .comment-box-prompt{align-items:flex-start;padding:8px;gap:0}.chisel-review .comment-box-native .comment-box-symbol{margin-top:0;background:transparent;border-radius:8px}.chisel-review .comment-box-native .comment-box-footer{background:var(--chisel-ui-surface);border-top:1px solid var(--chisel-ui-border)}.chisel-review .comment-box-native .comment-box-cancel{border:0;border-radius:8px;background:transparent;font:inherit}.chisel-review .comment-box-native .comment-box-submit{border-radius:8px;background:var(--chisel-ui-action);color:var(--chisel-ui-action-text)}.chisel-review .comment-box-native .comment-box-element{background:var(--chisel-ui-inset)}.chisel-review .comment-box-native .comment-box-properties{background:var(--chisel-ui-surface)}.chisel-review .feedback-comment{border-radius:var(--chisel-ui-radius)}.chisel-review .comment-thread[data-expanded=false] .chisel-rich-body{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3;max-height:4.5em;overflow:hidden}.chisel-review [data-slate-placeholder]{color:var(--chisel-ui-secondary);opacity:1!important}.chisel-review .chisel-reply-field{position:relative;flex:1;min-width:0;border:1px solid var(--chisel-ui-border);border-radius:16px;background:var(--chisel-ui-raised)}.chisel-review .chisel-reply-submit{position:absolute;right:4px;bottom:4px;display:grid;place-items:center;width:40px;height:40px;padding:0;border:0;border-radius:50%;background:var(--chisel-ui-action);color:var(--chisel-ui-action-text)}.chisel-review .feedback-reply-avatar{width:32px;height:32px;margin-top:8px}.chisel-composer-input{flex:1;min-width:0;max-height:min(320px,42dvh);overflow:auto}.chisel-attachment-controls{display:flex;align-items:center;flex-wrap:wrap;gap:8px;padding:0 8px;font-size:12px;color:var(--chisel-ui-secondary)}.chisel-review-reply .chisel-attachment-controls{padding-right:48px}.chisel-formatting-tools button{display:grid;place-items:center;width:40px;height:40px;flex-shrink:0;padding:0;border:0;border-radius:8px;background:transparent;color:var(--chisel-ui-secondary);transition:background .15s,scale .15s}.chisel-formatting-tools button:hover{background:var(--chisel-ui-hover)}.chisel-formatting-tools button[aria-pressed=true]{color:var(--chisel-ui-accent-text);background:var(--chisel-ui-accent-surface)}.chisel-formatting-tools{display:flex;gap:0;flex-wrap:wrap;padding:0 4px}.chisel-formatting-tools button:active{scale:.96}.chisel-link-dialog{display:flex;flex-wrap:wrap;gap:6px;padding:8px;border-top:1px solid var(--chisel-ui-border);background:var(--chisel-ui-surface)}.chisel-link-dialog label{flex:1;min-width:120px;font-size:12px;color:var(--chisel-ui-secondary)}.chisel-link-dialog input{display:block;width:100%;height:40px;background:var(--chisel-ui-inset);border:1px solid var(--chisel-ui-border);border-radius:6px;color:var(--chisel-ui-text);padding:6px}.chisel-link-dialog button{align-self:flex-end;min-height:40px;min-width:40px;padding:6px;border:0;border-radius:6px;background:var(--chisel-ui-hover);color:var(--chisel-ui-text)}.chisel-link-dialog [role=alert]{width:100%;color:var(--chisel-ui-danger);font-size:12px}.chisel-attachment-error{color:var(--chisel-ui-danger);line-height:1.5}.chisel-attachment-previews{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;padding:8px;max-height:240px;overflow:auto}.chisel-attachment-preview{position:relative;min-width:0;overflow:hidden;border:1px solid var(--chisel-ui-border);border-radius:8px;background:var(--chisel-ui-surface)}.chisel-attachment-preview>img,.chisel-attachment-preview>a>img,.chisel-attachment-preview>video{display:block;width:100%;height:100px;object-fit:contain;background:var(--chisel-ui-inset);outline:1px solid #0000001a;outline-offset:-1px}.chisel-attachment-preview>span{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding:8px;font-size:12px}.chisel-attachment-preview>button:not(.review-media-thumbnail){position:absolute;top:0;right:0;display:grid;place-items:center;width:40px;height:40px;padding:0;border:0;border-radius:0 8px;background:var(--chisel-ui-surface);color:var(--chisel-ui-text)}.chisel-attachment-preview>.chisel-attachment-retry{right:40px;border-radius:0 0 0 8px}.chisel-attachment-preview>.chisel-attachment-load{position:static;width:100%;height:auto;min-height:100px;padding:8px}.comment-thread .feedback-thread-header{position:sticky;top:0;z-index:1;background:var(--chisel-ui-surface)}.chisel-review .comment-thread[data-native-ui=true][data-expanded=false]{border-radius:16px 16px 16px 0}@media(prefers-color-scheme:dark){.chisel-attachment-preview>img,.chisel-attachment-preview>a>img,.chisel-attachment-preview>video{outline-color:#ffffff1a}}@media(prefers-reduced-motion:reduce){.chisel-formatting-tools button{transition:none}}
2
+ `;var ie=`.feedback-shell.chisel-review .comment-thread[data-native-ui=true],.feedback-shell.chisel-review .comment-box-native{border:0;border-radius:16px;background:var(--chisel-ui-surface);box-shadow:var(--chisel-ui-popover-shadow)}.feedback-shell.chisel-review .comment-thread[data-expanded=false]{border-radius:16px 16px 16px 0}.chisel-review-thread-content,.review-thread,.review-comment-content{width:100%;min-width:0}.review-capture-context{margin:4px 12px 8px;font-size:12px;line-height:18px;color:var(--chisel-ui-muted);overflow-wrap:anywhere}.review-comment,.feedback-shell.chisel-review .chisel-review-message{display:flex;align-items:flex-start;gap:12px;padding:12px 20px}.review-comment-content,.chisel-review-message-content{flex:1;min-width:0}.review-comment-avatar{display:grid;place-items:center;flex-shrink:0;width:28px;height:28px;border-radius:50%;overflow:hidden;background:var(--chisel-ui-avatar);color:var(--chisel-ui-avatar-text);font-size:12px}.review-comment-avatar img{width:100%;height:100%;object-fit:cover}.review-comment-header,.chisel-review-byline{display:flex;align-items:baseline;flex-wrap:wrap;gap:8px;line-height:20px;margin-bottom:4px}.review-comment-author{font-size:13px;font-weight:500;overflow-wrap:anywhere}.review-comment-time{font-size:12px;color:var(--chisel-ui-muted);font-variant-numeric:tabular-nums;white-space:nowrap}.review-comment-header{position:relative;padding-right:32px}.review-comment-edit{position:absolute;right:-8px;top:-10px;display:grid;place-items:center;width:40px;height:40px;border:0;border-radius:8px;background:transparent;color:var(--chisel-ui-muted);opacity:0;transition:opacity .12s,background .12s}.review-comment:hover .review-comment-edit,.review-comment:focus-within .review-comment-edit,.review-comment-edit:focus-visible{opacity:1}.review-comment-edit:hover{background:var(--chisel-ui-hover);color:var(--chisel-ui-text)}@media(hover:none){.review-comment-edit{opacity:1}}.review-inline-edit{border:1px solid var(--chisel-ui-border);border-radius:10px;padding:8px}.review-inline-edit-actions{display:flex;justify-content:flex-end;gap:6px;margin-top:8px}.review-inline-edit-actions button{min-height:40px;padding:0 10px;border:0;border-radius:8px;background:transparent;color:var(--chisel-ui-text)}.review-inline-edit-actions button:last-child{background:var(--chisel-ui-action);color:var(--chisel-ui-action-text)}.review-inline-edit-actions button:not(:last-child):hover{background:var(--chisel-ui-hover)}.review-inline-edit-actions button:disabled{opacity:.45}@media(pointer:coarse){.review-inline-edit-actions button{min-height:44px}}.review-comment-body,.chisel-rich-body{font-size:14px;line-height:21px;white-space:pre-wrap;overflow-wrap:anywhere}.review-comment-body p{margin:0;min-height:21px}.review-comment-body p+p{margin-top:8px}.review-comment-body a{color:var(--chisel-ui-accent-text);text-decoration:underline;text-underline-offset:3px}.review-comment-body code{font:12px/1.5 ui-monospace,SFMono-Regular,monospace;background:var(--chisel-ui-inset);border-radius:4px;padding:2px 4px}.review-composer{margin:0}.feedback-shell.chisel-review .chisel-review-reply{display:flex;align-items:flex-start;gap:12px;border:0;padding:20px}.feedback-shell.chisel-review .chisel-review-reply[hidden]{display:none}.feedback-shell.chisel-review .feedback-reply-avatar{width:28px;height:28px;margin-top:8px}.feedback-shell.chisel-review .chisel-reply-field{position:relative;flex:1;min-width:0;min-height:48px;border:0;border-radius:12px;background:var(--chisel-ui-raised)}.review-composer-editor{min-width:0;min-height:40px;max-height:min(180px,30dvh);overflow:auto;padding:10px 12px;font-size:14px;line-height:21px;overflow-wrap:anywhere;outline:none}.review-composer-editor p{margin:0}.review-composer-editor>p,.chisel-rich-editor>p{position:relative}.feedback-shell.chisel-review .chisel-review-reply :is(.review-composer-editor,.chisel-rich-editor){padding-right:56px}.feedback-shell.chisel-review .chisel-reply-submit{position:absolute;right:4px;bottom:4px;display:grid;place-items:center;width:40px;height:40px;min-height:40px;padding:0;border:0;border-radius:8px;background:var(--chisel-ui-action);color:var(--chisel-ui-action-text)}.chisel-reply-submit:disabled{opacity:.35}.feedback-shell.chisel-review .comment-box-native>form{display:flex;flex-direction:column;flex:1;min-height:0;height:100%}.feedback-shell.chisel-review .comment-box-native .comment-box-prompt{align-items:flex-start;padding:8px;gap:0}.feedback-shell.chisel-review .comment-box-native .comment-box-footer{border:0;background:var(--chisel-ui-surface)}.feedback-shell.chisel-review .comment-box-native .comment-box-element{background:var(--chisel-ui-raised)}.feedback-shell.chisel-review .comment-box-native .comment-box-cancel{border:0;border-radius:8px;background:transparent}.feedback-shell.chisel-review .comment-box-native .comment-box-cancel:hover{background:var(--chisel-ui-hover)}.feedback-shell.chisel-review .comment-box-native .comment-box-submit{border-radius:8px}.chisel-composer-input{flex:1;min-width:0;max-height:min(320px,42dvh);overflow:auto}.chisel-attachment-controls{display:flex;align-items:center;flex-wrap:wrap;gap:8px;padding:0 8px;color:var(--chisel-ui-secondary);font-size:12px;line-height:18px}.chisel-review-reply .chisel-attachment-controls{padding-right:48px}.chisel-attachment-error{color:var(--chisel-ui-danger);line-height:1.5}.chisel-attachment-previews,.review-attachments{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;padding:8px;max-height:240px;overflow:auto}.review-attachments{padding:12px 0 0;max-height:none}.feedback-shell.chisel-review .chisel-attachment-preview,.review-attachment{position:relative;min-width:0;margin:0;overflow:hidden;border:0;border-radius:8px;background:var(--chisel-ui-raised)}.chisel-attachment-preview>img,.chisel-attachment-preview>video,.review-media-thumbnail img,.review-media-thumbnail video{display:block;width:100%;height:120px;object-fit:contain;background:var(--chisel-ui-inset)}.review-attachment figcaption{padding:8px;font-size:12px;line-height:18px;overflow-wrap:anywhere}.review-attachment-placeholder{min-height:100px;display:grid;place-items:center;padding:12px;color:var(--chisel-ui-secondary);font-size:12px}.chisel-attachment-preview>button:not(.review-media-thumbnail){position:absolute;top:0;right:0;display:grid;place-items:center;width:40px;height:40px;padding:0;border:0;border-radius:0 8px;background:var(--chisel-ui-surface);color:var(--chisel-ui-text)}.review-retry-upload{display:inline-flex;align-items:center;gap:6px;min-height:40px;padding:0 8px;border:0;border-radius:8px;background:transparent;color:var(--chisel-ui-text);font:inherit}.review-retry-upload:hover{background:var(--chisel-ui-hover)}.chisel-attachment-controls .review-retry-upload[aria-label]{min-width:40px;justify-content:center}.chisel-attachment-preview>span{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding:8px;font-size:12px}.feedback-shell.chisel-review .chisel-expand-thread{display:inline-flex;align-items:center;margin:0 20px 8px 60px;min-height:40px;padding:0;border:0;background:transparent;color:var(--chisel-ui-secondary);font-size:13px}.chisel-review [data-slate-placeholder]{opacity:1!important;color:var(--chisel-ui-secondary)}@media(pointer:coarse){.feedback-shell.chisel-review .chisel-reply-submit{width:44px;height:44px}.chisel-review-reply .chisel-attachment-controls{padding-right:52px}.feedback-shell.chisel-review .chisel-reply-field{min-height:52px}.feedback-shell.chisel-review .chisel-review-reply :is(.review-composer-editor,.chisel-rich-editor){padding-right:60px}}@media(forced-colors:active){.feedback-shell.chisel-review .comment-thread[data-native-ui=true],.feedback-shell.chisel-review .comment-box-native,.feedback-shell.chisel-review .chisel-reply-field{border:1px solid CanvasText;box-shadow:none}.review-comment-avatar{border:1px solid CanvasText}}.review-media-thumbnail{position:relative;display:block;width:100%;padding:0;border:0;background:var(--chisel-ui-inset);cursor:pointer}.review-media-thumbnail img,.review-media-thumbnail video{display:block;width:100%;height:120px;object-fit:contain}.review-media-play{position:absolute;inset:50% auto auto 50%;display:grid;place-items:center;width:40px;height:40px;border-radius:50%;background:#0009;color:#fff;transform:translate(-50%,-50%)}.review-media-lightbox{position:fixed;inset:0;width:100vw;height:100dvh;max-width:none;max-height:none;margin:0;padding:24px;border:0;background:transparent;color:var(--chisel-ui-text)}.review-media-lightbox[open]{display:flex;align-items:center;justify-content:center}.review-media-lightbox::backdrop{background:#000b}.review-media-lightbox-content{display:flex;flex-direction:column;width:fit-content;min-width:min(320px,100%);max-width:min(100%,1000px);max-height:100%;margin:auto;border-radius:12px;overflow:hidden;background:var(--chisel-ui-surface);box-shadow:0 16px 48px #0006}.review-media-lightbox-content header{display:flex;align-items:center;gap:12px;min-height:56px;padding:8px 12px 8px 16px}.review-media-lightbox-content header span{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.review-media-lightbox-content header a{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:40px;min-height:40px;padding:0;border-radius:8px;color:var(--chisel-ui-text);font-size:13px}.review-media-lightbox-content header a:hover{background:var(--chisel-ui-hover)}.review-media-lightbox-content header button{display:grid;place-items:center;flex-shrink:0;width:40px;height:40px;padding:0;border:0;border-radius:8px;background:transparent;color:var(--chisel-ui-text)}.review-media-lightbox-content header button:hover{background:var(--chisel-ui-hover)}.review-media-lightbox-content>img,.review-media-lightbox-content>video{display:block;min-height:0;max-width:100%;max-height:calc(100dvh - 104px);margin:auto;object-fit:contain;background:#000}
3
+ `;import{Fragment as Le,jsx as d,jsxs as b}from"react/jsx-runtime";var x="https://chisel-review.vercel.app";function re(l,n){try{localStorage.setItem(l,n),sessionStorage.removeItem(l);return}catch{}try{sessionStorage.setItem(l,n)}catch{}}function X(l){try{localStorage.removeItem(l)}catch{}try{sessionStorage.removeItem(l)}catch{}}function Pe(l){let n="";try{n=localStorage.getItem(l)||""}catch{}if(!/^[0-9a-f]{64}$/.test(n))try{n=sessionStorage.getItem(l)||""}catch{}return/^[0-9a-f]{64}$/.test(n)?(re(l,n),n):""}function te(l){return Array.isArray(l)?l.filter(n=>n&&typeof n.id=="string"&&typeof n.name=="string"&&typeof n.path=="string").map(n=>({id:n.id,name:n.name.slice(0,100),path:n.path.slice(0,2048),image:typeof n.image=="string"&&/^https?:\/\//.test(n.image)?n.image:null})):[]}function Me({projectId:l}){let n=`chisel:session:${l}`,o=g(""),Y=g(n),[ne,O]=v(0),[m,j]=v(null),[ae,q]=v(!0),[P,k]=v(""),[oe,y]=v(!1),[K,S]=v([]),[se,N]=v([]),[ce,M]=v("connecting"),L=g("/"),z=g(!1),[le,de]=v(0),W=g(new Set),f=u(()=>{for(let e of W.current)e()},[]),T=g(null);T.current=m;let A=g(null),I=g(""),p=u(async(e,i,t,a,r=!1)=>{let c=o.current,h=await fetch(`${x}/chisel/api/hosted/${e}`,{method:i,headers:{Authorization:`Bearer ${c}`,...t?{"Content-Type":"application/json","X-Chisel-Protocol":"2"}:{}},...t?{body:JSON.stringify(t)}:{},signal:a,keepalive:r}),s=await h.json().catch(()=>null);if(c!==o.current)throw new Error("Your Chisel account changed. Please retry.");if(!h.ok)throw h.status===401&&c===o.current&&T.current&&y(!0),Object.assign(new Error(s?.error||"Chisel is unavailable. Please try again."),{status:h.status});if(!s||typeof s!="object")throw new Error("Chisel is unavailable. Please try again.");if(s.siteId&&s.siteId!==l)throw new Error("This session belongs to another project.");return s},[l]),w=u((e,i)=>p(e==="GET"?`comments?path=${encodeURIComponent(L.current)}`:"comments",e,i),[p]),B=H(()=>{let e=0;return{async list(){let i=++e,t=await w("GET");return i===e&&(S(t.comments),z.current||N(te(t.participants))),t.comments},async mutate(i){++e;let t=(await w("POST",i)).comment;return++e,S(a=>a.some(r=>r.id===t.id)?a.map(r=>r.id===t.id?t:r):[...a,t]),window.setTimeout(f,0),t},async delete(i){++e,await w("POST",{type:"delete",id:i}),++e,S(t=>t.filter(a=>a.id!==i)),window.setTimeout(f,0)},subscribe(i){return W.current.add(i),()=>{W.current.delete(i)}}}},[w,f]),he=u(async(e,i)=>{await w("POST",{type:"read",id:e,activity:i})},[w]),pe=u((e,i)=>{S(t=>t.map(a=>a.id===e?{...a,readActivity:Math.max(a.readActivity??0,i)}:a))},[]),{flush:Q,markDisplayed:ue}=Ce(he,pe);F(()=>{if(!m)return;let e=!0,i,t,a=new AbortController,r=()=>{document.visibilityState==="visible"&&(clearTimeout(t),t=setTimeout(()=>{f(),Q()},40))};async function c(){let s=!1;try{let C=o.current,$=await fetch(`${x}/chisel/api/hosted/events?path=${encodeURIComponent(L.current)}`,{headers:{Authorization:`Bearer ${C}`},signal:a.signal});if(e&&$.status===401&&C===o.current&&y(!0),!$.ok||!$.body)throw new Error("Live updates unavailable");let ge=$.body.getReader(),xe=new TextDecoder,E="";for(;e;){let{value:be,done:fe}=await ge.read();if(fe)break;E+=xe.decode(be,{stream:!0});let D;for(;(D=E.indexOf(`
133
4
 
134
- `))>=0;){let A=f.slice(0,$);if(f=f.slice($+2),A==="data: ready")L("connected"),a();else if(A==="data: change")a();else if(A.startsWith("data: presence:"))try{T.current=!0,G(H(JSON.parse(A.slice(15))))}catch{}}}}catch{}e&&(T.current=!1,L("reconnecting"),i=setTimeout(()=>{n()},2e3))}L("connecting"),n();let v=window.setInterval(a,1e4);return window.addEventListener("focus",a),window.addEventListener("online",a),document.addEventListener("visibilitychange",a),()=>{e=!1,T.current=!1,r.abort(),clearTimeout(i),clearTimeout(t),window.clearInterval(v),window.removeEventListener("focus",a),window.removeEventListener("online",a),document.removeEventListener("visibilitychange",a)}},[m,Z,u,D]);let oe=x(e=>{C.current!==e&&(C.current=e,ee(i=>i+1),u())},[u]),F=U(()=>({async userId(){return E.current?.user.id??""},async discard(e){await d("attachments","POST",{action:"cancel",id:e.id},void 0,!0)},async send(e,i,t){let r=async()=>{let n=await d("attachments","POST",{action:"confirm",id:e.id});if(n.attachment?.id!==e.id||n.attachment.token!==e.id)throw new Error("The upload confirmation was incomplete. Please retry.");return e.id};if(i){if(!t())return"";try{return await r()}catch(n){if(![404,503].includes(n.status??0))throw n}}let a=await d("attachments","POST",{action:"prepare",id:e.id,name:e.name,mimeType:e.mimeType,size:e.size});if(a.attachment?.id===e.id&&a.attachment.token===e.id)return e.id;if(typeof a.url!="string"||typeof a.key!="string")throw new Error("The upload could not be started. Please retry.");if(!t())return"";if(!e.stored){let n=new FormData;n.append("cacheControl","3600"),n.append("",e.file);try{(await fetch(a.url,{method:"PUT",headers:{apikey:a.key,"x-upsert":"false"},body:n})).ok&&(e.stored=!0)}catch{}}return t()?r():""}}),[d]),J=U(()=>function(i=!1,t=0){return we(i,t,F)},[F]),ne=x(async(e,i)=>{let t=await d(`attachments?id=${encodeURIComponent(e)}`,"GET",void 0,i);if(typeof t.url!="string")throw new Error("Attachment unavailable");return t.url},[d]);O(()=>{if(c.current=sessionStorage.getItem(o)||"",!c.current){y(!1);return}let e=!0;return l("GET").then(i=>{e&&N(i)}).catch(()=>{e&&(c.current="",sessionStorage.removeItem(o))}).finally(()=>{e&&y(!1)}),()=>{e=!1}},[o,l]),O(()=>{let e=i=>{i.origin!==b||i.source!==P.current||i.data?.type!=="chisel:session"||i.data.state!==z.current||!/^[0-9a-f]{64}$/.test(i.data.token)||(c.current=i.data.token,sessionStorage.setItem(o,c.current),z.current="",P.current=null,E.current||y(!0),I(""),l("GET").then(t=>{N(t),k(!1);let r=new URL(window.location.href);r.searchParams.delete("invite"),window.history.replaceState(window.history.state,"",r)}).catch(t=>{c.current="",sessionStorage.removeItem(o),I(t instanceof Error?t.message:"Could not connect to Chisel.")}).finally(()=>y(!1)))};return window.addEventListener("message",e),()=>window.removeEventListener("message",e)},[o,l]);function V(){let e=crypto.getRandomValues(new Uint8Array(32));z.current=Array.from(e,r=>r.toString(16).padStart(2,"0")).join("");let i=new URL("/chisel/connect",b);i.searchParams.set("site",p),i.searchParams.set("origin",window.location.origin),i.searchParams.set("state",z.current);let t=new URLSearchParams(window.location.search).get("invite");t&&i.searchParams.set("invite",t),P.current=window.open(i.toString(),"chisel-sign-in","popup,width=520,height=700"),P.current||I("Allow pop-ups for this site, then try again.")}return q?g("main",{className:"chisel-sign-in",children:[s(_,{}),s("section",{children:s("p",{children:"Connecting to Chisel\u2026"})})]}):m?g(me,{children:[g("style",{children:[W,X]}),s(ue,{user:m.user,commentStore:ie,canManageComments:m.role==="owner",commentUI:{className:"chisel-review",renderComposer:e=>{let i=M.find(t=>t.id===e.commentId);return s(ve,{...e,defaultBody:i?.body,existingAttachments:i?.attachments,useAttachments:J})},renderThread:e=>s(xe,{...e,onRead:ae,useAttachments:J,loadMedia:ne},e.comment.id)},collaboration:{status:K,participants:Y,setPage:oe,markRead(){},unreadThreadIds:M.filter(e=>e.readActivity!==null&&e.readActivity<e.activity).map(e=>e.id)},getReviewLink:m.role==="owner"?async(e=!1)=>{let i=await fetch(`${b}/chisel/api/hosted/invite`,{method:"POST",headers:{Authorization:`Bearer ${c.current}`,"Content-Type":"application/json","X-Chisel-Protocol":"2"},body:JSON.stringify({rotate:e})}),t=await i.json();if(i.status===401&&k(!0),!i.ok||typeof t.invite!="string")throw new Error(t.error||"Could not create a review link.");let r=new URL("/chisel",window.location.origin);return r.searchParams.set("mode","comment"),r.searchParams.set("invite",t.invite),r.toString()}:void 0,validateSession:async()=>!!c.current,signOut:async()=>{let e=await fetch(`${b}/chisel/api/hosted/logout`,{method:"DELETE",headers:{Authorization:`Bearer ${c.current}`,"X-Chisel-Protocol":"2"}});if(!e.ok&&e.status!==401)throw new Error("Sign out failed.");c.current="",sessionStorage.removeItem(o)}}),Q&&g("div",{role:"alert",style:{position:"fixed",bottom:16,right:16,zIndex:999999,background:"white",color:"black",padding:12,borderRadius:8,boxShadow:"0 2px 12px #0003"},children:["Your Chisel session expired. ",s("button",{type:"button",onClick:V,children:"Sign in again"})]})]}):g("main",{className:"chisel-sign-in",children:[s(_,{}),g("section",{children:[s("strong",{className:"chisel-sign-in-brand",children:"chisel"}),s("h1",{children:"Sign in to review"}),s("p",{children:"Connect this website to your Chisel account."}),s("button",{onClick:V,children:"Continue with Chisel"}),B&&s("p",{role:"alert",children:B})]})]})}export{Pe as HostedChisel};
5
+ `))>=0;){let R=E.slice(0,D);if(E=E.slice(D+2),R==="data: ready")M("connected"),r();else if(R==="data: renew")s=!0;else if(R==="data: change")r();else if(R.startsWith("data: presence:"))try{z.current=!0,N(te(JSON.parse(R.slice(15))))}catch{}}}}catch{}e&&(z.current=!1,s||M("reconnecting"),i=setTimeout(()=>{c()},s?0:2e3))}M(s=>s==="connected"?s:"connecting"),c();let h=window.setInterval(r,1e4);return window.addEventListener("focus",r),window.addEventListener("online",r),document.addEventListener("visibilitychange",r),()=>{e=!1,z.current=!1,a.abort(),clearTimeout(i),clearTimeout(t),window.clearInterval(h),window.removeEventListener("focus",r),window.removeEventListener("online",r),document.removeEventListener("visibilitychange",r)}},[m,le,f,Q]);let me=u(e=>{L.current!==e&&(L.current=e,de(i=>i+1),f())},[f]),U=H(()=>({async userId(){return T.current?.user.id??""},async discard(e){await p("attachments","POST",{action:"cancel",id:e.id},void 0,!0)},async send(e,i,t){let a=async()=>{let c=await p("attachments","POST",{action:"confirm",id:e.id});if(c.attachment?.id!==e.id||c.attachment.token!==e.id)throw new Error("The upload confirmation was incomplete. Please retry.");return e.id};if(i){if(!t())return"";try{return await a()}catch(c){if(![404,503].includes(c.status??0))throw c}}let r=await p("attachments","POST",{action:"prepare",id:e.id,name:e.name,mimeType:e.mimeType,size:e.size});if(r.attachment?.id===e.id&&r.attachment.token===e.id)return e.id;if(typeof r.url!="string"||typeof r.key!="string")throw new Error("The upload could not be started. Please retry.");if(!t())return"";if(!e.stored){let c=new FormData;c.append("cacheControl","3600"),c.append("",e.file);try{(await fetch(r.url,{method:"PUT",headers:{apikey:r.key,"x-upsert":"false"},body:c})).ok&&(e.stored=!0)}catch{}}return t()?a():""}}),[p]),Z=H(()=>function(i=!1,t=0){return Te(i,t,U)},[U]),V=u(async(e,i)=>{let t=await p(`attachments?id=${encodeURIComponent(e)}`,"GET",void 0,i);if(typeof t.url!="string")throw new Error("Attachment unavailable");return t.url},[p]),G=u((e,i)=>p("project",e==="get"?"GET":"POST",e==="get"?void 0:{...i,action:e}),[p]),ve=u((e,i)=>Ee(i,U,t=>B.mutate({type:"snapshot",id:e,attachmentTokens:[t]})),[U,B]),we=u(e=>V(e,new AbortController().signal),[V]);F(()=>{Y.current!==n&&(o.current="",Y.current=n,j(null)),o.current=o.current||Pe(n);let e=!0,i=!1,t;q(!!o.current&&!T.current);async function a(){if(i||!o.current)return;clearTimeout(t),i=!0;let h=o.current;try{let s=await w("GET");if(!s.user?.id||s.siteId!==l||!["owner","reviewer"].includes(s.role))throw new Error("Could not verify your Chisel session. Please retry.");if(!e||h!==o.current)return;j(s),y(!1),k("");let C=new URL(window.location.href);C.searchParams.delete("invite"),window.history.replaceState(window.history.state,"",C)}catch(s){if(!e||h!==o.current)return;s.status===401?(o.current="",X(n),y(!!T.current)):t=setTimeout(()=>{document.visibilityState==="visible"&&a()},5e3),k(s instanceof Error?s.message:"Could not connect to Chisel.")}finally{i=!1,e&&q(!1)}}let r=()=>{document.visibilityState==="visible"&&a()},c=h=>{if(!(h.key!==n&&h.key!==null)){try{if(h.storageArea!==localStorage)return}catch{return}o.current=h.newValue&&/^[0-9a-f]{64}$/.test(h.newValue)?h.newValue:"",o.current||X(n),j(null),S([]),N([]),y(!1),k(""),O(s=>s+1)}};return o.current&&a(),window.addEventListener("online",r),window.addEventListener("focus",r),document.addEventListener("visibilitychange",r),window.addEventListener("storage",c),()=>{e=!1,clearTimeout(t),window.removeEventListener("online",r),window.removeEventListener("focus",r),document.removeEventListener("visibilitychange",r),window.removeEventListener("storage",c)}},[n,l,w,ne]),F(()=>{let e=i=>{i.origin!==x||i.source!==A.current||i.data?.type!=="chisel:session"||i.data.state!==I.current||!/^[0-9a-f]{64}$/.test(i.data.token)||(o.current=i.data.token,re(n,o.current),I.current="",A.current=null,k(""),O(t=>t+1))};return window.addEventListener("message",e),()=>window.removeEventListener("message",e)},[n]);function _(){let e=crypto.getRandomValues(new Uint8Array(32));I.current=Array.from(e,a=>a.toString(16).padStart(2,"0")).join("");let i=new URL("/chisel/connect",x);i.searchParams.set("site",l),i.searchParams.set("origin",window.location.origin),i.searchParams.set("state",I.current);let t=new URLSearchParams(window.location.search).get("invite");t&&i.searchParams.set("invite",t),A.current=window.open(i.toString(),"chisel-sign-in","popup,width=520,height=700"),A.current||k("Allow pop-ups for this site, then try again.")}return ae?b("main",{className:"chisel-sign-in",children:[d(J,{}),d("section",{children:d("p",{children:"Connecting to Chisel\u2026"})})]}):!m&&o.current?b("main",{className:"chisel-sign-in",children:[d(J,{}),b("section",{children:[d("strong",{className:"chisel-sign-in-brand",children:"chisel"}),d("h1",{children:"Reconnecting to Chisel"}),d("p",{children:"Your sign-in is saved. Check your connection and try again."}),d("button",{onClick:()=>O(e=>e+1),children:"Retry connection"}),P&&d("p",{role:"alert",children:P})]})]}):m?b(Le,{children:[b("style",{children:[ee,ie]}),d(ye,{user:m.user,commentStore:B,canManageComments:m.role==="owner",projectId:l,manageReview:G,saveSnapshot:ve,getAttachmentURL:we,accountURL:`${x}/chisel/projects`,agentURL:`${x}/chisel/mcp`,commentUI:{className:"chisel-review",renderComposer:e=>{let i=K.find(t=>t.id===e.commentId);return d(ke,{...e,defaultBody:i?.body,existingAttachments:i?.attachments,useAttachments:Z})},renderThread:e=>d(Se,{...e,onRead:ue,useAttachments:Z,loadMedia:V},e.comment.id)},collaboration:{status:ce,participants:se,setPage:me,markRead(){},unreadThreadIds:K.filter(e=>e.readActivity!==null&&e.readActivity<e.activity).map(e=>e.id)},getReviewLink:m.role==="owner"?async(e=!1)=>{let i=await G("get"),t=i.reviewURL??Re(window.location.origin);if(!t)throw new Error("Open the deployed website\u2019s HTTPS /chisel page to copy a review link. Localhost links cannot be shared with clients.");i.reviewURL||await G("settings",{reviewURL:t});let a=await fetch(`${x}/chisel/api/hosted/invite`,{method:"POST",headers:{Authorization:`Bearer ${o.current}`,"Content-Type":"application/json","X-Chisel-Protocol":"2"},body:JSON.stringify({rotate:e})}),r=await a.json();if(a.status===401&&y(!0),!a.ok||typeof r.invite!="string")throw new Error(r.error||"Could not create a review link.");let c=new URL(t);return c.searchParams.set("mode","comment"),c.searchParams.set("invite",r.invite),c.toString()}:void 0,validateSession:async()=>!!o.current,signOut:async()=>{let e=await fetch(`${x}/chisel/api/hosted/logout`,{method:"DELETE",headers:{Authorization:`Bearer ${o.current}`,"X-Chisel-Protocol":"2"}});if(!e.ok&&e.status!==401)throw new Error("Sign out failed.");o.current="",X(n)}},m.user.id),oe&&b("div",{role:"alert",style:{position:"fixed",bottom:16,right:16,zIndex:999999,background:"white",color:"black",padding:12,borderRadius:8,boxShadow:"0 2px 12px #0003"},children:["Your Chisel session expired. ",d("button",{type:"button",onClick:_,children:"Sign in again"})]})]}):b("main",{className:"chisel-sign-in",children:[d(J,{}),b("section",{children:[d("strong",{className:"chisel-sign-in-brand",children:"chisel"}),d("h1",{children:"Sign in to review"}),d("p",{children:"Connect this website to your Chisel account."}),d("button",{onClick:_,children:"Continue with Chisel"}),P&&d("p",{role:"alert",children:P})]})]})}export{Me as HostedChisel};