@particle-academy/react-fancy 4.14.0 → 4.15.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/README.md CHANGED
@@ -163,6 +163,7 @@ npx vite build # Build demo app (verifies imports work)
163
163
  | Composer | Chat-style message input composing textarea + actions | [docs](docs/Composer.md) |
164
164
  | Chart | SVG-based Bar, Line, Area, Pie, Donut, and Sparkline charts | [docs](docs/Chart.md) |
165
165
  | Editor | Toolbar chrome wrapper for contentEditable, with a Source toggle to edit raw HTML/Markdown | [docs](docs/Editor.md) |
166
+ | CodeView | Lightweight syntax-highlighted source view (HTML highlighted; fills height) | [docs](docs/CodeView.md) |
166
167
  | Kanban | Drag-and-drop board with columns and cards | [docs](docs/Kanban.md) |
167
168
  | Canvas | Interactive node canvas with pan, zoom, and connections | [docs](docs/Canvas.md) |
168
169
  | Diagram | Entity-relationship diagram with draggable nodes and relation lines | [docs](docs/Diagram.md) |
package/dist/index.cjs CHANGED
@@ -7,6 +7,7 @@ var jsxRuntime = require('react/jsx-runtime');
7
7
  var reactDom = require('react-dom');
8
8
  var lucideReact = require('lucide-react');
9
9
  var marked = require('marked');
10
+ var fancyFileCommons = require('@particle-academy/fancy-file-commons');
10
11
 
11
12
  // src/components/Button/Button.tsx
12
13
  function cn(...inputs) {
@@ -11388,7 +11389,7 @@ function EditorToolbar({
11388
11389
  {
11389
11390
  "data-react-fancy-editor-toolbar": "",
11390
11391
  className: cn(
11391
- "flex items-center gap-0.5 border-b border-zinc-200 px-2 py-1.5 dark:border-zinc-700",
11392
+ "flex shrink-0 items-center gap-0.5 border-b border-zinc-200 px-2 py-1.5 dark:border-zinc-700",
11392
11393
  className
11393
11394
  ),
11394
11395
  children: children ?? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
@@ -11430,6 +11431,98 @@ function EditorToolbarSeparator() {
11430
11431
  );
11431
11432
  }
11432
11433
  EditorToolbarSeparator.displayName = "EditorToolbarSeparator";
11434
+ function subscribeDark(callback) {
11435
+ const mq = window.matchMedia("(prefers-color-scheme: dark)");
11436
+ mq.addEventListener("change", callback);
11437
+ const observer = new MutationObserver(callback);
11438
+ observer.observe(document.documentElement, {
11439
+ attributes: true,
11440
+ attributeFilter: ["class"]
11441
+ });
11442
+ return () => {
11443
+ mq.removeEventListener("change", callback);
11444
+ observer.disconnect();
11445
+ };
11446
+ }
11447
+ function darkSnapshot() {
11448
+ return document.documentElement.classList.contains("dark") || window.matchMedia("(prefers-color-scheme: dark)").matches;
11449
+ }
11450
+ function useIsDark() {
11451
+ return react.useSyncExternalStore(subscribeDark, darkSnapshot, () => false);
11452
+ }
11453
+ var TEXT_METRICS = "m-0 p-3 font-mono text-xs leading-relaxed";
11454
+ function CodeView({
11455
+ value,
11456
+ onChange,
11457
+ language = "plaintext",
11458
+ readOnly = false,
11459
+ placeholder,
11460
+ minHeight = 120,
11461
+ maxHeight,
11462
+ className
11463
+ }) {
11464
+ const textareaRef = react.useRef(null);
11465
+ const isDark = useIsDark();
11466
+ const colors = isDark ? fancyFileCommons.DARK_COLORS : fancyFileCommons.LIGHT_COLORS;
11467
+ const highlighted = react.useMemo(() => {
11468
+ const tokens = language === "html" ? fancyFileCommons.tokenizeHtml(value) : [];
11469
+ return fancyFileCommons.highlightCode(value, tokens, colors);
11470
+ }, [value, language, colors]);
11471
+ react.useEffect(() => {
11472
+ const el = textareaRef.current;
11473
+ if (!el) return;
11474
+ el.style.height = "auto";
11475
+ el.style.height = `${el.scrollHeight}px`;
11476
+ }, [value]);
11477
+ const editable = !readOnly && !!onChange;
11478
+ return /* @__PURE__ */ jsxRuntime.jsx(
11479
+ "div",
11480
+ {
11481
+ "data-react-fancy-code-view": "",
11482
+ className: cn("relative overflow-auto", className),
11483
+ style: { backgroundColor: colors.background, color: colors.foreground, minHeight, maxHeight },
11484
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative min-h-full", children: [
11485
+ /* @__PURE__ */ jsxRuntime.jsx(
11486
+ "pre",
11487
+ {
11488
+ "aria-hidden": "true",
11489
+ className: cn("pointer-events-none absolute inset-0 overflow-hidden border-none", TEXT_METRICS),
11490
+ style: { whiteSpace: "pre-wrap", overflowWrap: "break-word" },
11491
+ children: /* @__PURE__ */ jsxRuntime.jsx("code", { dangerouslySetInnerHTML: { __html: highlighted + "\n" } })
11492
+ }
11493
+ ),
11494
+ placeholder && value.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("pointer-events-none absolute left-0 top-0 opacity-40", TEXT_METRICS), children: placeholder }),
11495
+ /* @__PURE__ */ jsxRuntime.jsx(
11496
+ "textarea",
11497
+ {
11498
+ ref: textareaRef,
11499
+ "data-react-fancy-code-view-input": "",
11500
+ value,
11501
+ onChange: editable ? (e) => onChange(e.target.value) : void 0,
11502
+ readOnly: !editable,
11503
+ spellCheck: false,
11504
+ autoComplete: "off",
11505
+ autoCorrect: "off",
11506
+ autoCapitalize: "off",
11507
+ "aria-label": placeholder,
11508
+ className: cn(
11509
+ "relative block w-full resize-none border-none bg-transparent text-transparent outline-none",
11510
+ TEXT_METRICS
11511
+ ),
11512
+ style: {
11513
+ caretColor: colors.cursorColor,
11514
+ overflow: "hidden",
11515
+ whiteSpace: "pre-wrap",
11516
+ overflowWrap: "break-word",
11517
+ minHeight
11518
+ }
11519
+ }
11520
+ )
11521
+ ] })
11522
+ }
11523
+ );
11524
+ }
11525
+ CodeView.displayName = "CodeView";
11433
11526
 
