@washi-ui/react 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +312 -0
- package/dist/index.d.mts +335 -0
- package/dist/index.d.ts +335 -0
- package/dist/index.js +1014 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +981 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +62 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1014 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
CommentList: () => CommentList,
|
|
34
|
+
WashiCommentsSidebar: () => WashiCommentsSidebar,
|
|
35
|
+
WashiFrame: () => WashiFrame,
|
|
36
|
+
WashiPinDialog: () => WashiPinDialog,
|
|
37
|
+
WashiProvider: () => WashiProvider,
|
|
38
|
+
WashiToolBubble: () => WashiToolBubble,
|
|
39
|
+
WashiUI: () => WashiUI,
|
|
40
|
+
useWashi: () => useWashi,
|
|
41
|
+
useWashiContext: () => useWashiContext
|
|
42
|
+
});
|
|
43
|
+
module.exports = __toCommonJS(index_exports);
|
|
44
|
+
|
|
45
|
+
// src/useWashi.ts
|
|
46
|
+
var import_react = require("react");
|
|
47
|
+
var import_core = require("@washi-ui/core");
|
|
48
|
+
function useWashi(options) {
|
|
49
|
+
const {
|
|
50
|
+
adapter,
|
|
51
|
+
initialMode = "view",
|
|
52
|
+
mountOptions,
|
|
53
|
+
onPinPlaced,
|
|
54
|
+
onCommentClick,
|
|
55
|
+
onCommentUpdate,
|
|
56
|
+
onCommentDelete
|
|
57
|
+
} = options;
|
|
58
|
+
const iframeRef = (0, import_react.useRef)(null);
|
|
59
|
+
const washiRef = (0, import_react.useRef)(null);
|
|
60
|
+
const [mode, setModeState] = (0, import_react.useState)(initialMode);
|
|
61
|
+
const [comments, setComments] = (0, import_react.useState)([]);
|
|
62
|
+
const [isReady, setIsReady] = (0, import_react.useState)(false);
|
|
63
|
+
const [error, setError] = (0, import_react.useState)(null);
|
|
64
|
+
(0, import_react.useEffect)(() => {
|
|
65
|
+
washiRef.current = new import_core.Washi(adapter);
|
|
66
|
+
return () => {
|
|
67
|
+
washiRef.current?.unmount();
|
|
68
|
+
washiRef.current = null;
|
|
69
|
+
};
|
|
70
|
+
}, [adapter]);
|
|
71
|
+
(0, import_react.useEffect)(() => {
|
|
72
|
+
const iframe = iframeRef.current;
|
|
73
|
+
const washi = washiRef.current;
|
|
74
|
+
if (!iframe || !washi) return;
|
|
75
|
+
const handleLoad = async () => {
|
|
76
|
+
try {
|
|
77
|
+
await washi.mount(iframe, mountOptions);
|
|
78
|
+
setComments(washi.getComments());
|
|
79
|
+
if (initialMode !== "view") {
|
|
80
|
+
washi.setMode(initialMode);
|
|
81
|
+
}
|
|
82
|
+
setIsReady(true);
|
|
83
|
+
setError(null);
|
|
84
|
+
} catch (err) {
|
|
85
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
86
|
+
setIsReady(false);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
if (iframe.contentDocument?.readyState === "complete") {
|
|
90
|
+
handleLoad();
|
|
91
|
+
} else {
|
|
92
|
+
iframe.addEventListener("load", handleLoad);
|
|
93
|
+
}
|
|
94
|
+
return () => {
|
|
95
|
+
iframe.removeEventListener("load", handleLoad);
|
|
96
|
+
};
|
|
97
|
+
}, [mountOptions, initialMode]);
|
|
98
|
+
(0, import_react.useEffect)(() => {
|
|
99
|
+
const washi = washiRef.current;
|
|
100
|
+
if (!washi || !isReady) return;
|
|
101
|
+
const unsubscribers = [];
|
|
102
|
+
if (onPinPlaced) {
|
|
103
|
+
unsubscribers.push(
|
|
104
|
+
washi.on("pin:placed", (event) => {
|
|
105
|
+
onPinPlaced(event);
|
|
106
|
+
})
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
if (onCommentClick) {
|
|
110
|
+
unsubscribers.push(
|
|
111
|
+
washi.on("comment:clicked", (comment) => {
|
|
112
|
+
onCommentClick(comment);
|
|
113
|
+
})
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
if (onCommentUpdate) {
|
|
117
|
+
unsubscribers.push(
|
|
118
|
+
washi.on(
|
|
119
|
+
"comment:updated",
|
|
120
|
+
(data) => {
|
|
121
|
+
onCommentUpdate(data);
|
|
122
|
+
}
|
|
123
|
+
)
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
if (onCommentDelete) {
|
|
127
|
+
unsubscribers.push(
|
|
128
|
+
washi.on("comment:deleted", (id) => {
|
|
129
|
+
onCommentDelete(id);
|
|
130
|
+
})
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
return () => {
|
|
134
|
+
unsubscribers.forEach((unsub) => unsub());
|
|
135
|
+
};
|
|
136
|
+
}, [isReady, onPinPlaced, onCommentClick, onCommentUpdate, onCommentDelete]);
|
|
137
|
+
const setMode = (0, import_react.useCallback)((newMode) => {
|
|
138
|
+
const washi = washiRef.current;
|
|
139
|
+
if (!washi) return;
|
|
140
|
+
try {
|
|
141
|
+
washi.setMode(newMode);
|
|
142
|
+
setModeState(newMode);
|
|
143
|
+
} catch (err) {
|
|
144
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
145
|
+
}
|
|
146
|
+
}, []);
|
|
147
|
+
const addComment = (0, import_react.useCallback)(async (input) => {
|
|
148
|
+
const washi = washiRef.current;
|
|
149
|
+
if (!washi) throw new Error("Washi is not initialized");
|
|
150
|
+
const comment = await washi.addComment(input);
|
|
151
|
+
setComments(washi.getComments());
|
|
152
|
+
return comment;
|
|
153
|
+
}, []);
|
|
154
|
+
const updateComment = (0, import_react.useCallback)(
|
|
155
|
+
async (id, updates) => {
|
|
156
|
+
const washi = washiRef.current;
|
|
157
|
+
if (!washi) throw new Error("Washi is not initialized");
|
|
158
|
+
await washi.updateComment(id, updates);
|
|
159
|
+
setComments(washi.getComments());
|
|
160
|
+
},
|
|
161
|
+
[]
|
|
162
|
+
);
|
|
163
|
+
const deleteComment = (0, import_react.useCallback)(async (id) => {
|
|
164
|
+
const washi = washiRef.current;
|
|
165
|
+
if (!washi) throw new Error("Washi is not initialized");
|
|
166
|
+
await washi.deleteComment(id);
|
|
167
|
+
setComments(washi.getComments());
|
|
168
|
+
}, []);
|
|
169
|
+
return {
|
|
170
|
+
iframeRef,
|
|
171
|
+
mode,
|
|
172
|
+
setMode,
|
|
173
|
+
comments,
|
|
174
|
+
addComment,
|
|
175
|
+
updateComment,
|
|
176
|
+
deleteComment,
|
|
177
|
+
isReady,
|
|
178
|
+
error
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// src/WashiProvider.tsx
|
|
183
|
+
var import_react2 = require("react");
|
|
184
|
+
var import_core2 = require("@washi-ui/core");
|
|
185
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
186
|
+
var WashiContext = (0, import_react2.createContext)(null);
|
|
187
|
+
function WashiProvider({
|
|
188
|
+
adapter,
|
|
189
|
+
initialMode = "view",
|
|
190
|
+
mountOptions,
|
|
191
|
+
children
|
|
192
|
+
}) {
|
|
193
|
+
const washiRef = (0, import_react2.useRef)(null);
|
|
194
|
+
if (!washiRef.current) {
|
|
195
|
+
washiRef.current = new import_core2.Washi(adapter);
|
|
196
|
+
}
|
|
197
|
+
const iframeRef = (0, import_react2.useRef)(null);
|
|
198
|
+
const iframeLoadCleanupRef = (0, import_react2.useRef)(null);
|
|
199
|
+
const washiEventCleanupsRef = (0, import_react2.useRef)([]);
|
|
200
|
+
const pinPlacedCallbacksRef = (0, import_react2.useRef)(
|
|
201
|
+
/* @__PURE__ */ new Set()
|
|
202
|
+
);
|
|
203
|
+
const clickCallbacksRef = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
|
|
204
|
+
const [mode, setModeState] = (0, import_react2.useState)(initialMode);
|
|
205
|
+
const [comments, setComments] = (0, import_react2.useState)([]);
|
|
206
|
+
const [isReady, setIsReady] = (0, import_react2.useState)(false);
|
|
207
|
+
const [error, setError] = (0, import_react2.useState)(null);
|
|
208
|
+
const [activeComment, setActiveCommentState] = (0, import_react2.useState)(null);
|
|
209
|
+
const [iframeElState, setIframeElState] = (0, import_react2.useState)(null);
|
|
210
|
+
(0, import_react2.useEffect)(() => {
|
|
211
|
+
return () => {
|
|
212
|
+
washiRef.current?.unmount();
|
|
213
|
+
};
|
|
214
|
+
}, []);
|
|
215
|
+
const refreshComments = (0, import_react2.useCallback)(() => {
|
|
216
|
+
const washi = washiRef.current;
|
|
217
|
+
if (washi) {
|
|
218
|
+
setComments(washi.getComments());
|
|
219
|
+
}
|
|
220
|
+
}, []);
|
|
221
|
+
const registerIframe = (0, import_react2.useCallback)(
|
|
222
|
+
(iframe) => {
|
|
223
|
+
iframeLoadCleanupRef.current?.();
|
|
224
|
+
iframeLoadCleanupRef.current = null;
|
|
225
|
+
washiEventCleanupsRef.current.forEach((fn) => fn());
|
|
226
|
+
washiEventCleanupsRef.current = [];
|
|
227
|
+
iframeRef.current = iframe;
|
|
228
|
+
setIframeElState(iframe);
|
|
229
|
+
if (!iframe) {
|
|
230
|
+
if (washiRef.current) {
|
|
231
|
+
washiRef.current.unmount();
|
|
232
|
+
setIsReady(false);
|
|
233
|
+
setComments([]);
|
|
234
|
+
}
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (!washiRef.current) return;
|
|
238
|
+
const washi = washiRef.current;
|
|
239
|
+
const handleLoad = async () => {
|
|
240
|
+
try {
|
|
241
|
+
await washi.mount(iframe, mountOptions);
|
|
242
|
+
if (iframeRef.current !== iframe) return;
|
|
243
|
+
setComments(washi.getComments());
|
|
244
|
+
if (initialMode !== "view") {
|
|
245
|
+
washi.setMode(initialMode);
|
|
246
|
+
}
|
|
247
|
+
setIsReady(true);
|
|
248
|
+
setError(null);
|
|
249
|
+
washiEventCleanupsRef.current.push(
|
|
250
|
+
washi.on("pin:placed", (event) => {
|
|
251
|
+
pinPlacedCallbacksRef.current.forEach((cb) => cb(event));
|
|
252
|
+
})
|
|
253
|
+
);
|
|
254
|
+
washiEventCleanupsRef.current.push(
|
|
255
|
+
washi.on("comment:clicked", (comment) => {
|
|
256
|
+
clickCallbacksRef.current.forEach((cb) => cb(comment));
|
|
257
|
+
})
|
|
258
|
+
);
|
|
259
|
+
washiEventCleanupsRef.current.push(
|
|
260
|
+
washi.on("comment:updated", () => {
|
|
261
|
+
setComments(washi.getComments());
|
|
262
|
+
})
|
|
263
|
+
);
|
|
264
|
+
washiEventCleanupsRef.current.push(
|
|
265
|
+
washi.on("comment:deleted", () => {
|
|
266
|
+
setComments(washi.getComments());
|
|
267
|
+
})
|
|
268
|
+
);
|
|
269
|
+
} catch (err) {
|
|
270
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
271
|
+
setIsReady(false);
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
if (iframe.contentDocument?.readyState === "complete") {
|
|
275
|
+
handleLoad();
|
|
276
|
+
} else {
|
|
277
|
+
iframe.addEventListener("load", handleLoad);
|
|
278
|
+
iframeLoadCleanupRef.current = () => iframe.removeEventListener("load", handleLoad);
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
[mountOptions, initialMode]
|
|
282
|
+
);
|
|
283
|
+
const setMode = (0, import_react2.useCallback)((newMode) => {
|
|
284
|
+
const washi = washiRef.current;
|
|
285
|
+
if (!washi) return;
|
|
286
|
+
try {
|
|
287
|
+
washi.setMode(newMode);
|
|
288
|
+
setModeState(newMode);
|
|
289
|
+
} catch (err) {
|
|
290
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
291
|
+
}
|
|
292
|
+
}, []);
|
|
293
|
+
const addComment = (0, import_react2.useCallback)(async (input) => {
|
|
294
|
+
const washi = washiRef.current;
|
|
295
|
+
if (!washi) throw new Error("Washi is not initialized");
|
|
296
|
+
const comment = await washi.addComment(input);
|
|
297
|
+
setComments(washi.getComments());
|
|
298
|
+
return comment;
|
|
299
|
+
}, []);
|
|
300
|
+
const updateComment = (0, import_react2.useCallback)(
|
|
301
|
+
async (id, updates) => {
|
|
302
|
+
const washi = washiRef.current;
|
|
303
|
+
if (!washi) throw new Error("Washi is not initialized");
|
|
304
|
+
await washi.updateComment(id, updates);
|
|
305
|
+
setComments(washi.getComments());
|
|
306
|
+
},
|
|
307
|
+
[]
|
|
308
|
+
);
|
|
309
|
+
const deleteComment = (0, import_react2.useCallback)(async (id) => {
|
|
310
|
+
const washi = washiRef.current;
|
|
311
|
+
if (!washi) throw new Error("Washi is not initialized");
|
|
312
|
+
await washi.deleteComment(id);
|
|
313
|
+
setComments(washi.getComments());
|
|
314
|
+
}, []);
|
|
315
|
+
const onPinPlaced = (0, import_react2.useCallback)(
|
|
316
|
+
(callback) => {
|
|
317
|
+
pinPlacedCallbacksRef.current.add(callback);
|
|
318
|
+
return () => {
|
|
319
|
+
pinPlacedCallbacksRef.current.delete(callback);
|
|
320
|
+
};
|
|
321
|
+
},
|
|
322
|
+
[]
|
|
323
|
+
);
|
|
324
|
+
const onCommentClick = (0, import_react2.useCallback)(
|
|
325
|
+
(callback) => {
|
|
326
|
+
clickCallbacksRef.current.add(callback);
|
|
327
|
+
return () => {
|
|
328
|
+
clickCallbacksRef.current.delete(callback);
|
|
329
|
+
};
|
|
330
|
+
},
|
|
331
|
+
[]
|
|
332
|
+
);
|
|
333
|
+
const setActivePin = (0, import_react2.useCallback)((commentId) => {
|
|
334
|
+
const washi = washiRef.current;
|
|
335
|
+
if (washi) {
|
|
336
|
+
washi.setActivePin(commentId);
|
|
337
|
+
}
|
|
338
|
+
}, []);
|
|
339
|
+
const getCommentIndex = (0, import_react2.useCallback)((commentId) => {
|
|
340
|
+
const washi = washiRef.current;
|
|
341
|
+
if (washi) {
|
|
342
|
+
return washi.getCommentIndex(commentId);
|
|
343
|
+
}
|
|
344
|
+
return -1;
|
|
345
|
+
}, []);
|
|
346
|
+
const setActiveComment = (0, import_react2.useCallback)((comment) => {
|
|
347
|
+
setActiveCommentState(comment);
|
|
348
|
+
const washi = washiRef.current;
|
|
349
|
+
if (washi) {
|
|
350
|
+
washi.setActivePin(comment?.id ?? null);
|
|
351
|
+
}
|
|
352
|
+
}, []);
|
|
353
|
+
const value = (0, import_react2.useMemo)(
|
|
354
|
+
() => ({
|
|
355
|
+
washi: washiRef.current,
|
|
356
|
+
iframeEl: iframeElState,
|
|
357
|
+
mode,
|
|
358
|
+
setMode,
|
|
359
|
+
comments,
|
|
360
|
+
addComment,
|
|
361
|
+
updateComment,
|
|
362
|
+
deleteComment,
|
|
363
|
+
refreshComments,
|
|
364
|
+
isReady,
|
|
365
|
+
error,
|
|
366
|
+
registerIframe,
|
|
367
|
+
onPinPlaced,
|
|
368
|
+
onCommentClick,
|
|
369
|
+
setActivePin,
|
|
370
|
+
getCommentIndex,
|
|
371
|
+
activeComment,
|
|
372
|
+
setActiveComment
|
|
373
|
+
}),
|
|
374
|
+
[
|
|
375
|
+
iframeElState,
|
|
376
|
+
mode,
|
|
377
|
+
setMode,
|
|
378
|
+
comments,
|
|
379
|
+
addComment,
|
|
380
|
+
updateComment,
|
|
381
|
+
deleteComment,
|
|
382
|
+
refreshComments,
|
|
383
|
+
isReady,
|
|
384
|
+
error,
|
|
385
|
+
registerIframe,
|
|
386
|
+
onPinPlaced,
|
|
387
|
+
onCommentClick,
|
|
388
|
+
setActivePin,
|
|
389
|
+
getCommentIndex,
|
|
390
|
+
activeComment,
|
|
391
|
+
setActiveComment
|
|
392
|
+
]
|
|
393
|
+
);
|
|
394
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WashiContext.Provider, { value, children });
|
|
395
|
+
}
|
|
396
|
+
function useWashiContext() {
|
|
397
|
+
const context = (0, import_react2.useContext)(WashiContext);
|
|
398
|
+
if (!context) {
|
|
399
|
+
throw new Error("useWashiContext must be used within a WashiProvider");
|
|
400
|
+
}
|
|
401
|
+
return context;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// src/WashiFrame.tsx
|
|
405
|
+
var import_react3 = require("react");
|
|
406
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
407
|
+
function WashiFrame({ src, className, style, ...props }) {
|
|
408
|
+
const { registerIframe } = useWashiContext();
|
|
409
|
+
const iframeRef = (0, import_react3.useRef)(null);
|
|
410
|
+
(0, import_react3.useEffect)(() => {
|
|
411
|
+
if (iframeRef.current) {
|
|
412
|
+
registerIframe(iframeRef.current);
|
|
413
|
+
}
|
|
414
|
+
return () => {
|
|
415
|
+
registerIframe(null);
|
|
416
|
+
};
|
|
417
|
+
}, [registerIframe]);
|
|
418
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
419
|
+
"iframe",
|
|
420
|
+
{
|
|
421
|
+
ref: iframeRef,
|
|
422
|
+
src,
|
|
423
|
+
className,
|
|
424
|
+
style,
|
|
425
|
+
...props
|
|
426
|
+
}
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// src/CommentList.tsx
|
|
431
|
+
var import_react4 = __toESM(require("react"));
|
|
432
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
433
|
+
function CommentList({
|
|
434
|
+
renderComment,
|
|
435
|
+
filter,
|
|
436
|
+
sort,
|
|
437
|
+
emptyState,
|
|
438
|
+
className,
|
|
439
|
+
style
|
|
440
|
+
}) {
|
|
441
|
+
const { comments, updateComment, deleteComment } = useWashiContext();
|
|
442
|
+
let displayComments = [...comments];
|
|
443
|
+
if (filter) {
|
|
444
|
+
displayComments = displayComments.filter(filter);
|
|
445
|
+
}
|
|
446
|
+
if (sort) {
|
|
447
|
+
displayComments.sort(sort);
|
|
448
|
+
}
|
|
449
|
+
if (displayComments.length === 0 && emptyState) {
|
|
450
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: emptyState });
|
|
451
|
+
}
|
|
452
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className, style, children: displayComments.map((comment) => {
|
|
453
|
+
const actions = {
|
|
454
|
+
onResolve: async () => {
|
|
455
|
+
await updateComment(comment.id, { resolved: !comment.resolved });
|
|
456
|
+
},
|
|
457
|
+
onDelete: async () => {
|
|
458
|
+
await deleteComment(comment.id);
|
|
459
|
+
},
|
|
460
|
+
onUpdate: async (updates) => {
|
|
461
|
+
await updateComment(comment.id, updates);
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.default.Fragment, { children: renderComment(comment, actions) }, comment.id);
|
|
465
|
+
}) });
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// src/WashiToolBubble.tsx
|
|
469
|
+
var import_react5 = require("react");
|
|
470
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
471
|
+
var positionStyles = {
|
|
472
|
+
"bottom-right": { bottom: 24, right: 24 },
|
|
473
|
+
"bottom-left": { bottom: 24, left: 24 },
|
|
474
|
+
"top-right": { top: 24, right: 24 },
|
|
475
|
+
"top-left": { top: 24, left: 24 }
|
|
476
|
+
};
|
|
477
|
+
function PencilIcon() {
|
|
478
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
479
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" }),
|
|
480
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" })
|
|
481
|
+
] });
|
|
482
|
+
}
|
|
483
|
+
function ChatIcon() {
|
|
484
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) });
|
|
485
|
+
}
|
|
486
|
+
function WashiToolBubble({
|
|
487
|
+
position = "bottom-right",
|
|
488
|
+
sidebarOpen = false,
|
|
489
|
+
onSidebarToggle,
|
|
490
|
+
accentColor = "#667eea"
|
|
491
|
+
}) {
|
|
492
|
+
const { mode, setMode, isReady, comments } = useWashiContext();
|
|
493
|
+
const [hoveredBtn, setHoveredBtn] = (0, import_react5.useState)(null);
|
|
494
|
+
const isAnnotate = mode === "annotate";
|
|
495
|
+
const commentCount = comments.length;
|
|
496
|
+
const badgeLabel = commentCount > 99 ? "99+" : String(commentCount);
|
|
497
|
+
const pillStyle = {
|
|
498
|
+
position: "fixed",
|
|
499
|
+
...positionStyles[position],
|
|
500
|
+
zIndex: 9998,
|
|
501
|
+
display: "flex",
|
|
502
|
+
alignItems: "center",
|
|
503
|
+
gap: "4px",
|
|
504
|
+
padding: "6px",
|
|
505
|
+
borderRadius: "999px",
|
|
506
|
+
backgroundColor: "rgba(255,255,255,0.92)",
|
|
507
|
+
backdropFilter: "blur(8px)",
|
|
508
|
+
WebkitBackdropFilter: "blur(8px)",
|
|
509
|
+
boxShadow: "0 4px 16px rgba(0,0,0,0.12), 0 1px 4px rgba(0,0,0,0.08)",
|
|
510
|
+
border: "1px solid rgba(0,0,0,0.06)"
|
|
511
|
+
};
|
|
512
|
+
const btnBase = {
|
|
513
|
+
position: "relative",
|
|
514
|
+
display: "flex",
|
|
515
|
+
alignItems: "center",
|
|
516
|
+
justifyContent: "center",
|
|
517
|
+
width: 40,
|
|
518
|
+
height: 40,
|
|
519
|
+
borderRadius: "50%",
|
|
520
|
+
border: "none",
|
|
521
|
+
cursor: "pointer",
|
|
522
|
+
transition: "background-color 0.15s, box-shadow 0.15s, color 0.15s",
|
|
523
|
+
outline: "none"
|
|
524
|
+
};
|
|
525
|
+
const pencilActive = isAnnotate;
|
|
526
|
+
const pencilDisabled = !isReady;
|
|
527
|
+
const pencilStyle = {
|
|
528
|
+
...btnBase,
|
|
529
|
+
backgroundColor: pencilActive ? accentColor : hoveredBtn === "pencil" ? "rgba(0,0,0,0.06)" : "transparent",
|
|
530
|
+
color: pencilActive ? "#fff" : "#374151",
|
|
531
|
+
opacity: pencilDisabled ? 0.5 : 1,
|
|
532
|
+
cursor: pencilDisabled ? "not-allowed" : "pointer",
|
|
533
|
+
boxShadow: pencilActive ? `0 0 0 3px ${accentColor}33` : "none"
|
|
534
|
+
};
|
|
535
|
+
const chatStyle = {
|
|
536
|
+
...btnBase,
|
|
537
|
+
backgroundColor: sidebarOpen ? "rgba(0,0,0,0.06)" : hoveredBtn === "chat" ? "rgba(0,0,0,0.06)" : "transparent",
|
|
538
|
+
color: "#374151"
|
|
539
|
+
};
|
|
540
|
+
const badgeStyle = {
|
|
541
|
+
position: "absolute",
|
|
542
|
+
top: 4,
|
|
543
|
+
right: 4,
|
|
544
|
+
minWidth: 16,
|
|
545
|
+
height: 16,
|
|
546
|
+
borderRadius: "999px",
|
|
547
|
+
backgroundColor: accentColor,
|
|
548
|
+
color: "#fff",
|
|
549
|
+
fontSize: "10px",
|
|
550
|
+
fontWeight: 700,
|
|
551
|
+
display: "flex",
|
|
552
|
+
alignItems: "center",
|
|
553
|
+
justifyContent: "center",
|
|
554
|
+
padding: "0 3px",
|
|
555
|
+
lineHeight: 1,
|
|
556
|
+
pointerEvents: "none"
|
|
557
|
+
};
|
|
558
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: pillStyle, children: [
|
|
559
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
560
|
+
"button",
|
|
561
|
+
{
|
|
562
|
+
style: pencilStyle,
|
|
563
|
+
disabled: pencilDisabled,
|
|
564
|
+
title: isAnnotate ? "Exit annotate mode" : "Enter annotate mode",
|
|
565
|
+
onClick: () => !pencilDisabled && setMode(isAnnotate ? "view" : "annotate"),
|
|
566
|
+
onMouseEnter: () => setHoveredBtn("pencil"),
|
|
567
|
+
onMouseLeave: () => setHoveredBtn(null),
|
|
568
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PencilIcon, {})
|
|
569
|
+
}
|
|
570
|
+
),
|
|
571
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
572
|
+
"button",
|
|
573
|
+
{
|
|
574
|
+
style: chatStyle,
|
|
575
|
+
title: sidebarOpen ? "Close comments" : "Open comments",
|
|
576
|
+
onClick: onSidebarToggle,
|
|
577
|
+
onMouseEnter: () => setHoveredBtn("chat"),
|
|
578
|
+
onMouseLeave: () => setHoveredBtn(null),
|
|
579
|
+
children: [
|
|
580
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ChatIcon, {}),
|
|
581
|
+
commentCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: badgeStyle, children: badgeLabel })
|
|
582
|
+
]
|
|
583
|
+
}
|
|
584
|
+
)
|
|
585
|
+
] });
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// src/WashiCommentsSidebar.tsx
|
|
589
|
+
var import_react6 = require("react");
|
|
590
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
591
|
+
var BUBBLE_EDGE = 24;
|
|
592
|
+
var BUBBLE_HEIGHT = 52;
|
|
593
|
+
var GAP = 10;
|
|
594
|
+
function getPanelBounds(position) {
|
|
595
|
+
const reserved = BUBBLE_EDGE + BUBBLE_HEIGHT + GAP;
|
|
596
|
+
switch (position) {
|
|
597
|
+
case "bottom-right":
|
|
598
|
+
return { top: 0, right: 0, bottom: reserved };
|
|
599
|
+
case "bottom-left":
|
|
600
|
+
return { top: 0, left: 0, bottom: reserved };
|
|
601
|
+
case "top-right":
|
|
602
|
+
return { top: reserved, right: 0, bottom: 0 };
|
|
603
|
+
case "top-left":
|
|
604
|
+
return { top: reserved, left: 0, bottom: 0 };
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
var KEYFRAMES_ID = "__washi-panel-kf__";
|
|
608
|
+
if (typeof document !== "undefined" && !document.getElementById(KEYFRAMES_ID)) {
|
|
609
|
+
const style = document.createElement("style");
|
|
610
|
+
style.id = KEYFRAMES_ID;
|
|
611
|
+
style.textContent = `
|
|
612
|
+
@keyframes washi-panel-in-right { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
|
|
613
|
+
@keyframes washi-panel-out-right { from { transform: translateX(0); opacity: 1; } to { transform: translateX(100%); opacity: 0; } }
|
|
614
|
+
@keyframes washi-panel-in-left { from { transform: translateX(-100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
|
|
615
|
+
@keyframes washi-panel-out-left { from { transform: translateX(0); opacity: 1; } to { transform: translateX(-100%); opacity: 0; } }
|
|
616
|
+
`;
|
|
617
|
+
document.head.appendChild(style);
|
|
618
|
+
}
|
|
619
|
+
var ANIM_DURATION = 240;
|
|
620
|
+
function WashiCommentsSidebar({
|
|
621
|
+
open,
|
|
622
|
+
onClose,
|
|
623
|
+
position = "bottom-right",
|
|
624
|
+
accentColor = "#667eea"
|
|
625
|
+
}) {
|
|
626
|
+
const { comments, updateComment, deleteComment } = useWashiContext();
|
|
627
|
+
const [visible, setVisible] = (0, import_react6.useState)(open);
|
|
628
|
+
const [phase, setPhase] = (0, import_react6.useState)(open ? "in" : "out");
|
|
629
|
+
(0, import_react6.useEffect)(() => {
|
|
630
|
+
if (open) {
|
|
631
|
+
setVisible(true);
|
|
632
|
+
const raf = requestAnimationFrame(() => setPhase("in"));
|
|
633
|
+
return () => cancelAnimationFrame(raf);
|
|
634
|
+
} else {
|
|
635
|
+
setPhase("out");
|
|
636
|
+
const t = setTimeout(() => setVisible(false), ANIM_DURATION);
|
|
637
|
+
return () => clearTimeout(t);
|
|
638
|
+
}
|
|
639
|
+
}, [open]);
|
|
640
|
+
if (!visible) return null;
|
|
641
|
+
const sortedComments = [...comments].sort((a, b) => a.createdAt - b.createdAt);
|
|
642
|
+
const side = position.endsWith("right") ? "right" : "left";
|
|
643
|
+
const animName = phase === "in" ? `washi-panel-in-${side}` : `washi-panel-out-${side}`;
|
|
644
|
+
const panelStyle = {
|
|
645
|
+
position: "fixed",
|
|
646
|
+
...getPanelBounds(position),
|
|
647
|
+
width: 320,
|
|
648
|
+
zIndex: 9999,
|
|
649
|
+
display: "flex",
|
|
650
|
+
flexDirection: "column",
|
|
651
|
+
backgroundColor: "#f9fafb",
|
|
652
|
+
boxShadow: side === "right" ? "-4px 0 24px rgba(0,0,0,0.10)" : "4px 0 24px rgba(0,0,0,0.10)",
|
|
653
|
+
borderLeft: side === "right" ? "1px solid #e5e7eb" : "none",
|
|
654
|
+
borderRight: side === "left" ? "1px solid #e5e7eb" : "none",
|
|
655
|
+
animation: `${animName} ${ANIM_DURATION}ms cubic-bezier(0.4,0,0.2,1) forwards`,
|
|
656
|
+
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif"
|
|
657
|
+
};
|
|
658
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: panelStyle, children: [
|
|
659
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: {
|
|
660
|
+
display: "flex",
|
|
661
|
+
alignItems: "center",
|
|
662
|
+
justifyContent: "space-between",
|
|
663
|
+
padding: "16px 20px",
|
|
664
|
+
borderBottom: "1px solid #e5e7eb",
|
|
665
|
+
backgroundColor: "#fff",
|
|
666
|
+
flexShrink: 0
|
|
667
|
+
}, children: [
|
|
668
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { style: { fontSize: "1rem", fontWeight: 600, color: "#1f2937" }, children: [
|
|
669
|
+
"Comments (",
|
|
670
|
+
comments.length,
|
|
671
|
+
")"
|
|
672
|
+
] }),
|
|
673
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
674
|
+
"button",
|
|
675
|
+
{
|
|
676
|
+
onClick: onClose,
|
|
677
|
+
title: "Close",
|
|
678
|
+
style: {
|
|
679
|
+
display: "flex",
|
|
680
|
+
alignItems: "center",
|
|
681
|
+
justifyContent: "center",
|
|
682
|
+
width: 30,
|
|
683
|
+
height: 30,
|
|
684
|
+
border: "none",
|
|
685
|
+
borderRadius: 6,
|
|
686
|
+
backgroundColor: "transparent",
|
|
687
|
+
cursor: "pointer",
|
|
688
|
+
color: "#9ca3af",
|
|
689
|
+
fontSize: 14,
|
|
690
|
+
padding: 0
|
|
691
|
+
},
|
|
692
|
+
children: "\u2715"
|
|
693
|
+
}
|
|
694
|
+
)
|
|
695
|
+
] }),
|
|
696
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { flex: 1, overflowY: "auto", padding: "12px" }, children: sortedComments.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: {
|
|
697
|
+
textAlign: "center",
|
|
698
|
+
padding: "40px 20px",
|
|
699
|
+
color: "#9ca3af",
|
|
700
|
+
fontSize: "0.875rem"
|
|
701
|
+
}, children: [
|
|
702
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { style: { marginBottom: 8 }, children: "No comments yet." }),
|
|
703
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { children: "Use the annotate tool to add one." })
|
|
704
|
+
] }) : sortedComments.map((comment, index) => {
|
|
705
|
+
const color = comment.color || accentColor;
|
|
706
|
+
const borderColor = comment.resolved ? "#10b981" : color;
|
|
707
|
+
const badgeBg = comment.resolved ? "#10b981" : color;
|
|
708
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
709
|
+
"div",
|
|
710
|
+
{
|
|
711
|
+
style: {
|
|
712
|
+
backgroundColor: "#fff",
|
|
713
|
+
border: "1px solid #e5e7eb",
|
|
714
|
+
borderLeft: `3px solid ${borderColor}`,
|
|
715
|
+
borderRadius: 8,
|
|
716
|
+
padding: "12px",
|
|
717
|
+
marginBottom: 10,
|
|
718
|
+
opacity: comment.resolved ? 0.7 : 1
|
|
719
|
+
},
|
|
720
|
+
children: [
|
|
721
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 8 }, children: [
|
|
722
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", alignItems: "center" }, children: [
|
|
723
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: {
|
|
724
|
+
display: "inline-flex",
|
|
725
|
+
alignItems: "center",
|
|
726
|
+
justifyContent: "center",
|
|
727
|
+
width: 20,
|
|
728
|
+
height: 20,
|
|
729
|
+
borderRadius: "50%",
|
|
730
|
+
backgroundColor: badgeBg,
|
|
731
|
+
color: "#fff",
|
|
732
|
+
fontSize: "0.7rem",
|
|
733
|
+
fontWeight: 700,
|
|
734
|
+
marginRight: 8,
|
|
735
|
+
flexShrink: 0
|
|
736
|
+
}, children: comment.resolved ? "\u2713" : index + 1 }),
|
|
737
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { style: { fontSize: "0.75rem", color: "#6b7280" }, children: [
|
|
738
|
+
"(",
|
|
739
|
+
comment.x.toFixed(1),
|
|
740
|
+
"%, ",
|
|
741
|
+
comment.y.toFixed(1),
|
|
742
|
+
"%)"
|
|
743
|
+
] })
|
|
744
|
+
] }),
|
|
745
|
+
comment.resolved && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { style: { fontSize: "0.75rem", color: "#059669", fontWeight: 500 }, children: "Resolved" })
|
|
746
|
+
] }),
|
|
747
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { style: { fontSize: "0.875rem", color: "#374151", marginBottom: 10, lineHeight: 1.5 }, children: comment.text }),
|
|
748
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", gap: 8 }, children: [
|
|
749
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
750
|
+
"button",
|
|
751
|
+
{
|
|
752
|
+
style: {
|
|
753
|
+
padding: "4px 10px",
|
|
754
|
+
fontSize: "0.75rem",
|
|
755
|
+
border: "none",
|
|
756
|
+
borderRadius: 4,
|
|
757
|
+
cursor: "pointer",
|
|
758
|
+
backgroundColor: "#e0e7ff",
|
|
759
|
+
color: "#4f46e5"
|
|
760
|
+
},
|
|
761
|
+
onClick: () => updateComment(comment.id, { resolved: !comment.resolved }),
|
|
762
|
+
children: comment.resolved ? "Unresolve" : "Resolve"
|
|
763
|
+
}
|
|
764
|
+
),
|
|
765
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
766
|
+
"button",
|
|
767
|
+
{
|
|
768
|
+
style: {
|
|
769
|
+
padding: "4px 10px",
|
|
770
|
+
fontSize: "0.75rem",
|
|
771
|
+
border: "none",
|
|
772
|
+
borderRadius: 4,
|
|
773
|
+
cursor: "pointer",
|
|
774
|
+
backgroundColor: "#fee2e2",
|
|
775
|
+
color: "#dc2626"
|
|
776
|
+
},
|
|
777
|
+
onClick: () => deleteComment(comment.id),
|
|
778
|
+
children: "Delete"
|
|
779
|
+
}
|
|
780
|
+
)
|
|
781
|
+
] })
|
|
782
|
+
]
|
|
783
|
+
},
|
|
784
|
+
comment.id
|
|
785
|
+
);
|
|
786
|
+
}) })
|
|
787
|
+
] });
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// src/WashiPinDialog.tsx
|
|
791
|
+
var import_react7 = require("react");
|
|
792
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
793
|
+
function WashiPinDialog({ accentColor = "#667eea", onComment }) {
|
|
794
|
+
const { onPinPlaced, addComment, setMode, iframeEl } = useWashiContext();
|
|
795
|
+
const [pending, setPending] = (0, import_react7.useState)(null);
|
|
796
|
+
const [text, setText] = (0, import_react7.useState)("");
|
|
797
|
+
(0, import_react7.useEffect)(() => {
|
|
798
|
+
return onPinPlaced((event) => {
|
|
799
|
+
const containerW = iframeEl?.clientWidth ?? window.innerWidth;
|
|
800
|
+
const containerH = iframeEl?.clientHeight ?? window.innerHeight;
|
|
801
|
+
setText("");
|
|
802
|
+
setPending({
|
|
803
|
+
x: event.x,
|
|
804
|
+
y: event.y,
|
|
805
|
+
pixelX: event.x / 100 * containerW,
|
|
806
|
+
pixelY: event.y / 100 * containerH,
|
|
807
|
+
containerW,
|
|
808
|
+
containerH
|
|
809
|
+
});
|
|
810
|
+
});
|
|
811
|
+
}, [onPinPlaced, iframeEl]);
|
|
812
|
+
if (!pending) return null;
|
|
813
|
+
const handleSubmit = async () => {
|
|
814
|
+
if (!text.trim()) return;
|
|
815
|
+
const comment = await addComment({ x: pending.x, y: pending.y, text: text.trim(), color: accentColor });
|
|
816
|
+
setPending(null);
|
|
817
|
+
setMode("view");
|
|
818
|
+
onComment?.(comment);
|
|
819
|
+
};
|
|
820
|
+
const handleCancel = () => setPending(null);
|
|
821
|
+
const handleKeyDown = (e) => {
|
|
822
|
+
if (e.key === "Enter" && e.metaKey) handleSubmit();
|
|
823
|
+
if (e.key === "Escape") handleCancel();
|
|
824
|
+
};
|
|
825
|
+
const POPOVER_WIDTH = 280;
|
|
826
|
+
const left = Math.min(
|
|
827
|
+
Math.max(pending.pixelX - POPOVER_WIDTH / 2, 8),
|
|
828
|
+
pending.containerW - POPOVER_WIDTH - 8
|
|
829
|
+
);
|
|
830
|
+
const showAbove = pending.pixelY > pending.containerH * 0.65;
|
|
831
|
+
const top = showAbove ? pending.pixelY - 172 : pending.pixelY + 16;
|
|
832
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
833
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { position: "fixed", inset: 0, zIndex: 9999 }, onClick: handleCancel }),
|
|
834
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
835
|
+
"div",
|
|
836
|
+
{
|
|
837
|
+
style: {
|
|
838
|
+
position: "fixed",
|
|
839
|
+
left,
|
|
840
|
+
top,
|
|
841
|
+
width: POPOVER_WIDTH,
|
|
842
|
+
zIndex: 1e4,
|
|
843
|
+
backgroundColor: "#fff",
|
|
844
|
+
borderRadius: 12,
|
|
845
|
+
boxShadow: "0 8px 32px rgba(0,0,0,0.18), 0 2px 8px rgba(0,0,0,0.10)",
|
|
846
|
+
border: "1px solid rgba(0,0,0,0.06)",
|
|
847
|
+
padding: "14px 16px",
|
|
848
|
+
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif"
|
|
849
|
+
},
|
|
850
|
+
children: [
|
|
851
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 10 }, children: [
|
|
852
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { fontSize: "0.875rem", fontWeight: 600, color: "#1f2937" }, children: "Add comment" }),
|
|
853
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
854
|
+
"button",
|
|
855
|
+
{
|
|
856
|
+
onClick: handleCancel,
|
|
857
|
+
style: {
|
|
858
|
+
border: "none",
|
|
859
|
+
background: "none",
|
|
860
|
+
cursor: "pointer",
|
|
861
|
+
color: "#9ca3af",
|
|
862
|
+
fontSize: 14,
|
|
863
|
+
padding: "2px 4px",
|
|
864
|
+
borderRadius: 4
|
|
865
|
+
},
|
|
866
|
+
children: "\u2715"
|
|
867
|
+
}
|
|
868
|
+
)
|
|
869
|
+
] }),
|
|
870
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
871
|
+
"textarea",
|
|
872
|
+
{
|
|
873
|
+
style: {
|
|
874
|
+
width: "100%",
|
|
875
|
+
height: 80,
|
|
876
|
+
padding: "8px 10px",
|
|
877
|
+
border: "1px solid #e5e7eb",
|
|
878
|
+
borderRadius: 8,
|
|
879
|
+
fontSize: "0.8125rem",
|
|
880
|
+
resize: "none",
|
|
881
|
+
boxSizing: "border-box",
|
|
882
|
+
lineHeight: 1.5,
|
|
883
|
+
fontFamily: "inherit",
|
|
884
|
+
color: "#374151",
|
|
885
|
+
outline: "none"
|
|
886
|
+
},
|
|
887
|
+
placeholder: "Leave a comment...",
|
|
888
|
+
value: text,
|
|
889
|
+
onChange: (e) => setText(e.target.value),
|
|
890
|
+
onKeyDown: handleKeyDown,
|
|
891
|
+
autoFocus: true
|
|
892
|
+
}
|
|
893
|
+
),
|
|
894
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 10 }, children: [
|
|
895
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
896
|
+
"button",
|
|
897
|
+
{
|
|
898
|
+
style: {
|
|
899
|
+
padding: "6px 12px",
|
|
900
|
+
fontSize: "0.8125rem",
|
|
901
|
+
border: "1px solid #e5e7eb",
|
|
902
|
+
borderRadius: 6,
|
|
903
|
+
backgroundColor: "#fff",
|
|
904
|
+
cursor: "pointer",
|
|
905
|
+
color: "#6b7280"
|
|
906
|
+
},
|
|
907
|
+
onClick: handleCancel,
|
|
908
|
+
children: "Cancel"
|
|
909
|
+
}
|
|
910
|
+
),
|
|
911
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
912
|
+
"button",
|
|
913
|
+
{
|
|
914
|
+
style: {
|
|
915
|
+
padding: "6px 12px",
|
|
916
|
+
fontSize: "0.8125rem",
|
|
917
|
+
border: "none",
|
|
918
|
+
borderRadius: 6,
|
|
919
|
+
backgroundColor: accentColor,
|
|
920
|
+
color: "#fff",
|
|
921
|
+
cursor: text.trim() ? "pointer" : "not-allowed",
|
|
922
|
+
opacity: text.trim() ? 1 : 0.5
|
|
923
|
+
},
|
|
924
|
+
onClick: handleSubmit,
|
|
925
|
+
disabled: !text.trim(),
|
|
926
|
+
children: "Add"
|
|
927
|
+
}
|
|
928
|
+
)
|
|
929
|
+
] })
|
|
930
|
+
]
|
|
931
|
+
}
|
|
932
|
+
)
|
|
933
|
+
] });
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
// src/WashiUI.tsx
|
|
937
|
+
var import_react8 = require("react");
|
|
938
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
939
|
+
var SPINNER_ID = "__washi-ui-spinner__";
|
|
940
|
+
if (typeof document !== "undefined" && !document.getElementById(SPINNER_ID)) {
|
|
941
|
+
const style = document.createElement("style");
|
|
942
|
+
style.id = SPINNER_ID;
|
|
943
|
+
style.textContent = `@keyframes __washi-spin { to { transform: rotate(360deg); } }`;
|
|
944
|
+
document.head.appendChild(style);
|
|
945
|
+
}
|
|
946
|
+
function WashiUI({
|
|
947
|
+
position = "bottom-right",
|
|
948
|
+
accentColor = "#667eea",
|
|
949
|
+
showLoader = true
|
|
950
|
+
}) {
|
|
951
|
+
const { isReady } = useWashiContext();
|
|
952
|
+
const [sidebarOpen, setSidebarOpen] = (0, import_react8.useState)(false);
|
|
953
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
954
|
+
showLoader && !isReady && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
955
|
+
"div",
|
|
956
|
+
{
|
|
957
|
+
style: {
|
|
958
|
+
position: "fixed",
|
|
959
|
+
inset: 0,
|
|
960
|
+
zIndex: 100,
|
|
961
|
+
display: "flex",
|
|
962
|
+
alignItems: "center",
|
|
963
|
+
justifyContent: "center",
|
|
964
|
+
backgroundColor: "rgba(255,255,255,0.9)"
|
|
965
|
+
},
|
|
966
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
967
|
+
"div",
|
|
968
|
+
{
|
|
969
|
+
style: {
|
|
970
|
+
width: 40,
|
|
971
|
+
height: 40,
|
|
972
|
+
borderRadius: "50%",
|
|
973
|
+
border: "3px solid #e5e7eb",
|
|
974
|
+
borderTopColor: accentColor,
|
|
975
|
+
animation: "__washi-spin 1s linear infinite"
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
)
|
|
979
|
+
}
|
|
980
|
+
),
|
|
981
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
982
|
+
WashiToolBubble,
|
|
983
|
+
{
|
|
984
|
+
position,
|
|
985
|
+
accentColor,
|
|
986
|
+
sidebarOpen,
|
|
987
|
+
onSidebarToggle: () => setSidebarOpen((o) => !o)
|
|
988
|
+
}
|
|
989
|
+
),
|
|
990
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
991
|
+
WashiCommentsSidebar,
|
|
992
|
+
{
|
|
993
|
+
open: sidebarOpen,
|
|
994
|
+
onClose: () => setSidebarOpen(false),
|
|
995
|
+
position,
|
|
996
|
+
accentColor
|
|
997
|
+
}
|
|
998
|
+
),
|
|
999
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(WashiPinDialog, { accentColor })
|
|
1000
|
+
] });
|
|
1001
|
+
}
|
|
1002
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1003
|
+
0 && (module.exports = {
|
|
1004
|
+
CommentList,
|
|
1005
|
+
WashiCommentsSidebar,
|
|
1006
|
+
WashiFrame,
|
|
1007
|
+
WashiPinDialog,
|
|
1008
|
+
WashiProvider,
|
|
1009
|
+
WashiToolBubble,
|
|
1010
|
+
WashiUI,
|
|
1011
|
+
useWashi,
|
|
1012
|
+
useWashiContext
|
|
1013
|
+
});
|
|
1014
|
+
//# sourceMappingURL=index.js.map
|