@kyro-cms/admin 0.9.8 → 0.10.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/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- import React, { createContext, lazy, useState, useContext, useRef, useEffect, useCallback, useMemo, Suspense } from 'react';
1
+ import React, { createContext, lazy, useState, useRef, useEffect, useCallback, useContext, useMemo, Suspense } from 'react';
2
2
  import { create, useStore } from 'zustand';
3
3
  import { persist, createJSONStorage } from 'zustand/middleware';
4
- import { GripVertical, Copy, X, Video, Mail, Clock, Database, Users, Tag, Image, Activity, CircleHelp, Sparkles, Blocks, Columns3, Link2, ListOrdered, Box, Heading1, Star, AlignLeft, ChevronDown, File, Code, List, Plus, ChevronRight, ChevronUp, Search, LoaderCircle, Check, RefreshCw, Undo, Redo, Bold, Italic, Underline, Strikethrough, Highlighter, Palette, CheckSquare, Quote, Terminal, Minus, AlignCenter, AlignRight, Link, Minimize2, Maximize2, Grid3X3, Filter, FolderPlus, FolderInput, Folder, Trash2, Film, Music, FileText, Archive, Download, Crop, Server, CodeXml, Pencil, Eye, EllipsisVertical, Send, Info, TriangleAlert, ShieldAlert, CircleCheck, History, CheckCircle2, User, GitCompare, Undo2, ExternalLink, Settings, Shield, Hexagon, Network, Sun, Moon, LogOut, ArrowRight, LayoutDashboard, ArrowUpRight, Webhook, Zap, Pause, Play, Key, Lock, EyeOff, CirclePlay, RefreshCcw, Save, UserPlus, LockOpen } from 'lucide-react';
4
+ import { GripVertical, Copy, X, Video, Mail, Clock, Database, Users, Tag, Image, Activity, CircleHelp, Sparkles, Blocks, Columns3, Link2, ListOrdered, Box, Heading1, Star, AlignLeft, ChevronDown, File, Code, List, Plus, ChevronRight, ChevronUp, Search, LoaderCircle, Check, RefreshCw, CodeXml, FileText, Music, Film, Undo, Redo, Bold, Italic, Underline, Strikethrough, Highlighter, Palette, CheckSquare, Quote, Terminal, Minus, AlignCenter, AlignRight, Link, Minimize2, Maximize2, Grid3X3, Filter, FolderPlus, FolderInput, Folder, Trash2, Archive, Download, Crop, Server, Pencil, Eye, EllipsisVertical, Send, Info, TriangleAlert, ShieldAlert, CircleCheck, History, CheckCircle2, User, GitCompare, Undo2, ExternalLink, Settings, Shield, Hexagon, Network, Sun, Moon, LogOut, ArrowRight, LayoutDashboard, ArrowUpRight, Webhook, Zap, Pause, Play, Key, Lock, EyeOff, CirclePlay, RefreshCcw, Save, UserPlus, LockOpen } from 'lucide-react';
5
5
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
6
6
  import { createPortal } from 'react-dom';
7
7
  import { useEditor, EditorContent } from '@tiptap/react';