11434
11527
  // src/components/Editor/editor.utils.ts
11435
11528
  var proseClasses = [
@@ -11652,24 +11745,14 @@ function EditorContent({ className, maxHeight, sourceClassName }) {
11652
11745
  );
11653
11746
  if (showSource) {
11654
11747
  return /* @__PURE__ */ jsxRuntime.jsx(
11655
- "textarea",
11748
+ CodeView,
11656
11749
  {
11657
- "data-react-fancy-editor-source": "",
11658
11750
  value: _sourceValue,
11659
- onChange: (e) => _onSourceInput(e.target.value),
11660
- spellCheck: false,
11751
+ onChange: _onSourceInput,
11752
+ language: outputFormat === "html" ? "html" : "plaintext",
11661
11753
  placeholder,
11662
- "aria-label": `${outputFormat === "markdown" ? "Markdown" : "HTML"} source`,
11663
- style: {
11664
- maxHeight: maxHeight ? `${maxHeight}px` : void 0
11665
- },
11666
- className: cn(
11667
- "block min-h-[120px] w-full resize-y px-4 py-3 font-mono text-xs leading-relaxed outline-none",
11668
- "bg-zinc-50 text-zinc-800 focus:outline-none dark:bg-zinc-900 dark:text-zinc-200",
11669
- "placeholder:text-zinc-400",
11670
- maxHeight && "overflow-y-auto",
11671
- sourceClassName
11672
- )
11754
+ maxHeight,
11755
+ className: cn("min-h-0 flex-auto", sourceClassName)
11673
11756
  }
11674
11757
  );
11675
11758
  }
@@ -11687,9 +11770,8 @@ function EditorContent({ className, maxHeight, sourceClassName }) {
11687
11770
  maxHeight: maxHeight ? `${maxHeight}px` : void 0
11688
11771
  },
11689
11772
  className: cn(
11690
- "min-h-[120px] px-4 py-3 text-sm outline-none",
11773
+ "min-h-[120px] flex-auto overflow-y-auto px-4 py-3 text-sm outline-none",
11691
11774
  "focus:outline-none",
11692
- maxHeight && "overflow-y-auto",
11693
11775
  proseClasses,
11694
11776
  extClasses,
11695
11777
  "empty:before:content-[attr(data-placeholder)] empty:before:text-zinc-400 empty:before:pointer-events-none",
@@ -11989,7 +12071,7 @@ function EditorRoot({
11989
12071
  "data-react-fancy-editor": "",
11990
12072
  "data-mode": "edit",
11991
12073
  className: cn(
11992
- "overflow-hidden rounded-xl border border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900",
12074
+ "flex flex-col overflow-hidden rounded-xl border border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900",
11993
12075
  className
11994
12076
  ),
11995
12077
  children
@@ -12963,6 +13045,7 @@ function TreeNode({ node, depth }) {
12963
13045
  indentSize,
12964
13046
  showIcons,
12965
13047
  draggable,
13048
+ dragCursor,
12966
13049
  dragState,
12967
13050
  setDragState,
12968
13051
  onNodeMove,
@@ -13093,7 +13176,12 @@ function TreeNode({ node, depth }) {
13093
13176
  isSelected ? "bg-blue-500/15 text-blue-600 dark:text-blue-400" : "text-zinc-700 hover:bg-zinc-100 dark:text-zinc-300 dark:hover:bg-zinc-800",
13094
13177
  node.disabled && "pointer-events-none opacity-40",
13095
13178
  isDragging && "opacity-50",
13096
- canDrag && "cursor-grab active:cursor-grabbing",
13179
+ // Grab affordance. By default (`dragCursor="none"`) the row keeps its
13180
+ // normal click cursor on hover — no open-hand "move me" cursor before
13181
+ // any drag — and only shows the closed-hand `grabbing` cursor while a
13182
+ // drag is actually underway (mousedown/drag). `dragCursor="grab"` opts
13183
+ // back into the always-on grab cursor for whole-row drag handles (#14).
13184
+ canDrag && (dragCursor === "grab" ? "cursor-grab active:cursor-grabbing" : "active:cursor-grabbing"),
13097
13185
  isDropTarget && dropPosition === "inside" && "bg-blue-500/10 ring-1 ring-blue-500/30 ring-inset"
13098
13186
  ),
13099
13187
  style: { paddingLeft },
@@ -13149,6 +13237,7 @@ function TreeNavRoot({
13149
13237
  defaultExpandAll = false,
13150
13238
  indentSize = 16,
13151
13239
  showIcons = true,
13240
+ dragCursor = "none",
13152
13241
  className
13153
13242
  }) {
13154
13243
  const [internalExpanded, setInternalExpanded] = react.useState(() => {
@@ -13193,6 +13282,7 @@ function TreeNavRoot({
13193
13282
  indentSize,
13194
13283
  showIcons,
13195
13284
  draggable,
13285
+ dragCursor,
13196
13286
  dragState,
13197
13287
  setDragState,
13198
13288
  onNodeMove,
@@ -13210,6 +13300,7 @@ function TreeNavRoot({
13210
13300
  indentSize,
13211
13301
  showIcons,
13212
13302
  draggable,
13303
+ dragCursor,
13213
13304
  dragState,
13214
13305
  onNodeMove,
13215
13306
  acceptExternalDrops,
@@ -15986,6 +16077,7 @@ exports.Chart = Chart;
15986
16077
  exports.ChatDrawer = ChatDrawer;
15987
16078
  exports.Checkbox = Checkbox;
15988
16079
  exports.CheckboxGroup = CheckboxGroup;
16080
+ exports.CodeView = CodeView;
15989
16081
  exports.ColorPicker = ColorPicker;
15990
16082
  exports.Command = Command;
15991
16083
  exports.Composer = Composer;