@joewinke/jatui 0.2.5 → 0.2.6
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/CLAUDE.md +19 -2
- package/dist/components/AnnotationPanel.svelte +64 -57
- package/dist/components/AnnotationPanel.svelte.d.ts +2 -0
- package/dist/components/ImageLightbox.svelte +5 -0
- package/dist/components/ImageLightbox.svelte.d.ts +2 -0
- package/dist/dnd/types.d.ts +0 -9
- package/package.json +2 -2
package/CLAUDE.md
CHANGED
|
@@ -106,9 +106,9 @@ Then import:
|
|
|
106
106
|
|-----------|---------|-----------|
|
|
107
107
|
| `MarkupEditor` | Fullscreen canvas markup tool — draw arrows, boxes, ellipses, revision clouds, freehand, text over an image | `imageUrl`, `initialShapes?: MarkupShape[]`, `onsave: (shapes) => void`, `oncancel: () => void` |
|
|
108
108
|
| `MarkupOverlay` | Read-only canvas layer that renders saved `MarkupShape[]` over an image | `shapes: MarkupShape[]`, `imageUrl` |
|
|
109
|
-
| `ImageLightbox` | Inline image viewer with fullscreen modal, zoom controls, optional markup + annotation overlays | `imageUrl`, `filename?`, `markupShapes?`, `annotations?`, `activeAnnotationId?`, `annotationMode?`, `onAnnotationClick?`, `onImageClick?`, `onClose?` |
|
|
109
|
+
| `ImageLightbox` | Inline image viewer with fullscreen modal, zoom controls, optional markup + annotation overlays | `imageUrl`, `filename?`, `markupShapes?`, `annotations?`, `activeAnnotationId?`, `annotationMode?`, `showResolved?` (forwarded to the internal `AnnotationLayer`; default false), `onAnnotationClick?`, `onImageClick?`, `onClose?` |
|
|
110
110
|
| `AnnotationLayer` | Absolute-positioned pin overlay — renders threaded annotation pins at `x_percent`/`y_percent` coordinates | `annotations: Annotation[]`, `activeAnnotationId?`, `annotationMode?`, `showResolved?`, `onAnnotationClick?`, `onImageClick?` |
|
|
111
|
-
| `AnnotationPanel` | Sidebar thread list for annotations — reply, resolve, edit, delete. Uses callback props for async ops. | `annotations`, `profiles`, `activeAnnotationId?`, `currentUserId`, `callbacks: AnnotationCallbacks`, `onAnnotationSelect?`, `onAnnotationsChanged?` |
|
|
111
|
+
| `AnnotationPanel` | Sidebar thread list for annotations — reply, resolve, edit, delete. Uses callback props for async ops. | `annotations`, `profiles`, `activeAnnotationId?`, `currentUserId`, `callbacks: AnnotationCallbacks`, `readOnly?` (hides reply/resolve/edit/delete affordances; thread list stays readable), `onAnnotationSelect?`, `onAnnotationsChanged?` |
|
|
112
112
|
|
|
113
113
|
**Types & utilities:**
|
|
114
114
|
```typescript
|
|
@@ -239,6 +239,23 @@ If the project uses JST (Joe's Software Template), browse component demos at `/c
|
|
|
239
239
|
- Check the component source in `src/lib/components/` for full prop types
|
|
240
240
|
- Prefer jatui components over building custom ones when there's a match
|
|
241
241
|
|
|
242
|
+
## Releasing — never run `npm publish`
|
|
243
|
+
|
|
244
|
+
**To cut a release: bump `version` in `package.json`, commit, push to master. That's the whole job.**
|
|
245
|
+
|
|
246
|
+
`.github/workflows/publish.yml` publishes to npm on any push to master that touches
|
|
247
|
+
`package.json`, authenticating via npm Trusted Publishing (OIDC) — no token, and every
|
|
248
|
+
release carries a provenance attestation. The workflow is idempotent: if the version is
|
|
249
|
+
already on the registry it exits green without republishing, so re-running it is safe.
|
|
250
|
+
|
|
251
|
+
Publishing from a dev box instead does two bad things: the release is attributed to a
|
|
252
|
+
person rather than the attested `GitHub Actions` identity (**provenance is lost**), and it
|
|
253
|
+
races the workflow — that is exactly how 0.2.5 shipped by hand at 16:16:21Z and left CI red
|
|
254
|
+
with an `E403: cannot publish over the previously published version`.
|
|
255
|
+
|
|
256
|
+
`prepublishOnly` now runs `scripts/guard-ci-publish.mjs`, which hard-fails any publish
|
|
257
|
+
outside CI. If you hit that error, you are doing it wrong — bump the version and push.
|
|
258
|
+
|
|
242
259
|
# Layering Policy (DaisyUI is an implementation detail)
|
|
243
260
|
|
|
244
261
|
Three rules keep DaisyUI swappable instead of load-bearing:
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
activeAnnotationId?: string | null
|
|
8
8
|
currentUserId: string
|
|
9
9
|
callbacks: AnnotationCallbacks
|
|
10
|
+
/** Hides reply/resolve/edit/delete affordances — thread list stays readable */
|
|
11
|
+
readOnly?: boolean
|
|
10
12
|
onAnnotationSelect?: (annotation: Annotation | null) => void
|
|
11
13
|
onAnnotationsChanged?: () => void
|
|
12
14
|
}
|
|
@@ -17,6 +19,7 @@
|
|
|
17
19
|
activeAnnotationId = null,
|
|
18
20
|
currentUserId,
|
|
19
21
|
callbacks,
|
|
22
|
+
readOnly = false,
|
|
20
23
|
onAnnotationSelect,
|
|
21
24
|
onAnnotationsChanged,
|
|
22
25
|
}: Props = $props()
|
|
@@ -202,37 +205,39 @@
|
|
|
202
205
|
{#if isActive}
|
|
203
206
|
<div class="px-3 pb-3 space-y-2">
|
|
204
207
|
<!-- Actions for root -->
|
|
205
|
-
|
|
206
|
-
<
|
|
207
|
-
class="btn btn-ghost btn-xs gap-1 {thread.root.resolved ? 'text-warning' : 'text-success'}"
|
|
208
|
-
onclick={() => toggleResolve(thread.root)}
|
|
209
|
-
>
|
|
210
|
-
{#if thread.root.resolved}
|
|
211
|
-
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
212
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
|
213
|
-
</svg>
|
|
214
|
-
Reopen
|
|
215
|
-
{:else}
|
|
216
|
-
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
217
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
|
|
218
|
-
</svg>
|
|
219
|
-
Resolve
|
|
220
|
-
{/if}
|
|
221
|
-
</button>
|
|
222
|
-
{#if thread.root.user_id === currentUserId}
|
|
223
|
-
<button
|
|
224
|
-
class="btn btn-ghost btn-xs text-base-content/50"
|
|
225
|
-
onclick={() => startEdit(thread.root)}
|
|
226
|
-
>Edit</button>
|
|
208
|
+
{#if !readOnly}
|
|
209
|
+
<div class="flex items-center gap-1 ml-8">
|
|
227
210
|
<button
|
|
228
|
-
class="btn btn-ghost btn-xs text-
|
|
229
|
-
onclick={() =>
|
|
230
|
-
>
|
|
231
|
-
|
|
232
|
-
|
|
211
|
+
class="btn btn-ghost btn-xs gap-1 {thread.root.resolved ? 'text-warning' : 'text-success'}"
|
|
212
|
+
onclick={() => toggleResolve(thread.root)}
|
|
213
|
+
>
|
|
214
|
+
{#if thread.root.resolved}
|
|
215
|
+
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
216
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
|
217
|
+
</svg>
|
|
218
|
+
Reopen
|
|
219
|
+
{:else}
|
|
220
|
+
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
221
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
|
|
222
|
+
</svg>
|
|
223
|
+
Resolve
|
|
224
|
+
{/if}
|
|
225
|
+
</button>
|
|
226
|
+
{#if thread.root.user_id === currentUserId}
|
|
227
|
+
<button
|
|
228
|
+
class="btn btn-ghost btn-xs text-base-content/50"
|
|
229
|
+
onclick={() => startEdit(thread.root)}
|
|
230
|
+
>Edit</button>
|
|
231
|
+
<button
|
|
232
|
+
class="btn btn-ghost btn-xs text-error/70"
|
|
233
|
+
onclick={() => deleteAnnotation(thread.root.id)}
|
|
234
|
+
>Delete</button>
|
|
235
|
+
{/if}
|
|
236
|
+
</div>
|
|
237
|
+
{/if}
|
|
233
238
|
|
|
234
239
|
<!-- Edit form for root -->
|
|
235
|
-
{#if editingId === thread.root.id}
|
|
240
|
+
{#if !readOnly && editingId === thread.root.id}
|
|
236
241
|
<div class="ml-8 space-y-1.5">
|
|
237
242
|
<textarea
|
|
238
243
|
class="textarea textarea-bordered textarea-sm w-full"
|
|
@@ -257,7 +262,7 @@
|
|
|
257
262
|
<span class="text-xs font-medium">{getAuthorName(reply.user_id)}</span>
|
|
258
263
|
<span class="text-[10px] text-base-content/40">{formatTime(reply.created_at)}</span>
|
|
259
264
|
</div>
|
|
260
|
-
{#if editingId === reply.id}
|
|
265
|
+
{#if !readOnly && editingId === reply.id}
|
|
261
266
|
<div class="mt-1 space-y-1.5">
|
|
262
267
|
<textarea
|
|
263
268
|
class="textarea textarea-bordered textarea-xs w-full"
|
|
@@ -271,7 +276,7 @@
|
|
|
271
276
|
</div>
|
|
272
277
|
{:else}
|
|
273
278
|
<p class="text-sm text-base-content/80 mt-0.5">{reply.content}</p>
|
|
274
|
-
{#if reply.user_id === currentUserId}
|
|
279
|
+
{#if !readOnly && reply.user_id === currentUserId}
|
|
275
280
|
<div class="flex gap-1 mt-1">
|
|
276
281
|
<button class="btn btn-ghost btn-xs text-base-content/40 h-5 min-h-0" onclick={() => startEdit(reply)}>Edit</button>
|
|
277
282
|
<button class="btn btn-ghost btn-xs text-error/50 h-5 min-h-0" onclick={() => deleteAnnotation(reply.id)}>Delete</button>
|
|
@@ -283,33 +288,35 @@
|
|
|
283
288
|
{/each}
|
|
284
289
|
|
|
285
290
|
<!-- Reply input -->
|
|
286
|
-
|
|
287
|
-
<
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
e.
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
<
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
291
|
+
{#if !readOnly}
|
|
292
|
+
<div class="ml-8 flex gap-2">
|
|
293
|
+
<textarea
|
|
294
|
+
class="textarea textarea-bordered textarea-sm flex-1 min-h-[36px]"
|
|
295
|
+
rows="1"
|
|
296
|
+
placeholder="Reply..."
|
|
297
|
+
bind:value={replyContent}
|
|
298
|
+
onkeydown={(e) => {
|
|
299
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
300
|
+
e.preventDefault()
|
|
301
|
+
submitReply(thread.root.id)
|
|
302
|
+
}
|
|
303
|
+
}}
|
|
304
|
+
></textarea>
|
|
305
|
+
<button
|
|
306
|
+
class="btn btn-sm btn-primary self-end"
|
|
307
|
+
disabled={!replyContent.trim() || isSubmitting}
|
|
308
|
+
onclick={() => submitReply(thread.root.id)}
|
|
309
|
+
>
|
|
310
|
+
{#if isSubmitting}
|
|
311
|
+
<span class="loading loading-spinner loading-xs"></span>
|
|
312
|
+
{:else}
|
|
313
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
314
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8" />
|
|
315
|
+
</svg>
|
|
316
|
+
{/if}
|
|
317
|
+
</button>
|
|
318
|
+
</div>
|
|
319
|
+
{/if}
|
|
313
320
|
</div>
|
|
314
321
|
{/if}
|
|
315
322
|
</div>
|
|
@@ -5,6 +5,8 @@ interface Props {
|
|
|
5
5
|
activeAnnotationId?: string | null;
|
|
6
6
|
currentUserId: string;
|
|
7
7
|
callbacks: AnnotationCallbacks;
|
|
8
|
+
/** Hides reply/resolve/edit/delete affordances — thread list stays readable */
|
|
9
|
+
readOnly?: boolean;
|
|
8
10
|
onAnnotationSelect?: (annotation: Annotation | null) => void;
|
|
9
11
|
onAnnotationsChanged?: () => void;
|
|
10
12
|
}
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
activeAnnotationId?: string | null
|
|
18
18
|
/** Whether annotation placement mode is active */
|
|
19
19
|
annotationMode?: boolean
|
|
20
|
+
/** Whether resolved annotation pins are shown on the image (default false, matching AnnotationLayer) */
|
|
21
|
+
showResolved?: boolean
|
|
20
22
|
/** Called when a pin is clicked */
|
|
21
23
|
onAnnotationClick?: (annotation: Annotation) => void
|
|
22
24
|
/** Called when the image is clicked in annotation mode */
|
|
@@ -32,6 +34,7 @@
|
|
|
32
34
|
annotations = [],
|
|
33
35
|
activeAnnotationId = null,
|
|
34
36
|
annotationMode = false,
|
|
37
|
+
showResolved = false,
|
|
35
38
|
onAnnotationClick,
|
|
36
39
|
onImageClick,
|
|
37
40
|
onClose,
|
|
@@ -168,6 +171,7 @@
|
|
|
168
171
|
{annotations}
|
|
169
172
|
{activeAnnotationId}
|
|
170
173
|
{annotationMode}
|
|
174
|
+
{showResolved}
|
|
171
175
|
{onAnnotationClick}
|
|
172
176
|
{onImageClick}
|
|
173
177
|
/>
|
|
@@ -265,6 +269,7 @@
|
|
|
265
269
|
{annotations}
|
|
266
270
|
{activeAnnotationId}
|
|
267
271
|
annotationMode={false}
|
|
272
|
+
{showResolved}
|
|
268
273
|
{onAnnotationClick}
|
|
269
274
|
/>
|
|
270
275
|
{/if}
|
|
@@ -13,6 +13,8 @@ interface Props {
|
|
|
13
13
|
activeAnnotationId?: string | null;
|
|
14
14
|
/** Whether annotation placement mode is active */
|
|
15
15
|
annotationMode?: boolean;
|
|
16
|
+
/** Whether resolved annotation pins are shown on the image (default false, matching AnnotationLayer) */
|
|
17
|
+
showResolved?: boolean;
|
|
16
18
|
/** Called when a pin is clicked */
|
|
17
19
|
onAnnotationClick?: (annotation: Annotation) => void;
|
|
18
20
|
/** Called when the image is clicked in annotation mode */
|
package/dist/dnd/types.d.ts
CHANGED
|
@@ -2,16 +2,7 @@ import type { Input } from '@atlaskit/pragmatic-drag-and-drop/types';
|
|
|
2
2
|
export interface DragData extends Record<string | symbol, unknown> {
|
|
3
3
|
readonly type: string;
|
|
4
4
|
}
|
|
5
|
-
/**
|
|
6
|
-
* Module-scoped Symbol brand for board-managed items.
|
|
7
|
-
* ⚠ Symbol identity = module identity. Two copies of this module on one page makes
|
|
8
|
-
* isItemData() return false across the boundary — dnd/ must exist EXACTLY ONCE.
|
|
9
|
-
* Declare all @atlaskit/pragmatic-drag-and-drop* packages as peerDependencies in
|
|
10
|
-
* the host app (host owns the single instance).
|
|
11
|
-
*/
|
|
12
|
-
declare const ITEM: unique symbol;
|
|
13
5
|
export interface ItemDragData<TItem = unknown> extends DragData {
|
|
14
|
-
readonly [ITEM]: true;
|
|
15
6
|
readonly key: string;
|
|
16
7
|
readonly item: TItem;
|
|
17
8
|
readonly container: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@joewinke/jatui",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Shared Svelte 5 component library for JAT projects",
|
|
6
6
|
"type": "module",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"package": "svelte-package --input src/lib --output dist && node scripts/compile-rune-modules.mjs",
|
|
71
71
|
"check:demos": "node scripts/check-demo-coverage.mjs",
|
|
72
72
|
"guard": "node scripts/check-demo-coverage.mjs",
|
|
73
|
-
"prepublishOnly": "npm run package"
|
|
73
|
+
"prepublishOnly": "node scripts/guard-ci-publish.mjs && npm run package"
|
|
74
74
|
},
|
|
75
75
|
"peerDependencies": {
|
|
76
76
|
"@atlaskit/pragmatic-drag-and-drop": "~2.0.0",
|