@devalok/shilp-sutra 0.55.0 → 0.56.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.
Files changed (74) hide show
  1. package/BREAKING.json +13 -0
  2. package/MIGRATION.md +56 -0
  3. package/README.md +16 -1
  4. package/dist/_chunks/badge-group.js +45 -45
  5. package/dist/_chunks/emoji-suggestion.js +32 -30
  6. package/dist/_chunks/emoji-suggestion.js.map +1 -1
  7. package/dist/_chunks/mention-suggestion.js +35 -34
  8. package/dist/_chunks/mention-suggestion.js.map +1 -1
  9. package/dist/_chunks/primitives-internal.js +437 -537
  10. package/dist/_chunks/primitives-internal.js.map +1 -1
  11. package/dist/composed/content-card.js +18 -18
  12. package/dist/composed/extensions/emoji-node.js +8 -7
  13. package/dist/composed/extensions/emoji-node.js.map +1 -1
  14. package/dist/composed/rich-chat-input.js +535 -526
  15. package/dist/composed/rich-chat-input.js.map +1 -1
  16. package/dist/composed/rich-text-editor.js +201 -193
  17. package/dist/composed/rich-text-editor.js.map +1 -1
  18. package/dist/tokens/primitives.css +6 -1
  19. package/dist/tokens/semantic.css +1 -1
  20. package/dist/ui/alert.js +29 -29
  21. package/dist/ui/avatar.js +42 -42
  22. package/dist/ui/banner.js +25 -25
  23. package/dist/ui/button.js +31 -31
  24. package/dist/ui/button.js.map +1 -1
  25. package/dist/ui/card.js +39 -39
  26. package/dist/ui/checkbox.d.ts.map +1 -1
  27. package/dist/ui/checkbox.js +1 -0
  28. package/dist/ui/checkbox.js.map +1 -1
  29. package/dist/ui/combobox.js +135 -135
  30. package/dist/ui/combobox.js.map +1 -1
  31. package/dist/ui/dot.js +13 -13
  32. package/dist/ui/input.js +25 -25
  33. package/dist/ui/lib/utils.js +2 -1
  34. package/dist/ui/lib/utils.js.map +1 -1
  35. package/dist/ui/number-input.js +36 -36
  36. package/dist/ui/progress.js +55 -55
  37. package/dist/ui/select.js +58 -58
  38. package/dist/ui/sheet.js +46 -46
  39. package/dist/ui/sidebar.js +128 -128
  40. package/dist/ui/sidebar.js.map +1 -1
  41. package/dist/ui/skeleton.js +39 -39
  42. package/dist/ui/slider.d.ts.map +1 -1
  43. package/dist/ui/slider.js +18 -17
  44. package/dist/ui/slider.js.map +1 -1
  45. package/dist/ui/surface.js +9 -9
  46. package/dist/ui/switch.d.ts.map +1 -1
  47. package/dist/ui/switch.js +1 -0
  48. package/dist/ui/switch.js.map +1 -1
  49. package/dist/ui/tabs.js +47 -47
  50. package/dist/ui/text.js +8 -8
  51. package/dist/ui/textarea.js +12 -12
  52. package/dist/ui/toggle.js +12 -12
  53. package/docs/recipes/install-astro.md +2 -2
  54. package/docs/recipes/install-next-app-router.md +2 -2
  55. package/docs/recipes/install-remix.md +2 -2
  56. package/docs/recipes/install-tanstack-start.md +2 -2
  57. package/docs/recipes/install-vite.md +2 -2
  58. package/docs/recipes/troubleshoot.md +26 -5
  59. package/llms.txt +1 -1
  60. package/mcp-manifest.json +24 -3
  61. package/package.json +39 -11
  62. package/skill/SKILL.md +1 -1
  63. package/skill/references/components.md +1 -1
  64. package/skill/references/setup-astro.md +2 -2
  65. package/skill/references/setup-next-app-router.md +2 -2
  66. package/skill/references/setup-remix.md +2 -2
  67. package/skill/references/setup-tanstack-start.md +2 -2
  68. package/skill/references/setup-vite.md +2 -2
  69. package/skill/references/troubleshoot.md +26 -5
  70. package/dist/_chunks/tiptap.js +0 -18809
  71. package/dist/_chunks/tiptap.js.map +0 -1
  72. package/dist/_chunks/vendor-utils.js +0 -1766
  73. package/dist/_chunks/vendor-utils.js.map +0 -1
  74. package/scripts/welcome.mjs +0 -312
package/BREAKING.json CHANGED
@@ -350,6 +350,19 @@
350
350
  "Fastest migration: `npx shadcn@latest add @devalok/sidebar-app`, then replace <AppSidebar navGroups={…} user={…} currentPath={…} /> with the pasted <SidebarApp/>, wiring your router Link + active path. Composing @devalok/shilp-sutra/ui/sidebar directly is equally valid and needs no registry.",
351
351
  "Deprecation cycle: no PUBLISHED version carried an @deprecated AppSidebar — 0.53.0 shipped it undeprecated, 0.54.0 removes it. The deprecation marker existed only within the unreleased 0.54.0 line. This is a deliberate call for a 0.x beta with a single coordinated consumer (breaking changes ship in 0.x minors); a post-1.0 removal would get a real deprecate→remove window."
352
352
  ]
353
+ },
354
+ "0.56.0": {
355
+ "summary": "TipTap is no longer bundled — @tiptap/* and prosemirror-* are externalized, so RichTextEditor / RichChatInput now REQUIRE their peers to be installed. No symbol, prop, or export changed; this is an install-graph break only.",
356
+ "migrationDoc": "MIGRATION.md#v0560--tiptap-externalized--no-install-scripts",
357
+ "notes": [
358
+ "BREAKING: @tiptap/* and prosemirror-* were declared as optional peerDependencies AND bundled into a 641 KB _chunks/tiptap.js. RichTextEditor / RichChatInput therefore worked whether or not you installed the peers. They are now externalized and the peers are mandatory for those two components. Install command per framework: §2a of docs/recipes/install-*.md, or run the MCP's preflight tool.",
359
+ "WHY: a consumer who DID follow our peer instructions ran two ProseMirror copies. Plugin keys are module-scoped, so the copies could not see each other's plugins and the editors misbehaved un-attributably. Same failure class as the framer-motion/sonner externalization in 0.37.0. It also removes 641 KB from the package for every consumer who never touches rich text.",
360
+ "Twelve @tiptap/* packages are now declared as optional peers (previously four: core, pm, react, suggestion) — the full set we import must be resolvable by the consumer. Two internal `prosemirror-state` imports were rerouted to `@tiptap/pm/state` so they ride the existing @tiptap/pm peer instead of requiring a separate prosemirror-state install.",
361
+ "NOT breaking: every component outside RichTextEditor / RichChatInput is untouched. No exported symbol, prop type, or subpath export changed in this release — no widening and no narrowing.",
362
+ "use-sync-external-store is no longer a dependency. React 18+ has useSyncExternalStore built in and our own code always called it directly (primitives/_internal/react-use-is-hydrated.ts); the dep existed only to feed the bundled TipTap chunk. Runtime dependencies are now class-variance-authority, clsx, tailwind-merge, tw-animate-css.",
363
+ "clsx / class-variance-authority / tailwind-merge are now externalized rather than bundled. All three leak into our published .d.ts (25 files reference VariantProps, cva's return type embeds class-variance-authority/types#ClassProp, utils.d.ts imports ClassValue), so consumers already had to resolve them for tsc; bundling a second copy shipped the same code twice and blocked dedupe with the consumer's own copies. tailwind-merge moves devDependency -> dependency (it was already shipping, hidden inside our bundle). All auto-installed with us — no consumer action.",
364
+ "The postinstall script is REMOVED — this package now runs no lifecycle scripts at all on install. It previously printed a welcome banner and wrote a project-scoped .mcp.json into the consumer's repo pointing at our hosted docs MCP. Nothing is required when upgrading. To clean up after the old behaviour: remove the `shilp-sutra` entry from .mcp.json and delete the node_modules/.shilp-sutra-welcomed and .shilp-sutra-mcp-written sentinels. Add the MCP deliberately with `claude mcp add --transport http shilp-sutra https://shilp-sutra.devalok.in/mcp`."
365
+ ]
353
366
  }