@@ -3840,33 +3840,63 @@ function RichTextField({
3840
3840
  error,
3841
3841
  disabled
3842
3842
  }) {
3843
- const [isExpanded, setIsExpanded] = useState(false);
3844
- const [panelWidth, setPanelWidth] = useState(0);
3845
- const [isMediaPickerOpen, setIsMediaPickerOpen] = useState(false);
3846
3843
  const [isMounted, setIsMounted] = useState(false);
3847
3844
  useEffect(() => {
3848
3845
  setIsMounted(true);
3849
3846
  }, []);
3847
+ if (!isMounted) {
3848
+ return /* @__PURE__ */ jsx(FieldLayout, { field: field3, error, children: /* @__PURE__ */ jsxs(
3849
+ "div",
3850
+ {
3851
+ className: `border rounded-lg bg-[var(--kyro-bg)] overflow-hidden border-[var(--kyro-border)] flex flex-col shadow-sm transition-all duration-200
3852
+ ${error ? "border-[var(--kyro-error)] shadow-[0_0_0_1px_var(--kyro-error)]" : "border-[var(--kyro-border)]"}
3853
+ ${disabled ? "opacity-60 cursor-not-allowed" : ""}`,
3854
+ children: [
3855
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-1.5 p-1.5 border-b border-[var(--kyro-border)] bg-[var(--kyro-bg-secondary)] rounded-t-lg h-[40px]" }),
3856
+ /* @__PURE__ */ jsx("div", { className: "overflow-y-auto min-h-[160px] max-h-[400px] p-4" })
3857
+ ]
3858
+ }
3859
+ ) });
3860
+ }
3861
+ return /* @__PURE__ */ jsx(
3862
+ RichTextEditor,
3863
+ {
3864
+ field: field3,
3865
+ value,
3866
+ onChange,
3867
+ error,
3868
+ disabled
3869
+ }
3870
+ );
3871
+ }
3872
+ function RichTextEditor({
3873
+ field: field3,
3874
+ value,
3875
+ onChange,
3876
+ error,
3877
+ disabled
3878
+ }) {
3879
+ const [isExpanded, setIsExpanded] = useState(false);
3880
+ const [panelWidth, setPanelWidth] = useState(0);
3881
+ const [isMediaPickerOpen, setIsMediaPickerOpen] = useState(false);
3850
3882
  useEffect(() => {
3851
3883
  if (!isExpanded) {
3852
3884
  setPanelWidth(0);
3853
3885
  return;
3854
3886
  }
3887
+ const panel = document.querySelector('[data-kyro-slide-panel="true"]');
3855
3888
  const updateWidth = () => {
3856
- const panel2 = document.querySelector('[data-kyro-slide-panel="true"]');
3857
- if (panel2) {
3858
- setPanelWidth(panel2.getBoundingClientRect().width);
3889
+ if (panel) {
3890
+ setPanelWidth(panel.getBoundingClientRect().width);
3859
3891
  } else {
3860
3892
  setPanelWidth(0);
3861
3893
  }
3862
3894
  };
3863
3895
  updateWidth();
3864
- let observer = null;
3865
- const panel = document.querySelector('[data-kyro-slide-panel="true"]');
3866
- if (panel && typeof ResizeObserver !== "undefined") {
3867
- observer = new ResizeObserver(() => {
3868
- updateWidth();
3869
- });
3896
+ const observer = new MutationObserver(() => {
3897
+ updateWidth();
3898
+ });
3899
+ if (panel) {
3870
3900
  observer.observe(panel);
3871
3901
  }
3872
3902
  window.addEventListener("resize", updateWidth);
@@ -3904,7 +3934,7 @@ function RichTextField({
3904
3934
  TextStyle,
3905
3935
  Color
3906
3936
  ],
3907
- content: value || { type: "doc", content: [] },
3937
+ content: Array.isArray(value) ? { type: "doc", content: value } : value || { type: "doc", content: [] },
3908
3938
  editable: !disabled,
3909
3939
  onUpdate: ({ editor: editor2 }) => {
3910
3940
  onChange(editor2.getJSON());
@@ -3917,23 +3947,9 @@ function RichTextField({
3917
3947
  });
3918
3948
  useEffect(() => {
3919
3949
  if (editor && value && JSON.stringify(value) !== JSON.stringify(editor.getJSON())) {
3920
- editor.commands.setContent(value);
3950
+ editor.commands.setContent(Array.isArray(value) ? { type: "doc", content: value } : value);
3921
3951
  }
3922
3952
  }, [value, editor]);
3923
- if (!isMounted) {
3924
- return /* @__PURE__ */ jsx(FieldLayout, { field: field3, error, children: /* @__PURE__ */ jsxs(
3925
- "div",
3926
- {
3927
- className: `border rounded-lg bg-[var(--kyro-bg)] overflow-hidden border-[var(--kyro-border)] flex flex-col shadow-sm transition-all duration-200
3928
- ${error ? "border-[var(--kyro-error)] shadow-[0_0_0_1px_var(--kyro-error)]" : "border-[var(--kyro-border)]"}
3929
- ${disabled ? "opacity-60 cursor-not-allowed" : ""}`,
3930
- children: [
3931
- /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-1.5 p-1.5 border-b border-[var(--kyro-border)] bg-[var(--kyro-bg-secondary)] rounded-t-lg h-[40px]" }),
3932
- /* @__PURE__ */ jsx("div", { className: "overflow-y-auto min-h-[160px] max-h-[400px] p-4" })
3933
- ]
3934
- }
3935
- ) });
3936
- }
3937
3953
  return /* @__PURE__ */ jsxs(FieldLayout, { field: field3, error, children: [
3938
3954
  isExpanded ? (
3939
3955
  // Maximize mode placeholder inside form
@@ -5867,6 +5883,7 @@ function createNewBlock(type) {
5867
5883
  return {
5868
5884
  id: Math.random().toString(36).substr(2, 9),
5869
5885
  type,
5886
+ name: "",
5870
5887
  data,
5871
5888
  options,
5872
5889
  children,
@@ -7504,6 +7521,18 @@ var BlockEditModal = ({
7504
7521
  accentClass: theme.border,
7505
7522
  children: [
7506
7523
  /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
7524
+ /* @__PURE__ */ jsxs("div", { children: [
7525
+ /* @__PURE__ */ jsx("label", { className: "text-[10px] font-medium text-[var(--kyro-text-muted)] mb-1 block", children: "Block Name" }),
7526
+ /* @__PURE__ */ jsx(
7527
+ "input",
7528
+ {
7529
+ value: blockData?.name || "",
7530
+ onChange: (e) => updateBlock(block3.id, { name: e.target.value }),
7531
+ placeholder: blockSchema?.label || block3.type,
7532
+ className: "w-full bg-[var(--kyro-bg-primary)] border border-[var(--kyro-border)] rounded-lg px-3 py-2 text-sm text-[var(--kyro-text-primary)] outline-none focus:border-[var(--kyro-primary)] transition-colors"
7533
+ }
7534
+ )
7535
+ ] }),
7507
7536
  renderFields(),
7508
7537
  children.length > 0 && /* @__PURE__ */ jsxs("div", { className: "pt-4 border-t border-[var(--kyro-border)]", children: [
7509
7538
  /* @__PURE__ */ jsxs("label", { className: "text-[10px] font-medium text-[var(--kyro-text-muted)] mb-1.5 block", children: [
@@ -7615,7 +7644,7 @@ var ChildBlocksTree = ({
7615
7644
  blockIcons[child.type] && /* @__PURE__ */ jsx("div", { className: "w-8 h-8 rounded bg-[var(--kyro-surface-accent)] flex items-center justify-center text-[var(--kyro-text-secondary)]", children: blockIcons[child.type] }),
7616
7645
  /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
7617
7646
  /* @__PURE__ */ jsxs("div", { className: "text-xs font-medium text-[var(--kyro-text-secondary)] truncate", children: [
7618
- getBlockLabel(child.type),
7647
+ getBlockDisplayLabel(child),
7619
7648
  child.data?.text ? ` - ${child.data.text.slice(0, 30)}` : "",
7620
7649
  child.data?.heading ? ` - ${child.data.heading.slice(0, 30)}` : ""
7621
7650
  ] }),
@@ -7836,7 +7865,7 @@ var NestedChildBlocks = ({
7836
7865
  blockIcons[child.type] && /* @__PURE__ */ jsx("div", { className: "w-8 h-8 rounded bg-[var(--kyro-surface-accent)] flex items-center justify-center text-[var(--kyro-text-secondary)]", children: blockIcons[child.type] }),
7837
7866
  /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
7838
7867
  /* @__PURE__ */ jsxs("div", { className: "text-xs font-medium text-[var(--kyro-text-secondary)] truncate", children: [
7839
- getBlockLabel(child.type),
7868
+ getBlockDisplayLabel(child),
7840
7869
  child.data?.text ? ` - ${child.data.text.slice(0, 30)}` : "",
7841
7870
  child.data?.heading ? ` - ${child.data.heading.slice(0, 30)}` : ""
7842
7871
  ] }),
@@ -8550,7 +8579,7 @@ var blockIcons = {
8550
8579
  function getBlockComponent(type) {
8551
8580
  return BLOCK_COMPONENTS[type] || null;
8552
8581
  }
8553
- function getBlockLabel(type) {
8582
+ function getBlockLabel2(type) {
8554
8583
  const labelMap = {
8555
8584
  // Primitives
8556
8585
  paragraph: "Paragraph",
@@ -8593,6 +8622,11 @@ function getBlockLabel(type) {
8593
8622
  };
8594
8623
  return labelMap[type] || type;
8595
8624
  }
8625
+ function getBlockDisplayLabel(block3) {
8626
+ const name = block3.name;
8627
+ if (name && name.trim()) return name.trim();
8628
+ return getBlockLabel2(block3.type);
8629
+ }
8596
8630
  function getBlockPreviewSnippet(data, blockSchema) {
8597
8631
  if (blockSchema?.fields) {
8598
8632
  for (const field3 of blockSchema.fields) {
@@ -8621,16 +8655,32 @@ var SortableBlockComponent = ({
8621
8655
  transition,
8622
8656
  isDragging
8623
8657
  } = useSortable({ id: block3.id });
8624
- const { removeBlock } = useBlockActions();
8658
+ const { removeBlock, updateBlock } = useBlockActions();
8625
8659
  const isEditing = editingBlockId === block3.id;
8626
8660
  const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
8661
+ const [editingName, setEditingName] = useState(false);
8662
+ const [nameDraft, setNameDraft] = useState(block3.name || "");
8663
+ const nameInputRef = useRef(null);
8664
+ useEffect(() => {
8665
+ if (editingName && nameInputRef.current) {
8666
+ nameInputRef.current.focus();
8667
+ nameInputRef.current.select();
8668
+ }
8669
+ }, [editingName]);
8670
+ const commitName = useCallback(() => {
8671
+ setEditingName(false);
8672
+ const trimmed = nameDraft.trim();
8673
+ if (trimmed !== (block3.name || "").trim()) {
8674
+ updateBlock(block3.id, { name: trimmed || "" });
8675
+ }
8676
+ }, [nameDraft, block3.name, block3.id, updateBlock]);
8627
8677
  const style = {
8628
8678
  transform: CSS.Transform.toString(transform),
8629
8679
  transition,
8630
8680
  zIndex: isDragging ? 10 : 1,
8631
8681
  opacity: isDragging ? 0.8 : 1
8632
8682
  };
8633
- const itemLabel = getBlockLabel(block3.type);
8683
+ const itemLabel = getBlockDisplayLabel(block3);
8634
8684
  const data = block3.data || {};
8635
8685
  const previewSnippet = getBlockPreviewSnippet(data, blockSchema);
8636
8686
  if (compact) {
@@ -8652,7 +8702,36 @@ var SortableBlockComponent = ({
8652
8702
  }
8653
8703
  ),
8654
8704
  blockIcons[block3.type] && /* @__PURE__ */ jsx("span", { className: "text-[var(--kyro-text-secondary)] flex-shrink-0", children: blockIcons[block3.type] }),
8655
- /* @__PURE__ */ jsx("span", { className: "font-medium text-[var(--kyro-text-secondary)] truncate max-w-[120px]", children: itemLabel }),
8705
+ editingName ? /* @__PURE__ */ jsx(
8706
+ "input",
8707
+ {
8708
+ ref: nameInputRef,
8709
+ value: nameDraft,
8710
+ onChange: (e) => setNameDraft(e.target.value),
8711
+ onBlur: commitName,
8712
+ onKeyDown: (e) => {
8713
+ if (e.key === "Enter") commitName();
8714
+ if (e.key === "Escape") {
8715
+ setNameDraft(block3.name || "");
8716
+ setEditingName(false);
8717
+ }
8718
+ },
8719
+ onClick: (e) => e.stopPropagation(),
8720
+ className: "flex-1 min-w-0 bg-[var(--kyro-surface-accent)] border border-[var(--kyro-primary)] rounded px-1.5 py-0.5 text-[10px] font-medium text-[var(--kyro-text-primary)] outline-none"
8721
+ }
8722
+ ) : /* @__PURE__ */ jsx(
8723
+ "span",
8724
+ {
8725
+ onClick: (e) => {
8726
+ e.stopPropagation();
8727
+ setNameDraft(block3.name || "");
8728
+ setEditingName(true);
8729
+ },
8730
+ className: "font-medium text-[var(--kyro-text-secondary)] truncate max-w-[120px] cursor-text hover:text-[var(--kyro-text-primary)] transition-colors",
8731
+ title: "Click to rename",
8732
+ children: itemLabel
8733
+ }
8734
+ ),
8656
8735
  showDeleteConfirm ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5", onClick: (e) => e.stopPropagation(), children: [
8657
8736
  /* @__PURE__ */ jsx(
8658
8737
  "button",
@@ -8734,13 +8813,43 @@ var SortableBlockComponent = ({
8734
8813
  children: [
8735
8814
  blockIcons[block3.type] && /* @__PURE__ */ jsx("span", { className: "text-[var(--kyro-text-secondary)]", children: blockIcons[block3.type] }),
8736
8815
  /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
8737
- /* @__PURE__ */ jsxs("div", { className: "text-xs font-semibold text-[var(--kyro-text-secondary)] truncate", children: [
8738
- itemLabel,
8739
- previewSnippet && typeof previewSnippet === "string" && /* @__PURE__ */ jsxs("span", { className: "text-[var(--kyro-text-muted)] font-normal ml-1.5", children: [
8740
- "- ",
8741
- previewSnippet.length > 40 ? `${previewSnippet.slice(0, 40)}...` : previewSnippet
8742
- ] })
8743
- ] }),
8816
+ editingName ? /* @__PURE__ */ jsx(
8817
+ "input",
8818
+ {
8819
+ ref: nameInputRef,
8820
+ value: nameDraft,
8821
+ onChange: (e) => setNameDraft(e.target.value),
8822
+ onBlur: commitName,
8823
+ onKeyDown: (e) => {
8824
+ if (e.key === "Enter") commitName();
8825
+ if (e.key === "Escape") {
8826
+ setNameDraft(block3.name || "");
8827
+ setEditingName(false);
8828
+ }
8829
+ },
8830
+ onClick: (e) => e.stopPropagation(),
8831
+ placeholder: getBlockLabel2(block3.type),
8832
+ className: "w-full bg-[var(--kyro-surface-accent)] border border-[var(--kyro-primary)] rounded px-2 py-0.5 text-xs font-semibold text-[var(--kyro-text-primary)] outline-none"
8833
+ }
8834
+ ) : /* @__PURE__ */ jsxs(
8835
+ "div",
8836
+ {
8837
+ onClick: (e) => {
8838
+ e.stopPropagation();
8839
+ setNameDraft(block3.name || "");
8840
+ setEditingName(true);
8841
+ },
8842
+ className: "text-xs font-semibold text-[var(--kyro-text-secondary)] truncate cursor-text hover:text-[var(--kyro-text-primary)] transition-colors",
8843
+ title: "Click to rename",
8844
+ children: [
8845
+ itemLabel,
8846
+ previewSnippet && typeof previewSnippet === "string" && /* @__PURE__ */ jsxs("span", { className: "text-[var(--kyro-text-muted)] font-normal ml-1.5", children: [
8847
+ "- ",
8848
+ previewSnippet.length > 40 ? `${previewSnippet.slice(0, 40)}...` : previewSnippet
8849
+ ] })
8850
+ ]
8851
+ }
8852
+ ),
8744
8853
  blockSchema?.admin?.description && /* @__PURE__ */ jsx("div", { className: "text-[10px] text-[var(--kyro-text-muted)] mt-0.5 truncate opacity-80", children: blockSchema.admin.description })
8745
8854
  ] }),
8746
8855
  block3.children && Array.isArray(block3.children) && block3.children.length > 0 && /* @__PURE__ */ jsxs("span", { className: "text-[10px] bg-[var(--kyro-surface-accent)] px-2 py-0.5 rounded text-[var(--kyro-text-muted)] font-medium", children: [
@@ -14589,7 +14698,6 @@ var samplePlugin = {
14589
14698
  description: "A tiny sample plugin to demonstrate the extensibility surface",
14590
14699
  hooks: {
14591
14700
  onAdminReady: () => {
14592
- console.log("[ Kyro Admin ] sample-plugin: onAdminReady executed");
14593
14701
  return;
14594
14702
  }
14595
14703
  }
@@ -14603,7 +14711,6 @@ var samplePlugin2 = {
14603
14711
  description: "Second MVP plugin demonstrating beforeDeploy hook",
14604
14712
  hooks: {
14605
14713
  beforeDeploy: (ctx) => {
14606
- console.log("[Kyro Admin] sample-plugin-2 beforeDeploy");
14607
14714
  return { success: true };
14608
14715
  }
14609
14716
  }
@@ -14894,7 +15001,6 @@ export function c(size) {
14894
15001
  function debug(namespace) {
14895
15002
  function d(...args) {
14896
15003
  if (typeof localStorage !== "undefined" && localStorage.getItem("DEBUG")) {
14897
- console.log(namespace, ...args);
14898
15004
  }
14899
15005
  }
14900
15006
  d.enabled = false;
@@ -14914,9 +15020,33 @@ export default debug;
14914
15020
  "kyro:config": resolvedConfig
14915
15021
  }
14916
15022
  },
15023
+ build: {
15024
+ rollupOptions: {
15025
+ output: {
15026
+ manualChunks(id) {
15027
+ if (id.includes("@tiptap") || id.includes("prosemirror")) {
15028
+ return "vendor-tiptap";
15029
+ }
15030
+ }
15031
+ }
15032
+ }
15033
+ },
14917
15034
  optimizeDeps: {
14918
15035
  include: [
14919
- "use-sync-external-store"
15036
+ "use-sync-external-store",
15037
+ "@tiptap/core",
15038
+ "@tiptap/react",
15039
+ "@tiptap/pm",
15040
+ "@tiptap/starter-kit",
15041
+ "@tiptap/extension-link",
15042
+ "@tiptap/extension-image",
15043
+ "@tiptap/extension-text-align",
15044
+ "@tiptap/extension-underline",
15045
+ "@tiptap/extension-highlight",
15046
+ "@tiptap/extension-task-list",
15047
+ "@tiptap/extension-task-item",
15048
+ "@tiptap/extension-text-style",
15049
+ "@tiptap/extension-color"
14920
15050
  ],
14921
15051
  exclude: ["debug", "react/compiler-runtime"]
14922
15052
  },
@@ -14926,7 +15056,31 @@ export default debug;
14926
15056
  __KYRO_ADMIN_CONFIG_FILE__: JSON.stringify(configFile)
14927
15057
  },
14928
15058
  ssr: {
14929
- noExternal: ["@kyro-cms/admin", "@kyro-cms/core"]
15059
+ noExternal: [
15060
+ "@kyro-cms/admin",
15061
+ "@kyro-cms/core",
15062
+ "@tiptap/core",
15063
+ "@tiptap/react",
15064
+ "@tiptap/pm",
15065
+ "@tiptap/starter-kit",
15066
+ "@tiptap/extension-link",
15067
+ "@tiptap/extension-image",
15068
+ "@tiptap/extension-text-align",
15069
+ "@tiptap/extension-underline",
15070
+ "@tiptap/extension-highlight",
15071
+ "@tiptap/extension-task-list",
15072
+ "@tiptap/extension-task-item",
15073
+ "@tiptap/extension-text-style",
15074
+ "@tiptap/extension-color",
15075
+ "prosemirror-model",
15076
+ "prosemirror-state",
15077
+ "prosemirror-view",
15078
+ "prosemirror-schema-list",
15079
+ "prosemirror-commands",
15080
+ "prosemirror-keymap",
15081
+ "prosemirror-transform",
15082
+ "prosemirror-inputrules"
15083
+ ]
14930
15084
  }
14931
15085
  }
14932
15086
  });