use-chisel 0.1.3 → 0.1.5

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
@@ -44,8 +44,19 @@ Supports Next.js 14 through 16 with React 18 or 19, including both App Router an
44
44
  npm install use-chisel
45
45
  ```
46
46
 
47
- Run `npx chisel` to open the hosted Chisel sign-in page. Connecting your project
48
- to the hosted workspace is not available yet.
47
+ Run `npx chisel` from a Next.js App Router project to sign in and connect it to
48
+ hosted Chisel. The command adds a `/chisel` route and a preview component to the
49
+ app layout, and saves the project's public ID in `.chisel.json`. It does not
50
+ overwrite an existing `/chisel` route. Restart your development server and open
51
+ `/chisel` to sign in and review the site. Shared comments are stored by Chisel's
52
+ hosted service; project credentials and Supabase service keys are not required
53
+ in the client project. The hosted workspace supports rich text, attachments,
54
+ replies, resolution, review links, reviewer presence, and unread indicators.
55
+
56
+ The generated route defaults off in production. To use it on a deployed review
57
+ site, set `CHISEL_ENABLED=1` for that deployment and redeploy. Hosted connections
58
+ require HTTPS, except on localhost. Automatic setup currently requires an App
59
+ Router layout containing a single `</body>` tag.
49
60
 
50
61
  Run `npx chisel enable` or `npx chisel disable` in your project to update its
51
62
  local `.env.local` settings. Restart the development server afterwards. These
package/bin/chisel.js CHANGED
@@ -1,8 +1,51 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawn } from "node:child_process";
3
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
4
+ import { randomBytes, randomUUID } from "node:crypto";
5
+ import { createServer } from "node:http";
4
6
  import { join } from "node:path";
5
7
 
8
+ function openBrowser(url) {
9
+ console.log(`Open ${url} to continue in your browser.`);
10
+ const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "rundll32" : "xdg-open";
11
+ const args = process.platform === "win32" ? ["url.dll,FileProtocolHandler", url] : [url];
12
+ const browser = spawn(command, args, { detached: true, stdio: "ignore" });
13
+ browser.on("error", () => console.error("Could not open a browser. Open the link above instead."));
14
+ browser.unref();
15
+ }
16
+
17
+ function scaffoldInfo() {
18
+ const app = ["app", "src/app"].find(dir => existsSync(dir));
19
+ const layout = app && ["layout.tsx", "layout.jsx", "layout.js"].map(file => join(app, file)).find(existsSync);
20
+ if (!layout) throw new Error("Chisel currently sets up Next.js App Router projects with an app/layout file.");
21
+ const source = readFileSync(layout, "utf8");
22
+ if ((source.match(/<\/body\s*>/g) || []).length !== 1 || source.includes("ChiselConnectedPreview")) throw new Error("Could not safely update the app layout. Add ChiselPreview to the layout and create the /chisel route manually.");
23
+ const directory = join(app, "chisel");
24
+ if (["page.tsx", "page.jsx", "page.js"].some(file => existsSync(join(directory, file)))) throw new Error("The /chisel route already exists. Chisel will not overwrite it.");
25
+ return { layout, source, directory, extension: layout.endsWith(".tsx") ? "tsx" : "jsx" };
26
+ }
27
+
28
+ function scaffoldProject(info, projectId, configFile) {
29
+ const closingBody = info.source.match(/<\/body\s*>/)[0];
30
+ const updated = info.source.replace(closingBody, `<ChiselConnectedPreview />\n ${closingBody}`);
31
+ const directive = updated.match(/^\s*["']use client["'];?\s*/)?.[0] || "";
32
+ const layout = `${directive}import { ChiselPreview as ChiselConnectedPreview } from "use-chisel/workspace";\n${updated.slice(directive.length)}`;
33
+ const page = `import { notFound } from "next/navigation";\nimport { HostedChisel } from "use-chisel/hosted";\n\nexport default function ChiselPage() {\n const enabled = process.env.CHISEL_ENABLED === undefined ? process.env.NODE_ENV !== "production" : process.env.CHISEL_ENABLED === "1";\n if (!enabled) notFound();\n return <HostedChisel projectId="${projectId}" />;\n}\n`;
34
+ const pageFile = join(info.directory, `page.${info.extension}`);
35
+ if (readFileSync(info.layout, "utf8") !== info.source) throw new Error("The layout changed during setup. Run npx chisel again.");
36
+ mkdirSync(info.directory, { recursive: true });
37
+ writeFileSync(pageFile, page, { flag: "wx" });
38
+ let layoutWritten = false;
39
+ try {
40
+ writeFileSync(info.layout, layout); layoutWritten = true;
41
+ writeFileSync(configFile, JSON.stringify({ projectId }, null, 2) + "\n", { flag: "wx", mode: 0o600 });
42
+ } catch (error) {
43
+ if (layoutWritten && existsSync(info.layout) && readFileSync(info.layout, "utf8") === layout) writeFileSync(info.layout, info.source);
44
+ if (existsSync(pageFile) && readFileSync(pageFile, "utf8") === page) unlinkSync(pageFile);
45
+ throw error;
46
+ }
47
+ }
48
+
6
49
  const action = process.argv[2];
7
50
  if (action === "enable" || action === "disable") {
8
51
  if (!existsSync("package.json")) {
@@ -27,11 +70,41 @@ if (action === "enable" || action === "disable") {
27
70
  console.error("Usage: npx chisel [enable|disable]");
28
71
  process.exitCode = 1;
29
72
  } else {
30
- const url = "https://chisel-review.vercel.app/chisel/start";
31
- console.log(`Open ${url} to sign in to Chisel.`);
32
- const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "rundll32" : "xdg-open";
33
- const args = process.platform === "win32" ? ["url.dll,FileProtocolHandler", url] : [url];
34
- const browser = spawn(command, args, { detached: true, stdio: "ignore" });
35
- browser.on("error", () => console.error("Could not open a browser. Open the link above instead."));
36
- browser.unref();
73
+ const configFile = join(process.cwd(), ".chisel.json");
74
+ if (!existsSync("package.json")) {
75
+ openBrowser("https://chisel-review.vercel.app/chisel/start");
76
+ } else if (existsSync(configFile)) {
77
+ console.log("This project is already connected. Start your development server and open /chisel.");
78
+ } else {
79
+ let name, info;
80
+ try { name = JSON.parse(readFileSync("package.json", "utf8")).name || "My project"; }
81
+ catch { console.error("Could not read this project's package.json."); process.exit(1); }
82
+ try { info = scaffoldInfo(); }
83
+ catch (error) { console.error(error.message); process.exit(1); }
84
+ if (typeof name !== "string" || !name.trim()) name = "My project";
85
+ const state = randomBytes(32).toString("hex"), requestId = randomUUID();
86
+ const server = createServer((request, response) => {
87
+ let url;
88
+ try { url = new URL(request.url || "/", "http://127.0.0.1"); }
89
+ catch { response.writeHead(400).end("Invalid connection request."); return; }
90
+ const project = url.searchParams.get("project");
91
+ if (request.method !== "GET" || url.pathname !== "/" || url.searchParams.get("state") !== state || !project || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(project)) {
92
+ response.writeHead(400).end("Invalid connection request."); return;
93
+ }
94
+ try { scaffoldProject(info, project, configFile); }
95
+ catch (error) { console.error(`Could not finish project setup: ${error.message}`); response.writeHead(500).end("Could not finish project setup. Return to your terminal."); return; }
96
+ response.writeHead(200, { "Content-Type": "text/plain", "Cache-Control": "no-store" }).end("Project connected. You can return to your terminal.");
97
+ console.log("Project connected. Start your development server and open /chisel.");
98
+ clearTimeout(timeout); server.close();
99
+ });
100
+ server.on("error", () => { console.error("Could not start the local connection listener."); process.exitCode = 1; });
101
+ server.listen(0, "127.0.0.1", () => {
102
+ const url = new URL("https://chisel-review.vercel.app/chisel/start");
103
+ url.search = new URLSearchParams({ state, port: String(server.address().port), request: requestId, name: name.trim().slice(0, 120) }).toString();
104
+ openBrowser(url.toString());
105
+ console.log("Waiting for project confirmation. Press Ctrl+C to cancel.");
106
+ });
107
+ const timeout = setTimeout(() => { console.error("Project connection timed out. Run npx chisel again to retry."); server.close(); process.exitCode = 1; }, 300000);
108
+ timeout.unref();
109
+ }
37
110
  }
@@ -0,0 +1,3 @@
1
+ export declare function HostedChisel({ projectId }: {
2
+ projectId: string;
3
+ }): import("react").JSX.Element;
@@ -0,0 +1,134 @@
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(`
133
+
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};
@@ -0,0 +1,35 @@
1
+ export type Upload = {
2
+ id: string;
3
+ name: string;
4
+ mimeType: string;
5
+ size: number;
6
+ token?: string;
7
+ preview: string;
8
+ bucket: string;
9
+ path: string;
10
+ file: File;
11
+ stored: boolean;
12
+ status: "uploading" | "ready" | "failed";
13
+ };
14
+ export type AttachmentTransport = {
15
+ userId(): Promise<string>;
16
+ send(file: Upload, retrying: boolean, isActive: () => boolean): Promise<string>;
17
+ discard(file: Upload): Promise<void>;
18
+ };
19
+ export declare function useReviewAttachments(disabled: boolean | undefined, existingCount: number | undefined, transport: AttachmentTransport): {
20
+ attachments: Upload[];
21
+ uploading: boolean;
22
+ blocked: boolean;
23
+ controls: import("react").JSX.Element;
24
+ previewsUI: false | import("react").JSX.Element;
25
+ clear: () => void;
26
+ upload: (selectedFiles: FileList | null) => Promise<void>;
27
+ previews: {
28
+ id: string;
29
+ name: string;
30
+ mimeType: string;
31
+ size: number;
32
+ previewUrl: string;
33
+ }[];
34
+ tokens: string[];
35
+ };
@@ -0,0 +1,36 @@
1
+ import { type ReactNode } from "react";
2
+ import type { WorkspaceComposerProps, WorkspaceThreadProps } from "use-chisel/workspace";
3
+ import { type ReviewBody as CommentBody } from "./content";
4
+ import type { ReviewAttachment } from "./types";
5
+ export type ReviewAttachments = {
6
+ attachments: readonly unknown[];
7
+ uploading: boolean;
8
+ blocked: boolean;
9
+ controls: ReactNode;
10
+ previewsUI: ReactNode;
11
+ clear(): void;
12
+ upload(files: FileList | null): Promise<void>;
13
+ previews: {
14
+ id: string;
15
+ name: string;
16
+ mimeType: string;
17
+ size: number;
18
+ previewUrl?: string;
19
+ }[];
20
+ tokens: string[];
21
+ };
22
+ export type UseReviewAttachments = (disabled?: boolean, existingCount?: number) => ReviewAttachments;
23
+ export type LoadReviewMedia = (id: string, signal: AbortSignal) => Promise<string>;
24
+ export declare function ReviewBody({ body }: {
25
+ body: CommentBody;
26
+ }): import("react").JSX.Element;
27
+ export declare function ReviewComposer(props: WorkspaceComposerProps & {
28
+ defaultBody?: CommentBody;
29
+ existingAttachments?: ReviewAttachment[];
30
+ useAttachments: UseReviewAttachments;
31
+ }): import("react").JSX.Element;
32
+ export declare function ReviewThread(props: WorkspaceThreadProps & {
33
+ onRead(id: string, activity: number): void;
34
+ useAttachments: UseReviewAttachments;
35
+ loadMedia: LoadReviewMedia;
36
+ }): import("react").JSX.Element;
@@ -0,0 +1,24 @@
1
+ export type ReviewText = {
2
+ text: string;
3
+ bold?: boolean;
4
+ italic?: boolean;
5
+ strikethrough?: boolean;
6
+ code?: boolean;
7
+ };
8
+ export type ReviewLink = {
9
+ type: "link";
10
+ url: string;
11
+ text?: string;
12
+ };
13
+ export type ReviewBody = {
14
+ version: 1;
15
+ content: {
16
+ type: "paragraph";
17
+ children: (ReviewText | ReviewLink)[];
18
+ }[];
19
+ };
20
+ type CommentBody = ReviewBody;
21
+ export declare function commentBody(text: string): CommentBody;
22
+ export declare function bodyText(body: CommentBody): string;
23
+ export declare function trimCommentBody(body: CommentBody): CommentBody;
24
+ export {};
@@ -0,0 +1,44 @@
1
+ import { type BaseEditor, type Descendant } from "slate";
2
+ import { type HistoryEditor } from "slate-history";
3
+ import { ReactEditor } from "slate-react";
4
+ import { type ReviewBody as CommentBody } from "./content";
5
+ type Mark = "bold" | "italic" | "strikethrough" | "code";
6
+ type ReviewText = {
7
+ text: string;
8
+ } & Partial<Record<Mark, boolean>>;
9
+ type LinkElement = {
10
+ type: "link";
11
+ url: string;
12
+ children: ReviewText[];
13
+ };
14
+ type ParagraphElement = {
15
+ type: "paragraph";
16
+ children: (ReviewText | LinkElement)[];
17
+ };
18
+ declare module "slate" {
19
+ interface CustomTypes {
20
+ Editor: BaseEditor & ReactEditor & HistoryEditor;
21
+ Element: ParagraphElement | LinkElement;
22
+ Text: ReviewText;
23
+ }
24
+ }
25
+ export declare function bodyToEditor(body: CommentBody): Descendant[];
26
+ export declare function editorToBody(value: Descendant[]): CommentBody;
27
+ export declare function createReviewEditor(): BaseEditor & ReactEditor & HistoryEditor;
28
+ export declare function ReviewAttachmentButton({ onFiles, disabled }: {
29
+ onFiles(files: FileList | null): Promise<void>;
30
+ disabled?: boolean;
31
+ }): import("react").JSX.Element;
32
+ export declare function ReviewEditor({ text, defaultBody, disabled, label, placeholder, autoFocus, onChange, onSubmit, onFiles, uploading }: {
33
+ text: string;
34
+ defaultBody?: CommentBody;
35
+ disabled?: boolean;
36
+ label: string;
37
+ placeholder: string;
38
+ autoFocus?: boolean;
39
+ onChange(text: string, body: CommentBody): void;
40
+ onSubmit(): void;
41
+ onFiles?(files: FileList | null): Promise<void>;
42
+ uploading?: boolean;
43
+ }): import("react").JSX.Element;
44
+ export {};
@@ -0,0 +1,8 @@
1
+ type Media = {
2
+ src: string;
3
+ name: string;
4
+ mimeType: string;
5
+ onError?(): void;
6
+ };
7
+ export declare function MediaPreview({ src, name, mimeType, onError }: Media): import("react").JSX.Element;
8
+ export {};
@@ -0,0 +1,9 @@
1
+ export declare const imageLimit: number;
2
+ export declare const videoLimit: number;
3
+ export declare const imageTypes: string[];
4
+ export declare const videoTypes: string[];
5
+ export declare const mediaAccept: string;
6
+ export declare function mediaFileError(file: {
7
+ type: string;
8
+ size: number;
9
+ }): string | null;
@@ -0,0 +1,4 @@
1
+ export declare function useReadQueue(send: (id: string, activity: number) => Promise<unknown>, apply: (id: string, activity: number) => void): {
2
+ flush: () => Promise<void>;
3
+ markDisplayed: (id: string, activity: number) => void;
4
+ };
@@ -0,0 +1 @@
1
+ export declare function displayedActivity(readActivity: number, activities: readonly number[], displayed: ReadonlySet<number>): number;
@@ -0,0 +1,21 @@
1
+ import type { WorkspaceAttachment, WorkspaceComment, WorkspaceReply } from "use-chisel/workspace";
2
+ import type { ReviewBody } from "./content";
3
+ export type ReviewAttachment = WorkspaceAttachment & {
4
+ bucket: string;
5
+ path: string;
6
+ };
7
+ export type ReviewReply = Omit<WorkspaceReply, "attachments"> & {
8
+ id: string;
9
+ body: ReviewBody;
10
+ date: string;
11
+ activity: number;
12
+ attachments: ReviewAttachment[];
13
+ };
14
+ export type ReviewComment = Omit<WorkspaceComment, "replies" | "attachments"> & {
15
+ attachments: ReviewAttachment[];
16
+ body: ReviewBody;
17
+ rootActivity: number;
18
+ activity: number;
19
+ readActivity: number | null;
20
+ replies: ReviewReply[];
21
+ };
@@ -0,0 +1,2 @@
1
+ export * from "./review/content";
2
+ export * from "./review/media-limits";
@@ -0,0 +1,3 @@
1
+ function s(t){return{version:1,content:t.split(`
2
+ `).map(e=>({type:"paragraph",children:[{text:e}]}))}}function a(t){return t.content.map(e=>e.children.map(i=>"type"in i?i.text||i.url:i.text).join("")).join(`
3
+ `)}function g(t){let e=t.content.slice();for(;e.length;){let i=e[e.length-1],n=i.children.slice();for(;n.length;){let o=n[n.length-1],m="type"in o?o.text||o.url:o.text,r=m.trimEnd();if(r)return n[n.length-1]=r===m?o:{...o,text:r},e[e.length-1]={...i,children:n},{...t,content:e};n.pop()}e.pop()}return s("")}var u=5242880,d=20971520,p=["image/jpeg","image/png","image/gif","image/webp","image/avif","image/bmp"],c=["video/mp4","video/webm","video/quicktime"],x=[...p,...c].join(",");function h(t){let e=p.includes(t.type);return!e&&!c.includes(t.type)?"Choose a JPG, PNG, GIF, WebP, AVIF, BMP, MP4, WebM, or MOV file.":!Number.isSafeInteger(t.size)||t.size<=0?"This file is empty or invalid.":t.size>(e?5242880:20971520)?`${e?"Images must be 5 MB":"Videos must be 20 MB"} or smaller.`:null}export{a as bodyText,s as commentBody,u as imageLimit,p as imageTypes,x as mediaAccept,h as mediaFileError,g as trimCommentBody,d as videoLimit,c as videoTypes};
@@ -0,0 +1,8 @@
1
+ export { ReviewComposer, ReviewThread, ReviewBody } from "./review/comment-ui";
2
+ export type { UseReviewAttachments, ReviewAttachments, LoadReviewMedia } from "./review/comment-ui";
3
+ export { ReviewEditor, ReviewAttachmentButton, bodyToEditor, editorToBody, createReviewEditor } from "./review/editor";
4
+ export { displayedActivity } from "./review/read-state";
5
+ export { useReadQueue } from "./review/read-queue";
6
+ export type { ReviewComment, ReviewReply, ReviewAttachment } from "./review/types";
7
+ export { useReviewAttachments } from "./review/attachments";
8
+ export type { Upload, AttachmentTransport } from "./review/attachments";
@@ -0,0 +1,31 @@
1
+ "use client";import{jsx as n,Fragment as fa,jsxs as M}from"react/jsx-runtime";import{forwardRef as Xe,createElement as Ke}from"react";var ne=e=>e?.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase();function pe(e,a,o=[]){if(a==null)throw new Error("[lucide]: iconNode is required when icon name is used");return{name:ne(e),size:24,node:a,...o.length>0?{aliases:o}:{}}}var me=e=>{let a="",o=!1;for(let t of e){if(t==="-"||t==="_"||t<=" "){o=a.length>0;continue}a.length===0?a+=t.toLowerCase():a+=o?t.toUpperCase():t,o=!1}return a};var Le=e=>{let a=me(e);return a.charAt(0).toUpperCase()+a.slice(1)};import{forwardRef as Ne,createElement as ge}from"react";var O=(...e)=>e.filter((a,o,t)=>!!a&&a.trim()!==""&&t.indexOf(a)===o).join(" ").trim();var R={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":2,"stroke-linecap":"round","stroke-linejoin":"round"};function te(e){return e!=null}function Ie(e,a={}){let o=a.attributeNames??{},t=d=>o[d]??d,l=e.size??e.width??R.width,u=e.size??e.height??R.height,f=e.aliases?.filter(d=>typeof d=="string"&&d.trim()!=="").map(d=>`lucide-${d}`)??[],c=[...e.name?[`lucide-${e.name}`]:[],...f],i=a.className?.split(" ").filter(Boolean)??[],p=a.includeDefaultClasses===!1?O(...i):O("lucide",...c,...i),r=a.absoluteStrokeWidth?Number(a.strokeWidth??R["stroke-width"])*Number(e.size??e.width??R.width)/Number(a.size??a.width??R.width):a.strokeWidth??R["stroke-width"];return["svg",{...Object.entries(R).reduce((d,[s,m])=>(d[t(s)]=m,d),{}),..."color"in a&&a.color&&{[t("stroke")]:a.color},..."size"in a&&te(a.size)&&{[t("width")]:a.size,[t("height")]:a.size},..."width"in a&&te(a.width)&&{[t("width")]:a.width},..."height"in a&&te(a.height)&&{[t("height")]:a.height},[t("stroke-width")]:r,...p&&{[t("class")]:p},[t("viewBox")]:`0 0 ${l} ${u}`,...a.hasA11yProp===!1?{[t("aria-hidden")]:"true"}:{},..."attributes"in a&&a.attributes},e.node.map(d=>{let[s,m,x]=d,C=a.nonScalingStroke?{[t("vector-effect")]:"non-scaling-stroke",...m}:m;return x?[s,C,x]:[s,C]})]}function xe(e,a={}){return Ie(e,{...a,attributeNames:{...a.attributeNames,class:"className","stroke-width":"strokeWidth","stroke-linecap":"strokeLinecap","stroke-linejoin":"strokeLinejoin","vector-effect":"vectorEffect"}})}var Ce=e=>{for(let a in e)if(a.startsWith("aria-")||a==="role"||a==="title")return!0;return!1};import{createContext as We,useContext as Ve,useMemo as va,createElement as qa}from"react";var ze=We({});var he=()=>Ve(ze);var Se=Ne(({color:e,size:a,width:o,height:t,strokeWidth:l,absoluteStrokeWidth:u,nonScalingStroke:f,className:c="",children:i,iconNode:p=[],icon:r={node:p,aliases:[],size:24},...I},d)=>{let{size:s=24,strokeWidth:m=2,absoluteStrokeWidth:x=!1,nonScalingStroke:C=!1,color:F="currentColor",className:_=""}=he()??{},X=!!i||Ce(I),[$,Y,L=[]]=xe(r,{color:e??F,width:o??a??s,height:t??a??s,strokeWidth:l??m,absoluteStrokeWidth:u??x,nonScalingStroke:f??C,className:O(_,c),hasA11yProp:X,attributes:I});return ge($,{ref:d,...Y},[...L.map(([g,y])=>ge(g,y)),...Array.isArray(i)?i:[i]])});function P(e,a=[],o=[]){let t=typeof e=="string"?pe(e,a,o):e,l=Xe(({className:u,...f},c)=>Ke(Se,{ref:c,icon:t,className:u,...f}));return t.name&&(l.displayName=Le(t.name)),l}var ke={name:"arrow-up",size:24,node:[["path",{d:"m5 12 7-7 7 7",key:"hav0vg"}],["path",{d:"M12 19V5",key:"x0mq9r"}]]};ke.node;var H=P(ke);var we={name:"paperclip",size:24,node:[["path",{d:"m16 6-8.414 8.586a2 2 0 0 0 2.829 2.829l8.414-8.586a4 4 0 1 0-5.657-5.657l-8.379 8.551a6 6 0 1 0 8.485 8.485l8.379-8.551",key:"1miecu"}]]};we.node;var E=P(we);var Pe={name:"pencil",size:24,node:[["path",{d:"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z",key:"1a8usu"}],["path",{d:"m15 5 4 4",key:"1mk7zo"}]]};Pe.node;var G=P(Pe);var Ae={name:"play",size:24,node:[["path",{d:"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z",key:"10ikf1"}]]};Ae.node;var W=P(Ae);var Be={name:"rotate-cw",size:24,node:[["path",{d:"M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8",key:"1p45f6"}],["path",{d:"M21 3v5h-5",key:"1q7to0"}]]};Be.node;var V=P(Be);var De={name:"x",size:24,node:[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]]};De.node;var T=P(De);import{useEffect as U,useRef as D,useState as N}from"react";function A(e){return{version:1,content:e.split(`
2
+ `).map(a=>({type:"paragraph",children:[{text:a}]}))}}function ye(e){return e.content.map(a=>a.children.map(o=>"type"in o?o.text||o.url:o.text).join("")).join(`
3
+ `)}function K(e){let a=e.content.slice();for(;a.length;){let o=a[a.length-1],t=o.children.slice();for(;t.length;){let l=t[t.length-1],u="type"in l?l.text||l.url:l.text,f=u.trimEnd();if(f)return t[t.length-1]=f===u?l:{...l,text:f},a[a.length-1]={...o,children:t},{...e,content:a};t.pop()}a.pop()}return A("")}function oe(e,a,o){let t=e;for(let l of[...a].sort((u,f)=>u-f))if(!(l<=t)){if(!o.has(l))break;t=l}return t}import{jsx as S,Fragment as be,jsxs as Te}from"react/jsx-runtime";var Z=["image/jpeg","image/png","image/gif","image/webp","image/avif","image/bmp"],Me=["video/mp4","video/webm","video/quicktime"],Fe=[...Z,...Me].join(",");function Re(e){let a=Z.includes(e.type);return!a&&!Me.includes(e.type)?"Choose a JPG, PNG, GIF, WebP, AVIF, BMP, MP4, WebM, or MOV file.":!Number.isSafeInteger(e.size)||e.size<=0?"This file is empty or invalid.":e.size>(a?5242880:20971520)?`${a?"Images must be 5 MB":"Videos must be 20 MB"} or smaller.`:null}import{useEffect as Ze,useRef as de,useState as Je}from"react";import{createEditor as Qe,Editor as h,Element as q,Node as je,Path as _e,Range as ue,Transforms as B}from"slate";import{withHistory as $e}from"slate-history";import{Editable as Ye,Slate as ea,withReact as aa,ReactEditor as ve}from"slate-react";function le(e){return e.content.length?e.content.map(a=>({type:"paragraph",children:a.children.length?a.children.map(o=>"type"in o&&o.type==="link"?{type:"link",url:o.url,children:[{text:o.text||o.url}]}:{...o}):[{text:""}]})):[{type:"paragraph",children:[{text:""}]}]}function qe(e){return{version:1,content:e.map(a=>({type:"paragraph",children:q.isElement(a)?a.children.map(o=>q.isElement(o)?{type:"link",url:o.url,text:je.string(o)}:{...o}):[{text:a.text}]}))}}function re(e){return/^(https?:\/\/|mailto:)[^\s]+$/i.test(e)}function ta(e,a){if(!e.selection){let o=window.getSelection(),t=o&&ve.toSlateRange(e,o,{exactMatch:!1,suppressThrow:!0});B.select(e,t??h.end(e,[]))}h.marks(e)?.[a]?h.removeMark(e,a):h.addMark(e,a,!0)}function Ue(){let e=aa($e(Qe()),"application/x-chisel-review-fragment"),{isInline:a,insertText:o,insertData:t,insertBreak:l}=e;e.isInline=c=>c.type==="link"||a(c);function u(c,i){h.above(e,{at:c,match:p=>q.isElement(p)&&p.type==="link"})||(B.wrapNodes(e,{type:"link",url:i,children:[]},{at:c,split:!0}),B.collapse(e,{edge:"end"}))}function f(){if(!e.selection||!ue.isCollapsed(e.selection))return;let c=h.above(e,{match:d=>q.isElement(d)&&d.type==="paragraph"});if(!c)return;let i=e.selection.anchor,r=h.string(e,{anchor:h.start(e,c[1]),focus:i}).match(/\S+$/)?.[0];if(!r||!re(r))return;let I=h.before(e,i,{distance:r.length,unit:"offset"});if(I){u({anchor:I,focus:i},r);let d=h.above(e,{match:s=>q.isElement(s)&&s.type==="link"});d&&e.selection&&h.isEnd(e,e.selection.anchor,d[1])&&B.select(e,h.start(e,_e.next(d[1])))}}return e.insertText=c=>{if(ve.isComposing(e)){o(c);return}c===" "&&f();let i={"*":"bold",_:"italic","~":"strikethrough","`":"code"}[c];if(i&&e.selection&&ue.isCollapsed(e.selection)){let p=h.above(e,{match:r=>q.isElement(r)&&r.type==="paragraph"});if(p){let r=e.selection.anchor,I=h.string(e,{anchor:h.start(e,p[1]),focus:r}),d=I.lastIndexOf(c),s=I.slice(d+1);if(d>=0&&(!d||/[\s*_~`]/.test(I[d-1]))&&s&&s.trim()===s){let m=h.before(e,r,{distance:s.length,unit:"offset"}),x=m&&h.before(e,m,{unit:"offset"});if(m&&x){B.select(e,{anchor:m,focus:r}),h.addMark(e,i,!0),B.collapse(e,{edge:"end"}),B.delete(e,{at:{anchor:x,focus:m}}),h.removeMark(e,i);return}}}}o(c)},e.insertBreak=()=>{f(),l()},e.insertData=c=>{let i=c.getData("text/plain");re(i)?e.selection&&!ue.isCollapsed(e.selection)?u(e.selection,i):B.insertNodes(e,[{type:"link",url:i,children:[{text:i}]},{text:""}]):t(c)},e}function oa({attributes:e,children:a,element:o}){return o.type==="link"?S("a",{...e,href:re(o.url)?o.url:void 0,onClick:t=>t.preventDefault(),children:a}):S("p",{...e,children:a})}function ua({attributes:e,children:a,leaf:o}){let t=S(be,{children:a});return o.bold&&(t=S("strong",{children:t})),o.italic&&(t=S("em",{children:t})),o.strikethrough&&(t=S("s",{children:t})),o.code&&(t=S("code",{children:t})),S("span",{...e,children:t})}function J({onFiles:e,disabled:a}){let o=de(null);return Te(be,{children:[S("input",{ref:o,type:"file",accept:Fe,multiple:!0,hidden:!0,onChange:t=>{e(t.target.files),t.target.value=""}}),S("button",{type:"button",className:"review-retry-upload","aria-label":"Attach image or video",title:"Images up to 5 MB \xB7 Videos up to 20 MB",disabled:a,onClick:()=>o.current?.click(),children:S(E,{size:16,strokeWidth:1.75})})]})}function z({text:e,defaultBody:a,disabled:o,label:t,placeholder:l,autoFocus:u,onChange:f,onSubmit:c,onFiles:i,uploading:p}){let[r]=Je(Ue),I=de(le(a??A(e))),d=de(e);return Ze(()=>{if(e===d.current)return;let s=d.current;d.current=e,s&&e.startsWith(s)?B.insertText(r,e.slice(s.length),{at:h.end(r,[])}):(h.withoutNormalizing(r,()=>{B.removeNodes(r,{at:[],match:(m,x)=>x.length===1}),B.insertNodes(r,le(A(e)),{at:[0]}),B.deselect(r)}),e||(r.history={undos:[],redos:[]}))},[r,e]),Te(ea,{editor:r,initialValue:I.current,onChange:s=>{if(!r.operations.some(x=>x.type!=="set_selection"))return;let m=qe(s);d.current=ye(m),f(d.current,m)},children:[S(Ye,{className:"review-composer-editor",role:"textbox","aria-multiline":"true","aria-label":t,placeholder:l,readOnly:o,autoFocus:u,renderElement:oa,renderLeaf:ua,onKeyDown:s=>{if(s.nativeEvent.isComposing)return;let m=s.metaKey||s.ctrlKey;if(s.key==="Enter"){s.preventDefault(),m?r.insertBreak():c();return}let x=m?{b:"bold",i:"italic",e:"code",...s.shiftKey?{s:"strikethrough"}:{}}[s.key.toLowerCase()]:void 0;x&&(s.preventDefault(),ta(r,x))}}),i&&S("div",{className:"chisel-attachment-controls",children:S(J,{onFiles:i,disabled:o||p})})]})}import{jsx as k,jsxs as Q,Fragment as Oe}from"react/jsx-runtime";import{useEffect as da,useRef as la,useState as ra}from"react";function sa({src:e,name:a,mimeType:o,onError:t,close:l}){let u=la(null);return da(()=>{u.current?.showModal()},[]),k("dialog",{ref:u,className:"review-media-lightbox","aria-label":`Preview ${a}`,onCancel:f=>{f.preventDefault(),l()},onClick:f=>{f.target===f.currentTarget&&l()},children:Q("div",{className:"review-media-lightbox-content",children:[Q("header",{children:[k("span",{children:a}),k("a",{href:e,download:a,"aria-label":`Download ${a}`,children:"Download"}),k("button",{type:"button","aria-label":"Close preview",onClick:l,children:k(T,{size:20})})]}),o.startsWith("image/")?k("img",{src:e,alt:a,onError:t}):k("video",{src:e,controls:!0,autoPlay:!0,playsInline:!0,"aria-label":a,onError:t})]})})}function j({src:e,name:a,mimeType:o,onError:t}){let[l,u]=ra(!1);return Q(Oe,{children:[k("button",{type:"button",className:"review-media-thumbnail","aria-label":`Preview ${a}`,onClick:()=>u(!0),children:o.startsWith("image/")?k("img",{src:e,alt:a,loading:"lazy",onError:t}):Q(Oe,{children:[k("video",{src:e,muted:!0,playsInline:!0,preload:"metadata","aria-hidden":"true",onError:t}),k("span",{className:"review-media-play","aria-hidden":"true",children:k(W,{size:20,fill:"currentColor"})})]})}),l&&k(sa,{src:e,name:a,mimeType:o,onError:t,close:()=>u(!1)})]})}function He({body:e}){return n("div",{className:"review-comment-body",children:e.content.map((a,o)=>n("p",{children:a.children.map((t,l)=>{if("type"in t&&t.type==="link")return n("a",{href:/^(https?:|mailto:)/i.test(t.url)?t.url:void 0,target:"_blank",rel:"noopener noreferrer",children:t.text||t.url},l);if(!("text"in t))return null;let u=n(fa,{children:t.text});return"bold"in t&&t.bold&&(u=n("strong",{children:u})),"italic"in t&&t.italic&&(u=n("em",{children:u})),"strikethrough"in t&&t.strikethrough&&(u=n("s",{children:u})),"code"in t&&t.code&&(u=n("code",{children:u})),n("span",{children:u},l)})},o))})}function ia({attachment:e,loadMedia:a}){let[o,t]=N(e.previewUrl??""),[l,u]=N(!1),[f,c]=N(0),i=D(0);U(()=>{if(e.previewUrl){t(e.previewUrl);return}t("");let r=!0,I=!1,d=new AbortController;async function s(){if(!(I||document.visibilityState==="hidden")){I=!0;try{let C=await a(e.id,d.signal);r&&(t(C),u(!1),i.current=Date.now()+240*1e3)}catch{r&&u(!0)}finally{I=!1}}}function m(){Date.now()>=i.current&&s()}s();let x=window.setInterval(m,60*1e3);return window.addEventListener("focus",m),document.addEventListener("visibilitychange",m),()=>{r=!1,d.abort(),window.clearInterval(x),window.removeEventListener("focus",m),document.removeEventListener("visibilitychange",m)}},[e.id,e.previewUrl,f,a]);function p(){u(!0),Date.now()>=i.current&&c(r=>r+1)}return M("figure",{className:"review-attachment",children:[o&&!l?n(j,{src:o,name:e.name,mimeType:e.mimeType,onError:p}):n("div",{className:"review-attachment-placeholder",role:"status",children:l?n("button",{type:"button",className:"review-retry-upload",onClick:()=>c(r=>r+1),children:"Retry attachment"}):"Loading attachment\u2026"}),n("figcaption",{children:e.name})]})}function ca(e){let a=Math.max(0,Math.floor((Date.now()-Date.parse(e))/6e4));return a<1?"Just now":a<60?`${a}m`:a<1440?`${Math.floor(a/60)}h`:new Date(e).toLocaleDateString(void 0,{month:"short",day:"numeric"})}function na({comment:e,activity:a,loadMedia:o,editable:t,editing:l,saving:u,onStartEdit:f,onCancelEdit:c,onSaveEdit:i,onDraftChange:p,onEditStyles:r}){let[I,d]=N(e.text),s=D(e.body??A(e.text)),m=D(!1);U(()=>{l||(d(e.text),s.current=e.body??A(e.text))},[l,e.text,e.body]);async function x(){if(!(u||m.current||!I.trim()&&!e.attachments?.length)){m.current=!0;try{await i(I.trimEnd(),K(s.current))&&p(!1)}finally{m.current=!1}}}return M("article",{className:"review-comment","data-activity":a,children:[n("span",{className:"review-comment-avatar",children:e.authorImage?n("img",{src:e.authorImage,alt:""}):e.author.slice(0,1).toUpperCase()}),M("div",{className:"review-comment-content",children:[M("header",{className:"review-comment-header",children:[n("span",{className:"review-comment-author",children:e.author}),e.pending?n("span",{className:"review-comment-time",role:"status",children:"Sending\u2026"}):n("time",{className:"review-comment-time",dateTime:e.date,title:new Date(e.date).toLocaleString(),children:ca(e.date)}),t&&!l&&n("button",{type:"button",className:"review-comment-edit","aria-label":"Edit comment",title:"Edit comment",disabled:u,onClick:f,children:n(G,{size:16})})]}),l?M("form",{className:"review-inline-edit",onSubmit:C=>{C.preventDefault(),x()},children:[n(z,{text:I,defaultBody:s.current,disabled:u,label:"Edit comment",placeholder:"Edit comment\u2026",autoFocus:!0,onSubmit:()=>{x()},onChange:(C,F)=>{d(C),s.current=F,p(C!==e.text||JSON.stringify(F)!==JSON.stringify(e.body??A(e.text)))}}),M("div",{className:"review-inline-edit-actions",children:[r&&n("button",{type:"button",disabled:u,onClick:r,children:"Edit styles"}),n("button",{type:"button",disabled:u,onClick:c,children:"Cancel"}),n("button",{type:"submit",disabled:u||!I.trim()&&!e.attachments?.length,children:"Save"})]})]}):n(He,{body:e.body??A(e.text)}),e.attachments?.length>0&&n("div",{className:"review-attachments",children:e.attachments.map(C=>n(ia,{attachment:C,loadMedia:o},C.id))})]})]})}function pa(e){let a=e.useAttachments(e.disabled,e.existingAttachments?.length??0),o=D(e.defaultBody??A(e.text)),t=D(!1);U(()=>(e.onAttachmentDraftChange?.(a.uploading||a.attachments.length>0),()=>e.onAttachmentDraftChange?.(!1)),[a.uploading,a.attachments.length,e.onAttachmentDraftChange]);async function l(){if(!(e.disabled||a.blocked||t.current||!e.text.trim()&&!a.attachments.length&&!e.existingAttachments?.length)){t.current=!0;try{await e.onSubmit(e.text.trimEnd(),K(o.current),a.tokens,a.previews)&&a.clear()}finally{t.current=!1}}}return n("form",{className:"review-composer chisel-review-composer","aria-label":e.commentId?"Edit comment":"New comment",onSubmit:u=>{u.preventDefault(),l()},onPasteCapture:u=>{u.clipboardData.files.length&&(u.preventDefault(),u.stopPropagation(),a.upload(u.clipboardData.files))},onDragOver:u=>{u.dataTransfer.types.includes("Files")&&u.preventDefault()},onDropCapture:u=>{u.dataTransfer.files.length&&(u.preventDefault(),u.stopPropagation(),a.upload(u.dataTransfer.files))},children:e.renderContent(M("div",{className:"chisel-composer-input",children:[n(z,{text:e.text,defaultBody:e.defaultBody,disabled:e.disabled,label:"Comment",placeholder:"Leave a comment\u2026",autoFocus:!0,onSubmit:()=>{l()},onChange:(u,f)=>{o.current=f,e.onText(u)}}),a.previewsUI,a.controls]}),a.attachments.length>0||!!e.existingAttachments?.length,a.blocked,n(J,{onFiles:a.upload,disabled:e.disabled||a.uploading}))})}function ma(e){let a=e.useAttachments(e.saving),o=D(A(e.reply)),t=D(!1);U(()=>(e.onAttachmentDraftChange?.(a.uploading||a.attachments.length>0),()=>e.onAttachmentDraftChange?.(!1)),[a.uploading,a.attachments.length,e.onAttachmentDraftChange]);async function l(){if(!(e.saving||a.blocked||t.current||!e.reply.trim()&&!a.attachments.length)){t.current=!0;try{await e.onReply(e.reply.trimEnd(),K(o.current),a.tokens,a.previews)&&(o.current=A(""),a.clear())}finally{t.current=!1}}}return M("form",{className:"review-composer chisel-review-reply","aria-label":"Reply to thread",hidden:e.submittingReply,onSubmit:u=>{u.preventDefault(),l()},onPasteCapture:u=>{u.clipboardData.files.length&&(u.preventDefault(),u.stopPropagation(),a.upload(u.clipboardData.files))},onDragOver:u=>{u.dataTransfer.types.includes("Files")&&u.preventDefault()},onDropCapture:u=>{u.dataTransfer.files.length&&(u.preventDefault(),u.stopPropagation(),a.upload(u.dataTransfer.files))},children:[n("span",{className:"feedback-avatar feedback-reply-avatar",children:e.replyAuthor?.image?n("img",{src:e.replyAuthor.image,alt:""}):(e.replyAuthor?.name||"You").slice(0,1).toUpperCase()}),M("div",{className:"chisel-reply-field",children:[n(z,{text:e.reply,disabled:e.saving,label:"Reply",placeholder:"Write a reply\u2026",onFiles:a.upload,uploading:a.uploading,onSubmit:()=>{l()},onChange:(u,f)=>{o.current=f,e.onReplyText(u)}}),a.previewsUI,a.controls,n("button",{type:"submit",className:"chisel-reply-submit","aria-label":"Reply",disabled:e.saving||a.blocked||!e.reply.trim()&&!a.attachments.length,children:n(H,{size:16,strokeWidth:1.75})})]})]})}function La(e){let a=e.comment,[o,t]=N(null),l=D(!1),u=D(e.onEditDraftChange);u.current=e.onEditDraftChange,U(()=>()=>{u.current?.(!1)},[]);function f(d){l.current=d,e.onEditDraftChange?.(d)}async function c(d){d&&e.onBeginEdit&&!await e.onBeginEdit()||!d&&l.current&&e.onDiscardEdit&&!await e.onDiscardEdit()||(f(!1),t(d))}function i(d,s){return n(na,{comment:d,activity:s?a.rootActivity:d.activity,loadMedia:e.loadMedia,editable:!!(e.expanded&&e.onEdit&&e.userId&&d.authorId===e.userId&&!d.pending),editing:o===d.id,saving:e.saving,onEditStyles:s?e.onEditStyles:void 0,onStartEdit:()=>{c(d.id)},onCancelEdit:()=>{c(null)},onDraftChange:f,onSaveEdit:async(m,x)=>{let C=await e.onEdit?.(s?null:d.id,m,x);return C&&(f(!1),t(null)),!!C}},d.id)}let p=D(null),r=D(new Set),I=D(a.readActivity??0);return U(()=>{if(!e.expanded||!p.current||a.pending||a.readActivity===null)return;let d=[a.rootActivity,...a.replies.map(x=>x.activity)],s=new IntersectionObserver(x=>{if(document.visibilityState!=="visible")return;for(let F of x)F.isIntersecting&&F.intersectionRatio>0&&r.current.add(Number(F.target.dataset.activity));let C=oe(Math.max(I.current,a.readActivity??0),d,r.current);C>I.current&&(I.current=C,e.onRead(a.id,C))},{threshold:0});function m(){if(s.disconnect(),document.visibilityState==="visible")for(let x of p.current?.querySelectorAll("[data-activity]")??[])s.observe(x)}return m(),document.addEventListener("visibilitychange",m),()=>{s.disconnect(),document.removeEventListener("visibilitychange",m)}},[a,e.expanded,e.onRead]),M("div",{className:"chisel-review-thread-content",children:[M("div",{ref:p,className:"review-thread",children:[i(a,!0),e.expanded&&a.replies.map(d=>i(d,!1))]}),e.expanded?!o&&n(ma,{...e}):n("button",{type:"button",className:"chisel-expand-thread",onClick:e.onExpand,children:a.replies.length?`${a.replies.length} ${a.replies.length===1?"reply":"replies"}`:"Reply\u2026"})]})}import{useCallback as Ee,useRef as Ge}from"react";function Ia(e,a){let o=Ge(new Map),t=Ge(!1),l=Ee(async()=>{if(!t.current){t.current=!0;try{for(;o.current.size;){let[f,c]=o.current.entries().next().value;try{await e(f,c)}catch(i){if([400,403,404].includes(i.status??0)){o.current.delete(f);continue}throw i}o.current.get(f)===c&&o.current.delete(f),a(f,c)}}catch{}finally{t.current=!1}}},[e,a]),u=Ee((f,c)=>{o.current.set(f,Math.max(c,o.current.get(f)??0)),l()},[l]);return{flush:l,markDisplayed:u}}import{jsx as b,jsxs as se}from"react/jsx-runtime";import{useEffect as xa,useRef as fe,useState as ie}from"react";function Ca(e=!1,a=0,o){let[t,l]=ie([]),[u,f]=ie(!1),[c,i]=ie(""),p=fe(!0),r=fe(!1),I=fe(new Map),d=L=>o.discard(L);xa(()=>(p.current=!0,()=>{p.current=!1;for(let L of I.current.values())URL.revokeObjectURL(L.preview),d(L).catch(()=>{})}),[]);function s(){p.current&&l([...I.current.values()].map(L=>({...L})))}function m(){for(let L of I.current.values())URL.revokeObjectURL(L.preview);I.current.clear(),p.current&&(l([]),i(""))}async function x(L){let g=I.current.get(L);if(!(!g||r.current||e)){r.current=!0,f(!0);try{await d(g),I.current.delete(L),URL.revokeObjectURL(g.preview),s(),i("")}catch(y){p.current&&i(y instanceof Error?y.message:"The attachment could not be removed.")}finally{r.current=!1,p.current&&f(!1)}}}async function C(L){for(let g of L){if(!p.current)return;let y=g.status==="failed";g.status="uploading",s();try{g.token=await o.send(g,y,()=>p.current),g.status="ready"}catch(w){g.status="failed",p.current&&i(w instanceof Error?w.message:"The upload failed. Your comment is still here.")}finally{p.current||await d(g).catch(()=>{})}s()}}async function F(L){if(!L?.length||r.current||e)return;let g=Array.from(L),y=g.map(w=>Re(w)).find(Boolean);if(y){i(y);return}if(a+I.current.size+g.length>20){i("Attach up to 20 files per comment.");return}r.current=!0,f(!0),i("");try{let w=await o.userId();if(!w)throw new Error("Sign in again to attach files.");if(!p.current)return;let ee=g.map(v=>{let ae=crypto.randomUUID(),ce={id:ae,name:v.name,mimeType:v.type,size:v.size,file:v,bucket:Z.includes(v.type)?"chisel-review-images":"chisel-review-videos",path:`${w}/${ae}`,preview:URL.createObjectURL(v),stored:!1,status:"uploading"};return I.current.set(ae,ce),ce});s(),await C(ee)}catch(w){p.current&&i(w instanceof Error?w.message:"The upload failed. Your comment is still here.")}finally{r.current=!1,p.current&&f(!1)}}async function _(){if(!(r.current||e)){r.current=!0,f(!0),i("");try{await C([...I.current.values()].filter(L=>L.status==="failed"))}finally{r.current=!1,p.current&&f(!1)}}}let X=t.some(L=>L.status==="failed"),$=se("div",{className:"chisel-attachment-controls",children:[u&&b("span",{role:"status",children:"Uploading\u2026"}),X&&se("button",{type:"button",className:"review-retry-upload",disabled:u||e,onClick:()=>{_()},children:[b(V,{size:16,strokeWidth:1.75}),"Retry upload"]}),c&&b("span",{className:"chisel-attachment-error",role:"alert",children:c})]}),Y=t.length>0&&b("div",{className:"chisel-attachment-previews",children:t.map(L=>se("div",{className:"chisel-attachment-preview","data-state":L.status,children:[b(j,{src:L.preview,name:L.name,mimeType:L.mimeType}),b("span",{title:L.name,children:L.name}),b("button",{type:"button","aria-label":`Remove ${L.name}`,disabled:u||e,onClick:()=>{x(L.id)},children:b(T,{size:16,strokeWidth:1.75})})]},L.id))});return{attachments:t,uploading:u,blocked:u||X,controls:$,previewsUI:Y,clear:m,upload:F,previews:t.map(({id:L,name:g,mimeType:y,size:w,preview:ee})=>({id:L,name:g,mimeType:y,size:w,previewUrl:ee})),tokens:t.flatMap(L=>L.token?[L.token]:[])}}export{J as ReviewAttachmentButton,He as ReviewBody,pa as ReviewComposer,z as ReviewEditor,La as ReviewThread,le as bodyToEditor,Ue as createReviewEditor,oe as displayedActivity,qe as editorToBody,Ia as useReadQueue,Ca as useReviewAttachments};
4
+ /*! Bundled license information:
5
+
6
+ lucide-react/dist/esm/shared/src/utils/toKebabCase.mjs:
7
+ lucide-react/dist/esm/shared/src/utils/toLucideIconData.mjs:
8
+ lucide-react/dist/esm/shared/src/utils/toCamelCase.mjs:
9
+ lucide-react/dist/esm/shared/src/utils/toPascalCase.mjs:
10
+ lucide-react/dist/esm/shared/src/utils/mergeClasses.mjs:
11
+ lucide-react/dist/esm/shared/src/build/defaultAttributes.mjs:
12
+ lucide-react/dist/esm/shared/src/build/buildLucideIconNode.mjs:
13
+ lucide-react/dist/esm/shared/src/build/buildLucideIconForReact.mjs:
14
+ lucide-react/dist/esm/shared/src/utils/hasA11yProp.mjs:
15
+ lucide-react/dist/esm/context.mjs:
16
+ lucide-react/dist/esm/Icon.mjs:
17
+ lucide-react/dist/esm/createLucideIcon.mjs:
18
+ lucide-react/dist/esm/icons/arrow-up.mjs:
19
+ lucide-react/dist/esm/icons/paperclip.mjs:
20
+ lucide-react/dist/esm/icons/pencil.mjs:
21
+ lucide-react/dist/esm/icons/play.mjs:
22
+ lucide-react/dist/esm/icons/rotate-cw.mjs:
23
+ lucide-react/dist/esm/icons/x.mjs:
24
+ lucide-react/dist/esm/lucide-react.mjs:
25
+ (**
26
+ * @license lucide-react v1.61.0 - ISC
27
+ *
28
+ * This source code is licensed under the ISC license.
29
+ * See the LICENSE file in the root directory of this source tree.
30
+ *)
31
+ */
@@ -52,6 +52,12 @@ export type CommentMutation = {
52
52
  changes: StyleChange[];
53
53
  body?: unknown;
54
54
  attachmentTokens?: string[];
55
+ } | {
56
+ type: "edit_reply";
57
+ id: string;
58
+ replyId: string;
59
+ text: string;
60
+ body?: unknown;
55
61
  } | {
56
62
  type: "reply";
57
63
  id: string;