354
367
  }
355
368
  }
package/MIGRATION.md CHANGED
@@ -4,6 +4,62 @@ This page indexes all breaking changes across `@devalok/shilp-sutra` versions. F
4
4
 
5
5
  > **Upgrading from &lt; 0.36?** Start here, then read each intermediate version section. Breaking changes stack — skipping versions means stacking migrations.
6
6
 
7
+ ## v0.56.0 — TipTap externalized; no install scripts
8
+
9
+ One breaking change, and it only affects `RichTextEditor` / `RichChatInput`. No exported symbol, prop type, or subpath export changed — nothing widened and nothing narrowed.
10
+
11
+ ### If you import `RichTextEditor` or `RichChatInput`, install the TipTap peers
12
+
13
+ 0.55.x declared `@tiptap/*` as optional peers **and** bundled TipTap into a 641 KB chunk, so these components worked whether or not you installed anything. TipTap is now externalized: the peers are required.
14
+
15
+ ```bash
16
+ # RichTextEditor
17
+ pnpm add @tiptap/core @tiptap/extension-highlight @tiptap/extension-image \
18
+ @tiptap/extension-list @tiptap/extension-mention @tiptap/extension-text-align \
19
+ @tiptap/extensions @tiptap/markdown @tiptap/pm @tiptap/react \
20
+ @tiptap/starter-kit @tiptap/suggestion
21
+
22
+ # RichChatInput — the list above, plus:
23
+ pnpm add date-fns
24
+ ```
25
+
26
+ Note these are plain `pnpm add`, not `-D`. In 0.55.0 they were devDependencies for types only; now they are the runtime.
27
+
28
+ §2a of your framework's install recipe carries the same list, and the docs MCP's `preflight` derives it from your actual imports.
29
+
30
+ **Why the churn:** the old arrangement meant a consumer who *did* follow our peer instructions ran two ProseMirror copies. Plugin keys are module-scoped, so the copies could not recognise each other's plugins and the editors misbehaved in ways nobody could attribute to us. This is the same failure class as the framer-motion / sonner externalization in 0.37.0. It also removes 641 KB from the package for every consumer who never touches rich text.
31
+
32
+ ### If you don't use the editors — nothing to do
33
+
34
+ Every other component is untouched, and the package gets smaller.
35
+
36
+ ### `use-sync-external-store` is gone — nothing to do
37
+
38
+ It was never ours. React 18+ has `useSyncExternalStore` built in and our code always called it directly; the dependency existed only to feed the bundled TipTap chunk. Runtime dependencies are now `class-variance-authority`, `clsx`, `tailwind-merge`, `tw-animate-css`.
39
+
40
+ ### `clsx` / `class-variance-authority` / `tailwind-merge` externalized — nothing to do
41
+
42
+ All three leak into our published `.d.ts`, so you already had to resolve them for `tsc`. We were also bundling copies, which shipped the same code twice and stopped your own copies from deduping with ours. `tailwind-merge` moves from our devDependencies to our dependencies — it was already shipping, just hidden inside the bundle. All three install with us automatically.
43
+
44
+ ### No more `postinstall` — nothing to do
45
+
46
+ This package now runs **zero lifecycle scripts** on install. The old `postinstall` printed a welcome banner and wrote a project-scoped `.mcp.json` into your repo pointing at our hosted docs MCP.
47
+
48
+ To clean up after the old behaviour if you want to:
49
+
50
+ ```bash
51
+ # 1. drop the entry we added (or the whole file, if we created it and you keep nothing else there)
52
+ # .mcp.json → mcpServers → "shilp-sutra"
53
+ # 2. remove the sentinels
54
+ rm -f node_modules/.shilp-sutra-welcomed node_modules/.shilp-sutra-mcp-written
55
+ ```
56
+
57
+ To add the MCP deliberately:
58
+
59
+ ```bash
60
+ claude mcp add --transport http shilp-sutra https://shilp-sutra.devalok.in/mcp
61
+ ```
62
+
7
63
  ## v0.55.0 — additive; one optional step if you use the editors
8
64
 
9
65
  Nothing breaks. No existing import, prop value, or install stops working.
package/README.md CHANGED
@@ -53,8 +53,22 @@ claude mcp add --transport http shilp-sutra https://shilp-sutra.devalok.in/mcp
53
53
 
54
54
  Six read-only tools: `find_component`, `get_component`, `get_tokens`, `get_setup`, `upgrade`, `search_docs`. Every tool takes a `version` param — pass your installed version (from `node_modules/@devalok/shilp-sutra/package.json`) so answers match your prop surface exactly.
55
55
 
56
+ Prefer a local skill over a network call? Copy the one we ship:
57
+
58
+ ```sh
59
+ cp -r node_modules/@devalok/shilp-sutra/skill ~/.claude/skills/shilp-sutra
60
+ ```
61
+
56
62
  Without MCP: `llms.txt` (router) → `docs/components/<tier>/<name>.md` (per-component) → `mcp-manifest.json` (all props/tokens/composition as JSON). Full contract in `AGENTS.md`.
57
63
 
64
+ ### No install scripts
65
+
66
+ This package has **no `postinstall` and no lifecycle scripts of any kind**. Installing it runs zero code on your machine and writes nothing outside its own directory.
67
+
68
+ Up to 0.55.x we shipped a `postinstall` that printed a welcome banner and wrote a project-scoped `.mcp.json` pointing at the docs MCP above. Both are gone as of 0.56.0. The banner was noise; writing a config file into someone's repo — unasked, to wire their AI agent to our server — is exactly the behaviour npm's install-script blocking exists to stop, and it is not a thing a design system should do to earn a place in your dependency tree. Add the MCP yourself with the command above if you want it.
69
+
70
+ > **Upgrading from ≤0.55.x?** Nothing to do. If you want the old auto-written config gone, delete the `shilp-sutra` entry from your `.mcp.json` (or the file, if we created it and you keep nothing else there) and remove the `node_modules/.shilp-sutra-welcomed` / `.shilp-sutra-mcp-written` sentinels.
71
+
58
72
  ## Peer Dependencies
59
73
 
60
74
  ### Required
@@ -74,7 +88,8 @@ Install only what you use:
74
88
  |---------|----------|
75
89
  | Toasts (`./ui/toaster`, `./ui/toast`) | `sonner` |
76
90
  | Charts (`./ui/charts`) | `d3-array`, `d3-axis`, `d3-format`, `d3-interpolate`, `d3-scale`, `d3-selection`, `d3-shape`, `d3-time-format`, `d3-transition` |
77
- | Rich Text Editor (`./composed/rich-text-editor`) | `@tiptap/react`, `@tiptap/starter-kit` |
91
+ | Rich Text Editor (`./composed/rich-text-editor`) | `@tiptap/core`, `@tiptap/extension-highlight`, `@tiptap/extension-image`, `@tiptap/extension-list`, `@tiptap/extension-mention`, `@tiptap/extension-text-align`, `@tiptap/extensions`, `@tiptap/markdown`, `@tiptap/pm`, `@tiptap/react`, `@tiptap/starter-kit`, `@tiptap/suggestion` |
92
+ | Rich Chat Input (`./composed/rich-chat-input`) | the Rich Text Editor list, plus `date-fns` |
78
93
  | DataTable (`./ui/data-table`) | `@tanstack/react-table`, `@tanstack/react-virtual` |
79
94
  | Icons | `@tabler/icons-react` |
80
95
  | Date components (`./composed/date-picker`) | `date-fns` |
@@ -1,16 +1,16 @@
1
1
  "use client";
2
2
  import { Slot as e } from "./primitives-internal.js";
3
3
  import { durations as t, springs as n } from "../ui/lib/motion.js";
4
- import { cva as r } from "./vendor-utils.js";
5
- import { cn as i } from "../ui/lib/utils.js";
6
- import { Icon as a } from "../ui/icon.js";
7
- import { Dot as o } from "../ui/dot.js";
8
- import { normalizeIcon as s } from "./normalize-icon.js";
9
- import { BadgeIndicator as c } from "../ui/badge-indicator.js";
10
- import * as l from "react";
11
- import { jsx as u, jsxs as d } from "react/jsx-runtime";
12
- import { IconCheck as f, IconX as p } from "@tabler/icons-react";
13
- import { AnimatePresence as m, motion as h } from "framer-motion";
4
+ import { cn as r } from "../ui/lib/utils.js";
5
+ import { Icon as i } from "../ui/icon.js";
6
+ import { Dot as a } from "../ui/dot.js";
7
+ import { normalizeIcon as o } from "./normalize-icon.js";
8
+ import { BadgeIndicator as s } from "../ui/badge-indicator.js";
9
+ import * as c from "react";
10
+ import { jsx as l, jsxs as u } from "react/jsx-runtime";
11
+ import { IconCheck as d, IconX as f } from "@tabler/icons-react";
12
+ import { AnimatePresence as p, motion as m } from "framer-motion";
13
+ import { cva as h } from "class-variance-authority";
14
14
  //#region src/ui/badge.tsx
15
15
  var g = {
16
16
  default: {
@@ -116,13 +116,13 @@ function _(e, t) {
116
116
  if (t === "custom") return "";
117
117
  let n = g[t];
118
118
  switch (e) {
119
- case "subtle": return i(n.bg, n.fg, n.border);
120
- case "solid": return i(n.solid, n.solidFg, "border-transparent");
121
- case "outline": return i("bg-transparent", n.fg, n.border);
122
- case "soft": return i(n.bg, n.fg, "border-transparent");
119
+ case "subtle": return r(n.bg, n.fg, n.border);
120
+ case "solid": return r(n.solid, n.solidFg, "border-transparent");
121
+ case "outline": return r("bg-transparent", n.fg, n.border);
122
+ case "soft": return r(n.bg, n.fg, "border-transparent");
123
123
  }
124
124
  }
125
- var v = r("relative inline-flex items-center rounded-pill font-sans font-medium overflow-hidden isolate transition-[color,background-color,border-color,padding] duration-fast-02 ease-productive-standard select-none [&>span:not([data-grain])]:relative [&>span:not([data-grain])]:z-[2]", {
125
+ var v = h("relative inline-flex items-center rounded-pill font-sans font-medium overflow-hidden isolate transition-[color,background-color,border-color,padding] duration-fast-02 ease-productive-standard select-none [&>span:not([data-grain])]:relative [&>span:not([data-grain])]:z-[2]", {
126
126
  variants: {
127
127
  variant: {
128
128
  subtle: "border",
@@ -156,8 +156,8 @@ var v = r("relative inline-flex items-center rounded-pill font-sans font-medium
156
156
  sm: "pr-ds-02",
157
157
  md: "pr-ds-02",
158
158
  lg: "pr-ds-02b"
159
- }, S = l.forwardRef(({ className: r, variant: c, color: l, size: g, asChild: S, startIcon: C, endIcon: w, dot: T, dotPulse: E, onDismiss: D, onClick: O, selected: k, disabled: A, maxWidth: j, truncate: M, circle: N, children: P, style: F, ...I }, L) => {
160
- let R = c ?? "subtle", z = l ?? "default", B = g ?? "md", V = S ? e : O && D ? "div" : O ? "button" : "span", H = z === "custom" ? {
159
+ }, S = c.forwardRef(({ className: s, variant: c, color: h, size: g, asChild: S, startIcon: C, endIcon: w, dot: T, dotPulse: E, onDismiss: D, onClick: O, selected: k, disabled: A, maxWidth: j, truncate: M, circle: N, children: P, style: F, ...I }, L) => {
160
+ let R = c ?? "subtle", z = h ?? "default", B = g ?? "md", V = S ? e : O && D ? "div" : O ? "button" : "span", H = z === "custom" ? {
161
161
  ...R === "subtle" && {
162
162
  backgroundColor: "color-mix(in oklch, var(--badge-color) 15%, transparent)",
163
163
  color: "var(--badge-color)",
@@ -181,12 +181,12 @@ var v = r("relative inline-flex items-center rounded-pill font-sans font-medium
181
181
  } : void 0, U = O && D ? (e) => {
182
182
  (e.key === "Enter" || e.key === " ") && (e.preventDefault(), A || O(e)), I.onKeyDown?.(e);
183
183
  } : I.onKeyDown, W = !!(T || C || O && k && !C && !T), G = !!(D || w);
184
- return /* @__PURE__ */ d(V, {
184
+ return /* @__PURE__ */ u(V, {
185
185
  ref: L,
186
- className: i(v({
186
+ className: r(v({
187
187
  variant: R,
188
188
  size: B
189
- }), _(R, z), W && b[B], G && x[B], O && "cursor-pointer hover:brightness-[0.97] focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-1 transition-[color,background-color,border-color,transform,filter] duration-moderate-01 ease-productive-exit hover:duration-fast-02 hover:ease-productive-entrance active:scale-[0.95] active:brightness-[0.92] active:duration-[0ms]", k && "ring-1 ring-current/20 transition-shadow duration-fast-02", A && "opacity-action-disabled pointer-events-none saturate-[0.3]", N && "justify-center px-0 aspect-square", r),
189
+ }), _(R, z), W && b[B], G && x[B], O && "cursor-pointer hover:brightness-[0.97] focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-accent-9 focus-visible:ring-offset-1 transition-[color,background-color,border-color,transform,filter] duration-moderate-01 ease-productive-exit hover:duration-fast-02 hover:ease-productive-entrance active:scale-[0.95] active:brightness-[0.92] active:duration-[0ms]", k && "ring-1 ring-current/20 transition-shadow duration-fast-02", A && "opacity-action-disabled pointer-events-none saturate-[0.3]", N && "justify-center px-0 aspect-square", s),
190
190
  style: {
191
191
  ...F,
192
192
  ...H,
@@ -202,19 +202,19 @@ var v = r("relative inline-flex items-center rounded-pill font-sans font-medium
202
202
  } : {},
203
203
  ...I,
204
204
  children: [
205
- /* @__PURE__ */ u(m, { children: T && /* @__PURE__ */ u(h.span, {
205
+ /* @__PURE__ */ l(p, { children: T && /* @__PURE__ */ l(m.span, {
206
206
  initial: { scale: 0 },
207
207
  animate: { scale: 1 },
208
208
  exit: { scale: 0 },
209
209
  transition: n.snappy,
210
210
  className: "inline-flex shrink-0",
211
- children: /* @__PURE__ */ u(o, {
211
+ children: /* @__PURE__ */ l(a, {
212
212
  color: "current",
213
213
  size: "sm",
214
214
  pulse: E
215
215
  })
216
216
  }, "dot") }),
217
- O && !C && !T && /* @__PURE__ */ u(h.span, {
217
+ O && !C && !T && /* @__PURE__ */ l(m.span, {
218
218
  initial: !1,
219
219
  animate: k ? {
220
220
  opacity: 1,
@@ -239,33 +239,33 @@ var v = r("relative inline-flex items-center rounded-pill font-sans font-medium
239
239
  },
240
240
  className: "inline-flex shrink-0 overflow-hidden",
241
241
  "aria-hidden": !k,
242
- children: /* @__PURE__ */ u(a, {
243
- icon: f,
242
+ children: /* @__PURE__ */ l(i, {
243
+ icon: d,
244
244
  size: "xs"
245
245
  })
246
246
  }),
247
- C && /* @__PURE__ */ u("span", {
248
- className: i("shrink-0", y[B]),
249
- children: s(C)
247
+ C && /* @__PURE__ */ l("span", {
248
+ className: r("shrink-0", y[B]),
249
+ children: o(C)
250
250
  }),
251
- M || j ? /* @__PURE__ */ u("span", {
251
+ M || j ? /* @__PURE__ */ l("span", {
252
252
  className: "truncate",
253
253
  title: typeof P == "string" ? P : void 0,
254
254
  children: P
255
255
  }) : P,
256
- w && /* @__PURE__ */ u("span", {
257
- className: i("shrink-0", y[B]),
258
- children: s(w)
256
+ w && /* @__PURE__ */ l("span", {
257
+ className: r("shrink-0", y[B]),
258
+ children: o(w)
259
259
  }),
260
- D && /* @__PURE__ */ u("button", {
260
+ D && /* @__PURE__ */ l("button", {
261
261
  type: "button",
262
262
  onClick: (e) => {
263
263
  e.stopPropagation(), D();
264
264
  },
265
- className: i("shrink-0 rounded-pill text-current/60 hover:text-current hover:bg-current/10 transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-accent-9", B === "xs" ? "p-0 -mr-0.5 min-w-ds-05 min-h-ds-05" : "p-px"),
265
+ className: r("shrink-0 rounded-pill text-current/60 hover:text-current hover:bg-current/10 transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-accent-9", B === "xs" ? "p-0 -mr-0.5 min-w-ds-05 min-h-ds-05" : "p-px"),
266
266
  "aria-label": `Remove ${typeof P == "string" ? P : ""}`.trim() || "Remove",
267
- children: /* @__PURE__ */ u(a, {
268
- icon: p,
267
+ children: /* @__PURE__ */ l(i, {
268
+ icon: f,
269
269
  size: "xs"
270
270
  })
271
271
  })
@@ -274,24 +274,24 @@ var v = r("relative inline-flex items-center rounded-pill font-sans font-medium
274
274
  });
275
275
  S.displayName = "Badge";
276
276
  var C = Object.assign(S, {
277
- Indicator: c,
277
+ Indicator: s,
278
278
  Group: T
279
279
  }), w = {
280
280
  tight: "gap-1",
281
281
  default: "gap-1.5",
282
282
  loose: "gap-2"
283
- }, T = l.forwardRef(function({ max: e, gap: t = "default", size: n, onOverflowClick: r, className: a, children: o, ...s }, c) {
284
- let u = l.Children.toArray(o), f = u.length, p = e !== void 0 && f > e, m = p ? u.slice(0, e) : u, h = p ? f - e : 0;
285
- return /* @__PURE__ */ d("div", {
286
- ref: c,
287
- className: i("flex flex-wrap items-center", w[t], a),
283
+ }, T = c.forwardRef(function({ max: e, gap: t = "default", size: n, onOverflowClick: i, className: a, children: o, ...s }, l) {
284
+ let d = c.Children.toArray(o), f = d.length, p = e !== void 0 && f > e, m = p ? d.slice(0, e) : d, h = p ? f - e : 0;
285
+ return /* @__PURE__ */ u("div", {
286
+ ref: l,
287
+ className: r("flex flex-wrap items-center", w[t], a),
288
288
  ...s,
289
- children: [m, p && /* @__PURE__ */ d(C, {
289
+ children: [m, p && /* @__PURE__ */ u(C, {
290
290
  variant: "outline",
291
291
  color: "neutral",
292
292
  size: n ?? "sm",
293
- onClick: r,
294
- "aria-label": r ? `Show ${h} more` : void 0,
293
+ onClick: i,
294
+ "aria-label": i ? `Show ${h} more` : void 0,
295
295
  children: ["+", h]
296
296
  })]
297
297
  });
@@ -1,8 +1,10 @@
1
1
  "use client";
2
2
  import { cn as e } from "../ui/lib/utils.js";
3
- import { Extension as t, PluginKey as n, index_default$3 as r } from "./tiptap.js";
4
- import * as i from "react";
5
- import { jsx as a, jsxs as o } from "react/jsx-runtime";
3
+ import * as t from "react";
4
+ import { jsx as n, jsxs as r } from "react/jsx-runtime";
5
+ import { Extension as i } from "@tiptap/core";
6
+ import a from "@tiptap/suggestion";
7
+ import { PluginKey as o } from "@tiptap/pm/state";
6
8
  import { createRoot as s } from "react-dom/client";
7
9
  //#region src/composed/extensions/emoji-data.ts
8
10
  var c = null;
@@ -31,27 +33,27 @@ function d(e, t, n = 8) {
31
33
  //#endregion
32
34
  //#region src/composed/extensions/emoji-suggestion.tsx
33
35
  function f({ emoji: e, size: t = "1.2em" }) {
34
- return /* @__PURE__ */ a("span", {
36
+ return /* @__PURE__ */ n("span", {
35
37
  style: { fontSize: t },
36
38
  children: e.native
37
39
  });
38
40
  }
39
- var p = i.forwardRef(({ items: t, command: n }, r) => {
40
- let [s, c] = i.useState(0);
41
- return i.useEffect(() => c(0), [t]), i.useImperativeHandle(r, () => ({ onKeyDown: ({ event: e }) => e.key === "ArrowUp" ? (c((e) => (e + t.length - 1) % t.length), !0) : e.key === "ArrowDown" ? (c((e) => (e + 1) % t.length), !0) : e.key === "Enter" ? (t[s] && n(t[s]), !0) : !1 })), t.length ? /* @__PURE__ */ a("div", {
41
+ var p = t.forwardRef(({ items: i, command: a }, o) => {
42
+ let [s, c] = t.useState(0);
43
+ return t.useEffect(() => c(0), [i]), t.useImperativeHandle(o, () => ({ onKeyDown: ({ event: e }) => e.key === "ArrowUp" ? (c((e) => (e + i.length - 1) % i.length), !0) : e.key === "ArrowDown" ? (c((e) => (e + 1) % i.length), !0) : e.key === "Enter" ? (i[s] && a(i[s]), !0) : !1 })), i.length ? /* @__PURE__ */ n("div", {
42
44
  role: "listbox",
43
45
  "aria-label": "Emoji suggestions",
44
46
  className: "z-popover max-h-[200px] overflow-x-hidden overflow-y-auto rounded-control bg-surface-overlay shadow-raised-hover",
45
- children: t.map((t, r) => /* @__PURE__ */ o("button", {
47
+ children: i.map((t, i) => /* @__PURE__ */ r("button", {
46
48
  type: "button",
47
49
  role: "option",
48
- "aria-selected": r === s,
49
- onClick: () => n(t),
50
- className: e("flex w-full items-center gap-ds-03 px-ds-04 py-ds-02b text-left text-body-sm", r === s ? "bg-surface-raised text-surface-fg" : "text-surface-fg-muted hover:bg-surface-raised"),
51
- children: [/* @__PURE__ */ a(f, {
50
+ "aria-selected": i === s,
51
+ onClick: () => a(t),
52
+ className: e("flex w-full items-center gap-ds-03 px-ds-04 py-ds-02b text-left text-body-sm", i === s ? "bg-surface-raised text-surface-fg" : "text-surface-fg-muted hover:bg-surface-raised"),
53
+ children: [/* @__PURE__ */ n(f, {
52
54
  emoji: t,
53
55
  size: "1.25em"
54
- }), /* @__PURE__ */ o("span", {
56
+ }), /* @__PURE__ */ r("span", {
55
57
  className: "truncate",
56
58
  children: [
57
59
  ":",
@@ -65,31 +67,31 @@ var p = i.forwardRef(({ items: t, command: n }, r) => {
65
67
  p.displayName = "EmojiList";
66
68
  function m() {
67
69
  return () => {
68
- let e = null, t = null, n = null;
70
+ let e = null, t = null, r = null;
69
71
  return {
70
- onStart: (r) => {
72
+ onStart: (i) => {
71
73
  t = document.createElement("div"), t.style.position = "absolute", t.style.zIndex = "1400";
72
- let i = r.clientRect?.();
73
- i && (t.style.left = `${i.left}px`, t.style.top = `${i.bottom + 4}px`), document.body.appendChild(t), e = s(t), e.render(/* @__PURE__ */ a(p, {
74
+ let a = i.clientRect?.();
75
+ a && (t.style.left = `${a.left}px`, t.style.top = `${a.bottom + 4}px`), document.body.appendChild(t), e = s(t), e.render(/* @__PURE__ */ n(p, {
74
76
  ref: (e) => {
75
- n = e;
77
+ r = e;
76
78
  },
77
- items: r.items,
78
- command: (e) => r.command(e)
79
+ items: i.items,
80
+ command: (e) => i.command(e)
79
81
  }));
80
82
  },
81
- onUpdate: (r) => {
83
+ onUpdate: (i) => {
82
84
  if (!e || !t) return;
83
- let i = r.clientRect?.();
84
- i && (t.style.left = `${i.left}px`, t.style.top = `${i.bottom + 4}px`), e.render(/* @__PURE__ */ a(p, {
85
+ let a = i.clientRect?.();
86
+ a && (t.style.left = `${a.left}px`, t.style.top = `${a.bottom + 4}px`), e.render(/* @__PURE__ */ n(p, {
85
87
  ref: (e) => {
86
- n = e;
88
+ r = e;
87
89
  },
88
- items: r.items,
89
- command: (e) => r.command(e)
90
+ items: i.items,
91
+ command: (e) => i.command(e)
90
92
  }));
91
93
  },
92
- onKeyDown: (r) => r.event.key === "Escape" ? (t && (e?.unmount(), t.remove(), t = null, e = null), !0) : n?.onKeyDown(r) ?? !1,
94
+ onKeyDown: (n) => n.event.key === "Escape" ? (t && (e?.unmount(), t.remove(), t = null, e = null), !0) : r?.onKeyDown(n) ?? !1,
93
95
  onExit: () => {
94
96
  t && (e?.unmount(), t.remove(), t = null, e = null);
95
97
  }
@@ -97,11 +99,11 @@ function m() {
97
99
  };
98
100
  }
99
101
  function h() {
100
- return t.create({
102
+ return i.create({
101
103
  name: "emojiSuggestion",
102
104
  addProseMirrorPlugins() {
103
- return [r({
104
- pluginKey: new n("emojiSuggestion"),
105
+ return [a({
106
+ pluginKey: new o("emojiSuggestion"),
105
107
  editor: this.editor,
106
108
  char: ":",
107
109
  items: async ({ query: e }) => d(await l(), e, 8),
@@ -1 +1 @@
1
- {"version":3,"file":"emoji-suggestion.js","names":[],"sources":["../../src/composed/extensions/emoji-data.ts","../../src/composed/extensions/emoji-suggestion.tsx"],"sourcesContent":["// Emoji dataset for the editors' `:shortcode:` autocomplete. Sources\n// `@emoji-mart/data` — a pure-JSON package with NO react peer, so it is\n// unaffected by the React-19 peer issue that drove the picker off\n// `@emoji-mart/react` (→ frimousse). Native-only since that migration: no\n// spritesheet coordinates, no per-art-style loaders.\n\ninterface EmojiSkin {\n native: string\n}\n\ninterface EmojiEntry {\n id: string\n name: string\n keywords?: string[]\n skins: EmojiSkin[]\n}\n\nexport interface EmojiDataset {\n emojis: Record<string, EmojiEntry>\n}\n\nexport interface ResolvedEmoji {\n id: string\n name: string\n native: string\n}\n\nlet cachedData: EmojiDataset | null = null\n\nexport async function loadEmojiData(): Promise<EmojiDataset> {\n if (cachedData) return cachedData\n const mod = await import('@emoji-mart/data')\n cachedData = ((mod as { default?: EmojiDataset }).default ?? mod) as EmojiDataset\n return cachedData\n}\n\nfunction resolve(entry: EmojiEntry): ResolvedEmoji {\n return { id: entry.id, name: entry.name, native: entry.skins[0]?.native ?? '' }\n}\n\nexport function searchEmoji(data: EmojiDataset, query: string, limit = 8): ResolvedEmoji[] {\n const entries = Object.values(data.emojis)\n if (!query) return entries.slice(0, limit).map(resolve)\n const q = query.toLowerCase()\n const results: ResolvedEmoji[] = []\n for (const e of entries) {\n if (results.length >= limit) break\n if (\n e.id.includes(q) ||\n e.name.toLowerCase().includes(q) ||\n e.keywords?.some((k) => k.includes(q))\n ) {\n results.push(resolve(e))\n }\n }\n return results\n}\n","import { Extension } from '@tiptap/core'\nimport Suggestion, { type SuggestionKeyDownProps,type SuggestionProps } from '@tiptap/suggestion'\nimport { PluginKey } from 'prosemirror-state'\nimport * as React from 'react'\nimport { createRoot, type Root } from 'react-dom/client'\n\nimport { cn } from '../../ui/lib/utils'\nimport { loadEmojiData, type ResolvedEmoji, searchEmoji } from './emoji-data'\n\n// Re-export for external consumers\nexport type { ResolvedEmoji as EmojiSuggestionItem }\n\n// Native-only since the frimousse migration.\nfunction EmojiImage({ emoji, size = '1.2em' }: { emoji: ResolvedEmoji; size?: string }) {\n return <span style={{ fontSize: size }}>{emoji.native}</span>\n}\n\ninterface EmojiListRef {\n onKeyDown: (props: SuggestionKeyDownProps) => boolean\n}\n\ninterface EmojiListProps {\n items: ResolvedEmoji[]\n command: (item: ResolvedEmoji) => void\n}\n\nconst EmojiList = React.forwardRef<EmojiListRef, EmojiListProps>(\n ({ items, command }, ref) => {\n const [selectedIndex, setSelectedIndex] = React.useState(0)\n\n React.useEffect(() => setSelectedIndex(0), [items])\n\n React.useImperativeHandle(ref, () => ({\n onKeyDown: ({ event }: SuggestionKeyDownProps) => {\n if (event.key === 'ArrowUp') {\n setSelectedIndex((i) => (i + items.length - 1) % items.length)\n return true\n }\n if (event.key === 'ArrowDown') {\n setSelectedIndex((i) => (i + 1) % items.length)\n return true\n }\n if (event.key === 'Enter') {\n if (items[selectedIndex]) command(items[selectedIndex])\n return true\n }\n return false\n },\n }))\n\n if (!items.length) return null\n\n return (\n <div role=\"listbox\" aria-label=\"Emoji suggestions\" className=\"z-popover max-h-[200px] overflow-x-hidden overflow-y-auto rounded-control bg-surface-overlay shadow-raised-hover\">\n {items.map((item, index) => (\n <button\n key={item.id}\n type=\"button\"\n role=\"option\"\n aria-selected={index === selectedIndex}\n onClick={() => command(item)}\n className={cn(\n 'flex w-full items-center gap-ds-03 px-ds-04 py-ds-02b text-left text-body-sm',\n index === selectedIndex ? 'bg-surface-raised text-surface-fg' : 'text-surface-fg-muted hover:bg-surface-raised',\n )}\n >\n <EmojiImage emoji={item} size=\"1.25em\" />\n <span className=\"truncate\">:{item.id}:</span>\n </button>\n ))}\n </div>\n )\n },\n)\nEmojiList.displayName = 'EmojiList'\n\nfunction createEmojiSuggestionRenderer() {\n return () => {\n let root: Root | null = null\n let container: HTMLDivElement | null = null\n let componentRef: EmojiListRef | null = null\n\n return {\n onStart: (props: SuggestionProps<ResolvedEmoji>) => {\n container = document.createElement('div')\n container.style.position = 'absolute'\n container.style.zIndex = '1400'\n const rect = props.clientRect?.()\n if (rect) {\n container.style.left = `${rect.left}px`\n container.style.top = `${rect.bottom + 4}px`\n }\n document.body.appendChild(container)\n root = createRoot(container)\n root.render(\n <EmojiList\n ref={(r) => { componentRef = r }}\n items={props.items as ResolvedEmoji[]}\n command={(item) => props.command(item)}\n />,\n )\n },\n\n onUpdate: (props: SuggestionProps<ResolvedEmoji>) => {\n if (!root || !container) return\n const rect = props.clientRect?.()\n if (rect) {\n container.style.left = `${rect.left}px`\n container.style.top = `${rect.bottom + 4}px`\n }\n root.render(\n <EmojiList\n ref={(r) => { componentRef = r }}\n items={props.items as ResolvedEmoji[]}\n command={(item) => props.command(item)}\n />,\n )\n },\n\n onKeyDown: (props: SuggestionKeyDownProps) => {\n if (props.event.key === 'Escape') {\n if (container) {\n root?.unmount()\n container.remove()\n container = null\n root = null\n }\n return true\n }\n return componentRef?.onKeyDown(props) ?? false\n },\n\n onExit: () => {\n if (container) {\n root?.unmount()\n container.remove()\n container = null\n root = null\n }\n },\n }\n }\n}\n\n/** Factory: returns the configured `:shortcode:` emoji suggestion extension. */\nexport function createEmojiSuggestion() {\n return Extension.create({\n name: 'emojiSuggestion',\n\n addProseMirrorPlugins() {\n return [\n Suggestion({\n pluginKey: new PluginKey('emojiSuggestion'),\n editor: this.editor,\n char: ':',\n items: async ({ query }) => {\n const data = await loadEmojiData()\n return searchEmoji(data, query, 8)\n },\n command: ({ editor, range, props: item }) => {\n const emoji = item as ResolvedEmoji\n editor\n .chain()\n .focus()\n .deleteRange(range)\n .insertContent({\n type: 'emojiNode',\n attrs: { id: emoji.id, native: emoji.native },\n })\n .run()\n },\n render: createEmojiSuggestionRenderer(),\n }),\n ]\n },\n })\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"emoji-suggestion.js","names":[],"sources":["../../src/composed/extensions/emoji-data.ts","../../src/composed/extensions/emoji-suggestion.tsx"],"sourcesContent":["// Emoji dataset for the editors' `:shortcode:` autocomplete. Sources\n// `@emoji-mart/data` — a pure-JSON package with NO react peer, so it is\n// unaffected by the React-19 peer issue that drove the picker off\n// `@emoji-mart/react` (→ frimousse). Native-only since that migration: no\n// spritesheet coordinates, no per-art-style loaders.\n\ninterface EmojiSkin {\n native: string\n}\n\ninterface EmojiEntry {\n id: string\n name: string\n keywords?: string[]\n skins: EmojiSkin[]\n}\n\nexport interface EmojiDataset {\n emojis: Record<string, EmojiEntry>\n}\n\nexport interface ResolvedEmoji {\n id: string\n name: string\n native: string\n}\n\nlet cachedData: EmojiDataset | null = null\n\nexport async function loadEmojiData(): Promise<EmojiDataset> {\n if (cachedData) return cachedData\n const mod = await import('@emoji-mart/data')\n cachedData = ((mod as { default?: EmojiDataset }).default ?? mod) as EmojiDataset\n return cachedData\n}\n\nfunction resolve(entry: EmojiEntry): ResolvedEmoji {\n return { id: entry.id, name: entry.name, native: entry.skins[0]?.native ?? '' }\n}\n\nexport function searchEmoji(data: EmojiDataset, query: string, limit = 8): ResolvedEmoji[] {\n const entries = Object.values(data.emojis)\n if (!query) return entries.slice(0, limit).map(resolve)\n const q = query.toLowerCase()\n const results: ResolvedEmoji[] = []\n for (const e of entries) {\n if (results.length >= limit) break\n if (\n e.id.includes(q) ||\n e.name.toLowerCase().includes(q) ||\n e.keywords?.some((k) => k.includes(q))\n ) {\n results.push(resolve(e))\n }\n }\n return results\n}\n","import { Extension } from '@tiptap/core'\nimport Suggestion, { type SuggestionKeyDownProps,type SuggestionProps } from '@tiptap/suggestion'\nimport { PluginKey } from '@tiptap/pm/state'\nimport * as React from 'react'\nimport { createRoot, type Root } from 'react-dom/client'\n\nimport { cn } from '../../ui/lib/utils'\nimport { loadEmojiData, type ResolvedEmoji, searchEmoji } from './emoji-data'\n\n// Re-export for external consumers\nexport type { ResolvedEmoji as EmojiSuggestionItem }\n\n// Native-only since the frimousse migration.\nfunction EmojiImage({ emoji, size = '1.2em' }: { emoji: ResolvedEmoji; size?: string }) {\n return <span style={{ fontSize: size }}>{emoji.native}</span>\n}\n\ninterface EmojiListRef {\n onKeyDown: (props: SuggestionKeyDownProps) => boolean\n}\n\ninterface EmojiListProps {\n items: ResolvedEmoji[]\n command: (item: ResolvedEmoji) => void\n}\n\nconst EmojiList = React.forwardRef<EmojiListRef, EmojiListProps>(\n ({ items, command }, ref) => {\n const [selectedIndex, setSelectedIndex] = React.useState(0)\n\n React.useEffect(() => setSelectedIndex(0), [items])\n\n React.useImperativeHandle(ref, () => ({\n onKeyDown: ({ event }: SuggestionKeyDownProps) => {\n if (event.key === 'ArrowUp') {\n setSelectedIndex((i) => (i + items.length - 1) % items.length)\n return true\n }\n if (event.key === 'ArrowDown') {\n setSelectedIndex((i) => (i + 1) % items.length)\n return true\n }\n if (event.key === 'Enter') {\n if (items[selectedIndex]) command(items[selectedIndex])\n return true\n }\n return false\n },\n }))\n\n if (!items.length) return null\n\n return (\n <div role=\"listbox\" aria-label=\"Emoji suggestions\" className=\"z-popover max-h-[200px] overflow-x-hidden overflow-y-auto rounded-control bg-surface-overlay shadow-raised-hover\">\n {items.map((item, index) => (\n <button\n key={item.id}\n type=\"button\"\n role=\"option\"\n aria-selected={index === selectedIndex}\n onClick={() => command(item)}\n className={cn(\n 'flex w-full items-center gap-ds-03 px-ds-04 py-ds-02b text-left text-body-sm',\n index === selectedIndex ? 'bg-surface-raised text-surface-fg' : 'text-surface-fg-muted hover:bg-surface-raised',\n )}\n >\n <EmojiImage emoji={item} size=\"1.25em\" />\n <span className=\"truncate\">:{item.id}:</span>\n </button>\n ))}\n </div>\n )\n },\n)\nEmojiList.displayName = 'EmojiList'\n\nfunction createEmojiSuggestionRenderer() {\n return () => {\n let root: Root | null = null\n let container: HTMLDivElement | null = null\n let componentRef: EmojiListRef | null = null\n\n return {\n onStart: (props: SuggestionProps<ResolvedEmoji>) => {\n container = document.createElement('div')\n container.style.position = 'absolute'\n container.style.zIndex = '1400'\n const rect = props.clientRect?.()\n if (rect) {\n container.style.left = `${rect.left}px`\n container.style.top = `${rect.bottom + 4}px`\n }\n document.body.appendChild(container)\n root = createRoot(container)\n root.render(\n <EmojiList\n ref={(r) => { componentRef = r }}\n items={props.items as ResolvedEmoji[]}\n command={(item) => props.command(item)}\n />,\n )\n },\n\n onUpdate: (props: SuggestionProps<ResolvedEmoji>) => {\n if (!root || !container) return\n const rect = props.clientRect?.()\n if (rect) {\n container.style.left = `${rect.left}px`\n container.style.top = `${rect.bottom + 4}px`\n }\n root.render(\n <EmojiList\n ref={(r) => { componentRef = r }}\n items={props.items as ResolvedEmoji[]}\n command={(item) => props.command(item)}\n />,\n )\n },\n\n onKeyDown: (props: SuggestionKeyDownProps) => {\n if (props.event.key === 'Escape') {\n if (container) {\n root?.unmount()\n container.remove()\n container = null\n root = null\n }\n return true\n }\n return componentRef?.onKeyDown(props) ?? false\n },\n\n onExit: () => {\n if (container) {\n root?.unmount()\n container.remove()\n container = null\n root = null\n }\n },\n }\n }\n}\n\n/** Factory: returns the configured `:shortcode:` emoji suggestion extension. */\nexport function createEmojiSuggestion() {\n return Extension.create({\n name: 'emojiSuggestion',\n\n addProseMirrorPlugins() {\n return [\n Suggestion({\n pluginKey: new PluginKey('emojiSuggestion'),\n editor: this.editor,\n char: ':',\n items: async ({ query }) => {\n const data = await loadEmojiData()\n return searchEmoji(data, query, 8)\n },\n command: ({ editor, range, props: item }) => {\n const emoji = item as ResolvedEmoji\n editor\n .chain()\n .focus()\n .deleteRange(range)\n .insertContent({\n type: 'emojiNode',\n attrs: { id: emoji.id, native: emoji.native },\n })\n .run()\n },\n render: createEmojiSuggestionRenderer(),\n }),\n ]\n },\n })\n}\n"],"mappings":"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"}
@@ -2,36 +2,37 @@
2
2
  import { cn as e } from "../ui/lib/utils.js";
3
3
  import { Icon as t } from "../ui/icon.js";
4
4
  import { TruncatedText as n } from "../ui/truncated-text.js";
5
- import { Node3 as r, NodeViewWrapper as i, ReactNodeViewRenderer as a, mergeAttributes as o } from "./tiptap.js";
6
- import * as s from "react";
7
- import { jsx as c, jsxs as l } from "react/jsx-runtime";
8
- import { IconDownload as u, IconFile as d } from "@tabler/icons-react";
5
+ import * as r from "react";
6
+ import { jsx as i, jsxs as a } from "react/jsx-runtime";
7
+ import { IconDownload as o, IconFile as s } from "@tabler/icons-react";
8
+ import { NodeViewWrapper as c, ReactNodeViewRenderer as l } from "@tiptap/react";
9
+ import { Node as u, mergeAttributes as d } from "@tiptap/core";
9
10
  import { createRoot as f } from "react-dom/client";
10
11
  //#region src/composed/extensions/file-attachment.tsx
11
12
  function p(e) {
12
13
  return e < 1024 ? `${e} B` : e < 1024 * 1024 ? `${(e / 1024).toFixed(1)} KB` : `${(e / (1024 * 1024)).toFixed(1)} MB`;
13
14
  }
14
15
  function m({ node: e }) {
15
- return /* @__PURE__ */ c(i, {
16
+ return /* @__PURE__ */ i(c, {
16
17
  className: "my-ds-02",
17
- children: /* @__PURE__ */ l("a", {
18
+ children: /* @__PURE__ */ a("a", {
18
19
  href: /^https?:\/\//.test(e.attrs.url) ? e.attrs.url : "#",
19
20
  target: "_blank",
20
21
  rel: "noopener noreferrer",
21
22
  className: "inline-flex items-center gap-ds-03 rounded-control border border-surface-border-strong bg-surface-raised px-ds-04 py-ds-03 text-body-sm text-surface-fg transition-colors hover:border-surface-border-strong hover:bg-surface-raised-hover",
22
23
  contentEditable: !1,
23
24
  children: [
24
- /* @__PURE__ */ c(t, {
25
- icon: d,
25
+ /* @__PURE__ */ i(t, {
26
+ icon: s,
26
27
  size: "sm",
27
28
  className: "shrink-0 text-surface-fg-subtle"
28
29
  }),
29
- /* @__PURE__ */ c(n, {
30
+ /* @__PURE__ */ i(n, {
30
31
  mode: "middle",
31
32
  className: "min-w-0",
32
33
  children: e.attrs.name
33
34
  }),
34
- /* @__PURE__ */ l("span", {
35
+ /* @__PURE__ */ a("span", {
35
36
  className: "shrink-0 text-surface-fg-subtle",
36
37
  children: [
37
38
  "(",
@@ -39,8 +40,8 @@ function m({ node: e }) {
39
40
  ")"
40
41
  ]
41
42
  }),
42
- /* @__PURE__ */ c(t, {
43
- icon: u,
43
+ /* @__PURE__ */ i(t, {
44
+ icon: o,
44
45
  size: "sm",
45
46
  className: "shrink-0 text-surface-fg-subtle"
46
47
  })
@@ -48,7 +49,7 @@ function m({ node: e }) {
48
49
  })
49
50
  });
50
51
  }
51
- var h = r.create({
52
+ var h = u.create({
52
53
  name: "fileAttachment",
53
54
  group: "block",
54
55
  atom: !0,
@@ -63,31 +64,31 @@ var h = r.create({
63
64
  return [{ tag: "div[data-file-attachment]" }];
64
65
  },
65
66
  renderHTML({ HTMLAttributes: e }) {
66
- return ["div", o({ "data-file-attachment": "" }, e)];
67
+ return ["div", d({ "data-file-attachment": "" }, e)];
67
68
  },
68
69
  addNodeView() {
69
- return a(m);
70
+ return l(m);
70
71
  }
71
- }), g = s.forwardRef(({ items: t, command: n }, r) => {
72
- let [i, a] = s.useState(0);
73
- return s.useEffect(() => a(0), [t]), s.useImperativeHandle(r, () => ({ onKeyDown: ({ event: e }) => e.key === "ArrowUp" ? (a((e) => (e + t.length - 1) % t.length), !0) : e.key === "ArrowDown" ? (a((e) => (e + 1) % t.length), !0) : e.key === "Enter" ? (t[i] && n(t[i]), !0) : !1 })), t.length ? /* @__PURE__ */ c("div", {
72
+ }), g = r.forwardRef(({ items: t, command: n }, o) => {
73
+ let [s, c] = r.useState(0);
74
+ return r.useEffect(() => c(0), [t]), r.useImperativeHandle(o, () => ({ onKeyDown: ({ event: e }) => e.key === "ArrowUp" ? (c((e) => (e + t.length - 1) % t.length), !0) : e.key === "ArrowDown" ? (c((e) => (e + 1) % t.length), !0) : e.key === "Enter" ? (t[s] && n(t[s]), !0) : !1 })), t.length ? /* @__PURE__ */ i("div", {
74
75
  role: "listbox",
75
76
  "aria-label": "Mention suggestions",
76
77
  className: "z-popover overflow-hidden rounded-control bg-surface-overlay shadow-raised-hover",
77
- children: t.map((t, r) => /* @__PURE__ */ l("button", {
78
+ children: t.map((t, r) => /* @__PURE__ */ a("button", {
78
79
  type: "button",
79
80
  role: "option",
80
- "aria-selected": r === i,
81
+ "aria-selected": r === s,
81
82
  onClick: () => n(t),
82
- className: e("flex w-full items-center gap-ds-03 px-ds-04 py-ds-02b text-left text-body-sm", r === i ? "bg-surface-raised text-surface-fg" : "text-surface-fg-muted hover:bg-surface-raised"),
83
- children: [t.avatar ? /* @__PURE__ */ c("img", {
83
+ className: e("flex w-full items-center gap-ds-03 px-ds-04 py-ds-02b text-left text-body-sm", r === s ? "bg-surface-raised text-surface-fg" : "text-surface-fg-muted hover:bg-surface-raised"),
84
+ children: [t.avatar ? /* @__PURE__ */ i("img", {
84
85
  src: t.avatar,
85
86
  alt: "",
86
87
  className: "h-ico-md w-ico-md rounded-pill object-cover"
87
- }) : /* @__PURE__ */ c("span", {
88
+ }) : /* @__PURE__ */ i("span", {
88
89
  className: "flex h-ico-md w-ico-md items-center justify-center rounded-pill bg-accent-2 text-[10px] font-semibold text-accent-11",
89
90
  children: t.label.charAt(0).toUpperCase()
90
- }), /* @__PURE__ */ c("span", { children: t.label })]
91
+ }), /* @__PURE__ */ i("span", { children: t.label })]
91
92
  }, t.id))
92
93
  }) : null;
93
94
  });
@@ -96,32 +97,32 @@ function _(e) {
96
97
  return () => {
97
98
  let t = null, n = null, r = null;
98
99
  return {
99
- onStart: (i) => {
100
+ onStart: (a) => {
100
101
  n = document.createElement("div"), n.style.position = "absolute", n.style.zIndex = "1400";
101
- let a = i.clientRect?.();
102
- a && (n.style.left = `${a.left}px`, n.style.top = `${a.bottom + 4}px`), document.body.appendChild(n), t = f(n), t.render(/* @__PURE__ */ c(g, {
102
+ let o = a.clientRect?.();
103
+ o && (n.style.left = `${o.left}px`, n.style.top = `${o.bottom + 4}px`), document.body.appendChild(n), t = f(n), t.render(/* @__PURE__ */ i(g, {
103
104
  ref: (e) => {
104
105
  r = e;
105
106
  },
106
- items: i.items,
107
+ items: a.items,
107
108
  command: (t) => {
108
- i.command({
109
+ a.command({
109
110
  id: t.id,
110
111
  label: t.label
111
112
  }), e?.(t);
112
113
  }
113
114
  }));
114
115
  },
115
- onUpdate: (i) => {
116
+ onUpdate: (a) => {
116
117
  if (!t || !n) return;
117
- let a = i.clientRect?.();
118
- a && (n.style.left = `${a.left}px`, n.style.top = `${a.bottom + 4}px`), t.render(/* @__PURE__ */ c(g, {
118
+ let o = a.clientRect?.();
119
+ o && (n.style.left = `${o.left}px`, n.style.top = `${o.bottom + 4}px`), t.render(/* @__PURE__ */ i(g, {
119
120
  ref: (e) => {
120
121
  r = e;
121
122
  },
122
- items: i.items,
123
+ items: a.items,
123
124
  command: (t) => {
124
- i.command({
125
+ a.command({
125
126
  id: t.id,
126
127
  label: t.label
127
128
  }), e?.(t);