@k2b/ui 0.16.0 → 0.18.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 (88) hide show
  1. package/dist/browser/chat/ChatComposer.js +261 -274
  2. package/dist/browser/chat/ChatComposer.js.map +1 -1
  3. package/dist/browser/chat/ChatPrimitives.js +9 -6
  4. package/dist/browser/chat/ChatPrimitives.js.map +1 -1
  5. package/dist/browser/content/FileView.js +62 -36
  6. package/dist/browser/content/FileView.js.map +1 -1
  7. package/dist/browser/content/MarkdownView.js +9 -3
  8. package/dist/browser/content/MarkdownView.js.map +1 -1
  9. package/dist/browser/content/VideoPlayer.js +279 -0
  10. package/dist/browser/content/VideoPlayer.js.map +1 -0
  11. package/dist/browser/content/file-view-preview.js +2 -2
  12. package/dist/browser/content/file-view-preview.js.map +1 -1
  13. package/dist/browser/content/index.js +2 -0
  14. package/dist/browser/content/index.js.map +1 -1
  15. package/dist/browser/content/markdown-links.js +178 -0
  16. package/dist/browser/content/markdown-links.js.map +1 -0
  17. package/dist/browser/inputs/FileDropTarget.js +129 -0
  18. package/dist/browser/inputs/FileDropTarget.js.map +1 -0
  19. package/dist/browser/inputs/FileInputs.js +37 -11
  20. package/dist/browser/inputs/FileInputs.js.map +1 -1
  21. package/dist/browser/inputs/file-drop.js +338 -0
  22. package/dist/browser/inputs/file-drop.js.map +1 -0
  23. package/dist/browser/inputs/index.js +1 -0
  24. package/dist/browser/inputs/index.js.map +1 -1
  25. package/dist/browser/inputs/markdown/MarkdownEditor.js +72 -78
  26. package/dist/browser/inputs/markdown/MarkdownEditor.js.map +1 -1
  27. package/dist/browser/intl/messages.js +97 -4
  28. package/dist/browser/intl/messages.js.map +1 -1
  29. package/dist/browser/layout/AppWorkspace.js +210 -44
  30. package/dist/browser/layout/AppWorkspace.js.map +1 -1
  31. package/dist/browser/layout/Navigation.js +278 -170
  32. package/dist/browser/layout/Navigation.js.map +1 -1
  33. package/dist/browser/layout/index.js.map +1 -1
  34. package/dist/browser/layout/navigation-model.js +3 -1
  35. package/dist/browser/layout/navigation-model.js.map +1 -1
  36. package/dist/browser/surfaces/NoticeCard.js +47 -44
  37. package/dist/browser/surfaces/NoticeCard.js.map +1 -1
  38. package/dist/ssr/chat/ChatComposer.js +59 -48
  39. package/dist/ssr/chat/ChatComposer.js.map +1 -1
  40. package/dist/ssr/chat/ChatPrimitives.js +9 -6
  41. package/dist/ssr/chat/ChatPrimitives.js.map +1 -1
  42. package/dist/ssr/content/FileView.js +30 -6
  43. package/dist/ssr/content/FileView.js.map +1 -1
  44. package/dist/ssr/content/MarkdownView.js +9 -3
  45. package/dist/ssr/content/MarkdownView.js.map +1 -1
  46. package/dist/ssr/content/VideoPlayer.js +208 -0
  47. package/dist/ssr/content/VideoPlayer.js.map +1 -0
  48. package/dist/ssr/content/file-view-preview.js +2 -2
  49. package/dist/ssr/content/file-view-preview.js.map +1 -1
  50. package/dist/ssr/content/index.js +2 -0
  51. package/dist/ssr/content/index.js.map +1 -1
  52. package/dist/ssr/content/markdown-links.js +178 -0
  53. package/dist/ssr/content/markdown-links.js.map +1 -0
  54. package/dist/ssr/inputs/FileDropTarget.js +103 -0
  55. package/dist/ssr/inputs/FileDropTarget.js.map +1 -0
  56. package/dist/ssr/inputs/FileInputs.js +32 -10
  57. package/dist/ssr/inputs/FileInputs.js.map +1 -1
  58. package/dist/ssr/inputs/file-drop.js +338 -0
  59. package/dist/ssr/inputs/file-drop.js.map +1 -0
  60. package/dist/ssr/inputs/index.js +1 -0
  61. package/dist/ssr/inputs/index.js.map +1 -1
  62. package/dist/ssr/inputs/markdown/MarkdownEditor.js +14 -2
  63. package/dist/ssr/inputs/markdown/MarkdownEditor.js.map +1 -1
  64. package/dist/ssr/intl/messages.js +97 -4
  65. package/dist/ssr/intl/messages.js.map +1 -1
  66. package/dist/ssr/layout/AppWorkspace.js +173 -27
  67. package/dist/ssr/layout/AppWorkspace.js.map +1 -1
  68. package/dist/ssr/layout/Navigation.js +175 -102
  69. package/dist/ssr/layout/Navigation.js.map +1 -1
  70. package/dist/ssr/layout/index.js.map +1 -1
  71. package/dist/ssr/layout/navigation-model.js +3 -1
  72. package/dist/ssr/layout/navigation-model.js.map +1 -1
  73. package/dist/ssr/surfaces/NoticeCard.js +17 -12
  74. package/dist/ssr/surfaces/NoticeCard.js.map +1 -1
  75. package/dist/styles.css +1 -1
  76. package/dist/types/chat/ChatPrimitives.d.ts +1 -1
  77. package/dist/types/content/VideoPlayer.d.ts +41 -0
  78. package/dist/types/content/index.d.ts +2 -0
  79. package/dist/types/content/markdown-links.d.ts +66 -0
  80. package/dist/types/inputs/FileDropTarget.d.ts +23 -0
  81. package/dist/types/inputs/FileInputs.d.ts +2 -0
  82. package/dist/types/inputs/file-drop.d.ts +80 -0
  83. package/dist/types/inputs/index.d.ts +2 -0
  84. package/dist/types/intl/messages.d.ts +47 -0
  85. package/dist/types/layout/AppWorkspace.d.ts +21 -0
  86. package/dist/types/layout/index.d.ts +2 -2
  87. package/dist/types/layout/navigation-model.d.ts +29 -0
  88. package/package.json +1 -1
@@ -31,19 +31,18 @@ var _tmpl$ = ["<button type=\"button\" class=\"k2b-app-workspace__resize\" role=
31
31
  _tmpl$20 = ["<span class=\"k2b-app-workspace__sidebar-item-meta\"", ">", "</span>"],
32
32
  _tmpl$21 = ["<a", " class=\"k2b-app-workspace__sidebar-item-action\"", ">", "</a>"],
33
33
  _tmpl$22 = ["<button type=\"button\" class=\"k2b-app-workspace__sidebar-item-action\"", ">", "</button>"],
34
- _tmpl$23 = ["<span class=\"k2b-app-workspace__sidebar-item-icon\"", " aria-hidden=\"true\"><i", "></i></span>"],
35
- _tmpl$24 = ["<span class=\"k2b-app-workspace__sidebar-item-label\"><span class=\"k2b-app-workspace__sidebar-item-label-text\">", "</span></span>"],
36
- _tmpl$25 = ["<span class=\"", "\"", ">", "</span>"],
37
- _tmpl$26 = ["<span class=\"k2b-app-workspace__nav-tree-toggle\" data-k2b-nav-tree-toggle aria-hidden=\"true\"><i class=\"", "\"></i></span>"],
38
- _tmpl$27 = ["<div class=\"k2b-app-workspace__nav-tree-group\" role=\"group\">", "</div>"],
39
- _tmpl$28 = ["<div class=\"k2b-app-workspace__nav-tree-node\" role=\"treeitem\"", ">", "", "</div>"],
40
- _tmpl$29 = ["<div class=\"", "\" role=\"tree\"", ">", "</div>"],
41
- _tmpl$30 = ["<div class=\"k2b-app-workspace__sidebar-icon-grid\"", ">", "</div>"],
42
- _tmpl$31 = ["<i", " aria-hidden=\"true\"></i>"],
43
- _tmpl$32 = ["<div class=\"", "\" data-k2b-app-workspace", " style=\"", "\">", "</div>"],
44
- _tmpl$33 = ["<div class=\"", "\">", "</div>"];
34
+ _tmpl$23 = ["<span class=\"k2b-app-workspace__sidebar-item-label\"><span class=\"k2b-app-workspace__sidebar-item-label-text\">", "</span></span>"],
35
+ _tmpl$24 = ["<span class=\"", "\"", ">", "</span>"],
36
+ _tmpl$25 = ["<span class=\"k2b-app-workspace__nav-tree-toggle\" data-k2b-nav-tree-toggle aria-hidden=\"true\"><i class=\"", "\"></i></span>"],
37
+ _tmpl$26 = ["<div class=\"k2b-app-workspace__nav-tree-group\" role=\"group\">", "</div>"],
38
+ _tmpl$27 = ["<div class=\"k2b-app-workspace__nav-tree-node\" role=\"treeitem\"", " style=\"", "\">", "", "</div>"],
39
+ _tmpl$28 = ["<div class=\"", "\" role=\"tree\"", ">", "</div>"],
40
+ _tmpl$29 = ["<div class=\"k2b-app-workspace__sidebar-icon-grid\"", ">", "</div>"],
41
+ _tmpl$30 = ["<i", " aria-hidden=\"true\"></i>"],
42
+ _tmpl$31 = ["<div class=\"", "\" data-k2b-app-workspace", " style=\"", "\">", "</div>"],
43
+ _tmpl$32 = ["<div class=\"", "\">", "</div>"];
45
44
  import { Link } from "@k2b/ssr/nav";
46
- import { children, createContext, createMemo, createSignal, createUniqueId, For, mergeProps, onCleanup, onMount, Show, useContext } from "solid-js";
45
+ import { children, createContext, createEffect, createMemo, createSignal, createUniqueId, For, mergeProps, onCleanup, onMount, Show, useContext } from "solid-js";
47
46
  import { useUiMessages } from "../intl/messages";
48
47
  import { APP_WORKSPACE_SIDEBAR_HIDE_EVENT, installAppWorkspaceController } from "./app-workspace-controller";
49
48
  import { APP_WORKSPACE_DETAIL_DEFAULT, APP_WORKSPACE_DETAIL_MAX, APP_WORKSPACE_DETAIL_MIN, APP_WORKSPACE_DRAWER_DEFAULT, APP_WORKSPACE_DRAWER_MAX, APP_WORKSPACE_DRAWER_MIN, APP_WORKSPACE_PANE_DEFAULT, APP_WORKSPACE_PANE_MAX, APP_WORKSPACE_PANE_MIN, APP_WORKSPACE_SIDEBAR_COLLAPSED, APP_WORKSPACE_SIDEBAR_DEFAULT, APP_WORKSPACE_SIDEBAR_MAX, APP_WORKSPACE_SIDEBAR_MIN, appWorkspaceLayoutStyle, appWorkspacePanelVariable } from "./app-workspace-state";
@@ -72,8 +71,12 @@ const sidebarChildSlot = value => Boolean(value && typeof value === "object" &&
72
71
  const sidebarItemSlot = value => Boolean(value && typeof value === "object" && "kind" in value && [SIDEBAR_ITEM_ICON, SIDEBAR_ITEM_LABEL, SIDEBAR_ITEM_META, SIDEBAR_ITEM_ACTION].includes(value.kind));
73
72
  const navTreeItemSlot = value => Boolean(value && typeof value === "object" && "kind" in value && value.kind === NAV_TREE_ITEM);
74
73
  function ResizeHandle(props) {
75
- return _$ssr(_tmpl$, _$ssrAttribute("aria-label", _$escape(props.label ?? (props.kind === "sidebar" ? "Resize navigation" : props.kind === "pane" ? "Resize workspace pane" : props.kind === "detail" ? "Resize detail panel" : "Resize bottom drawer"), true), false) + _$ssrAttribute("aria-controls", _$escape(props.controls, true), false) + _$ssrAttribute("aria-orientation", props.kind === "drawer" ? "horizontal" : "vertical", false) + _$ssrAttribute("aria-valuemin", _$escape(props.minSize, true), false) + _$ssrAttribute("aria-valuemax", _$escape(props.maxSize, true), false) + _$ssrAttribute("aria-valuenow", _$escape(props.defaultSize, true), false) + _$ssrAttribute("data-app-workspace-resize", _$escape(props.kind, true), false) + _$ssrAttribute("data-workspace-panel-id", _$escape(props.panelId, true), false) + _$ssrAttribute("data-workspace-resize-edge", _$escape(props.edge, true), false) + _$ssrAttribute("data-workspace-resize-shadow", props.shadow ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-workspace-default-size", _$escape(props.defaultSize, true), false) + _$ssrAttribute("data-workspace-min-size", _$escape(props.minSize, true), false) + _$ssrAttribute("data-workspace-max-size", _$escape(props.maxSize, true), false), _$ssrStyle(props.style));
74
+ const messages = useUiMessages();
75
+ return _$ssr(_tmpl$, _$ssrAttribute("aria-label", _$escape(props.label ?? (props.kind === "sidebar" ? messages().resizeNavigation : props.kind === "pane" ? messages().resizeWorkspacePane : props.kind === "detail" ? messages().resizeDetailPanel : messages().resizeBottomDrawer), true), false) + _$ssrAttribute("aria-controls", _$escape(props.controls, true), false) + _$ssrAttribute("aria-orientation", props.kind === "drawer" ? "horizontal" : "vertical", false) + _$ssrAttribute("aria-valuemin", _$escape(props.minSize, true), false) + _$ssrAttribute("aria-valuemax", _$escape(props.maxSize, true), false) + _$ssrAttribute("aria-valuenow", _$escape(props.defaultSize, true), false) + _$ssrAttribute("data-app-workspace-resize", _$escape(props.kind, true), false) + _$ssrAttribute("data-workspace-panel-id", _$escape(props.panelId, true), false) + _$ssrAttribute("data-workspace-resize-edge", _$escape(props.edge, true), false) + _$ssrAttribute("data-workspace-resize-shadow", props.shadow ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("data-workspace-default-size", _$escape(props.defaultSize, true), false) + _$ssrAttribute("data-workspace-min-size", _$escape(props.minSize, true), false) + _$ssrAttribute("data-workspace-max-size", _$escape(props.maxSize, true), false), _$ssrStyle(props.style));
76
76
  }
77
+
78
+ /** One requested place for an item among its unchanged siblings. */
79
+
77
80
  const modeAttrs = mode => mode && mode !== "always" ? {
78
81
  "data-sidebar-mode": mode
79
82
  } : {};
@@ -85,6 +88,13 @@ const scrollAttrs = key => key ? {
85
88
  "data-scroll-preserve": key
86
89
  } : {};
87
90
  const iconClass = (icon, fallback = "ti-circle") => icon?.startsWith("ti ") ? icon : `ti ${icon || fallback}`;
91
+ /** A named icon is an image screen readers announce; any other icon is decoration. */
92
+ const iconAccessibility = label => label ? {
93
+ role: "img",
94
+ "aria-label": label
95
+ } : {
96
+ "aria-hidden": true
97
+ };
88
98
  function AppWorkspaceMainPane(props) {
89
99
  return {
90
100
  kind: MAIN_PANE,
@@ -770,6 +780,27 @@ const AppWorkspaceNavTreeItem = props => ({
770
780
  kind: NAV_TREE_ITEM,
771
781
  ...props
772
782
  });
783
+ const NAV_TREE_DRAG_TYPE = "application/x-k2b-nav-tree-item";
784
+ const callDragHandler = (handler, event) => {
785
+ if (typeof handler === "function") handler(event);else handler?.[0](handler[1], event);
786
+ };
787
+
788
+ /**
789
+ * The move that puts the sibling at `from` into the gap before `slot`
790
+ * (`siblings.length` is the gap after the last one), or `null` when its place
791
+ * would not change or it would pass an item that is not movable.
792
+ */
793
+ const navTreeMove = (siblings, from, slot, parentId) => {
794
+ if (from < 0 || slot < 0 || slot > siblings.length || slot === from || slot === from + 1) return null;
795
+ const passed = slot < from ? siblings.slice(slot, from) : siblings.slice(from + 1, slot);
796
+ if (passed.some(sibling => !sibling.movable)) return null;
797
+ return {
798
+ id: siblings[from].id,
799
+ parentId,
800
+ beforeId: siblings[slot]?.id ?? null,
801
+ afterId: siblings[slot - 1]?.id ?? null
802
+ };
803
+ };
773
804
  const AppWorkspaceNavTree = props => {
774
805
  let root;
775
806
  const resolved = children(() => props.children);
@@ -814,16 +845,48 @@ const AppWorkspaceNavTree = props => {
814
845
  });
815
846
  };
816
847
 
848
+ // Reordering: the dragged item and the gap the pointer marks. The application renders the new
849
+ // order; the item moved by keyboard takes focus back when its row mounts or changes place.
850
+ const [dragged, setDragged] = createSignal(null);
851
+ const [dropTarget, setDropTarget] = createSignal(null);
852
+ let claimedDragOver = null;
853
+ let refocusId = null;
854
+ const endDrag = () => {
855
+ setDragged(null);
856
+ setDropTarget(null);
857
+ };
858
+ const requestMove = (move, keepFocus) => {
859
+ if (!move || !props.onMove) return;
860
+ refocusId = keepFocus ? move.id : null;
861
+ props.onMove(move);
862
+ };
863
+
817
864
  // Items arrive through an accessor so a changed nested list reconciles rows by identity instead of rebuilding the list.
818
- const renderItems = (value, depth, parentId) => _$createComponent(For, {
865
+ const renderItems = (siblings, depth, parentId) => _$createComponent(For, {
819
866
  get each() {
820
- return navTreeItems(value());
867
+ return siblings();
821
868
  },
822
- children: item => {
869
+ children: (item, place) => {
823
870
  const nested = createMemo(() => navTreeItems(item.children));
824
871
  const hasChildren = () => nested().length > 0;
825
872
  const selected = () => props.selectedId === item.id;
873
+ const siblingIndex = id => siblings().findIndex(sibling => sibling.id === id);
874
+ const movable = () => Boolean(props.onMove && item.movable && !item.disabled);
826
875
  let treeItem;
876
+ const ownRow = () => treeItem?.firstElementChild;
877
+
878
+ // A new row for the moved item, or the same row at its new place: moving a focused row drops its focus.
879
+ createEffect(() => {
880
+ place();
881
+ if (refocusId !== item.id) return;
882
+ queueMicrotask(() => {
883
+ if (refocusId !== item.id || !treeItem?.isConnected) return;
884
+ const active = treeItem.ownerDocument.activeElement;
885
+ if (!active || active === treeItem.ownerDocument.body) treeItem.focus({
886
+ preventScroll: true
887
+ });
888
+ });
889
+ });
827
890
  const activate = event => {
828
891
  if (item.disabled) return;
829
892
  treeItem?.focus({
@@ -856,6 +919,13 @@ const AppWorkspaceNavTree = props => {
856
919
  };
857
920
  const onKeyDown = event => {
858
921
  if (event.target !== event.currentTarget) return;
922
+ if (event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey && (event.key === "ArrowUp" || event.key === "ArrowDown")) {
923
+ if (!movable()) return;
924
+ event.preventDefault();
925
+ const from = siblingIndex(item.id);
926
+ requestMove(navTreeMove(siblings(), from, event.key === "ArrowUp" ? from - 1 : from + 2, parentId ?? null), true);
927
+ return;
928
+ }
859
929
  if (event.altKey || event.ctrlKey || event.metaKey) return;
860
930
  const items = treeItemElements();
861
931
  const index = items.indexOf(event.currentTarget);
@@ -909,21 +979,26 @@ const AppWorkspaceNavTree = props => {
909
979
  return rowIcon();
910
980
  },
911
981
  get children() {
912
- return _$ssr(_tmpl$23, _$ssrAttribute("data-k2b-nav-tree-toggle", usesIconDisclosure() ? "" : _$escape(undefined, true), false), _$ssrAttribute("class", _$escape(iconClass(rowIcon()), true), false));
982
+ return _$ssrElement("span", _$mergeProps({
983
+ "class": "k2b-app-workspace__sidebar-item-icon",
984
+ get ["data-k2b-nav-tree-toggle"]() {
985
+ return usesIconDisclosure() ? "" : undefined;
986
+ }
987
+ }, () => iconAccessibility(item.iconLabel)), _$ssr(_tmpl$19, _$ssrAttribute("class", _$escape(iconClass(rowIcon()), true), false)), false);
913
988
  }
914
- }), _$ssr(_tmpl$24, _$escape(item.label)), _$createComponent(Show, {
989
+ }), _$ssr(_tmpl$23, _$escape(item.label)), _$createComponent(Show, {
915
990
  get when() {
916
991
  return item.meta !== undefined;
917
992
  },
918
993
  get children() {
919
- return _$ssr(_tmpl$25, `k2b-app-workspace__sidebar-item-meta ${hasChildren() ? "" : "k2b-app-workspace__nav-tree-leaf-meta"}`, _$ssrAttribute("data-visibility", item.metaVisibility === "hover" ? "hover" : _$escape(undefined, true), false), _$escape(item.meta));
994
+ return _$ssr(_tmpl$24, `k2b-app-workspace__sidebar-item-meta ${hasChildren() ? "" : "k2b-app-workspace__nav-tree-leaf-meta"}`, _$ssrAttribute("data-visibility", item.metaVisibility === "hover" ? "hover" : _$escape(undefined, true), false), _$escape(item.meta));
920
995
  }
921
996
  }), _$createComponent(Show, {
922
997
  get when() {
923
998
  return _$memo(() => !!hasChildren())() && !usesIconDisclosure();
924
999
  },
925
1000
  get children() {
926
- return _$ssr(_tmpl$26, `ti ${isExpanded(item.id) ? "ti-chevron-down" : "ti-chevron-right"}`);
1001
+ return _$ssr(_tmpl$25, `ti ${isExpanded(item.id) ? "ti-chevron-down" : "ti-chevron-right"}`);
927
1002
  }
928
1003
  })];
929
1004
  const row = () => _$createComponent(AppWorkspaceSidebarRow, {
@@ -978,17 +1053,88 @@ const AppWorkspaceNavTree = props => {
978
1053
  },
979
1054
  children: rowContent
980
1055
  });
981
- return _$ssr(_tmpl$28, _$ssrAttribute("aria-level", _$escape(depth, true) + 1, false) + _$ssrAttribute("aria-selected", _$escape(selected(), true), false) + _$ssrAttribute("aria-expanded", hasChildren() ? _$escape(isExpanded(item.id), true) : _$escape(undefined, true), false) + _$ssrAttribute("aria-disabled", item.disabled ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("tabindex", item.disabled ? -1 : tabStopId() === item.id ? 0 : -1, false) + _$ssrAttribute("data-k2b-nav-tree-id", _$escape(item.id, true), false) + _$ssrAttribute("data-k2b-nav-tree-parent-id", _$escape(parentId, true), false), _$escape(row()), _$escape(_$createComponent(Show, {
1056
+
1057
+ // A gap is offered only next to a sibling's own row, never over its sub-items.
1058
+ const dropMove = event => {
1059
+ const drag = dragged();
1060
+ const row = ownRow();
1061
+ if (!drag || !event.dataTransfer?.types.includes(NAV_TREE_DRAG_TYPE)) return null;
1062
+ if (drag.parentId !== (parentId ?? null) || drag.id === item.id || !row?.contains(event.target)) return null;
1063
+ const rect = row.getBoundingClientRect();
1064
+ const side = event.clientY < rect.top + rect.height / 2 ? "before" : "after";
1065
+ const slot = siblingIndex(item.id) + (side === "after" ? 1 : 0);
1066
+ const move = navTreeMove(siblings(), siblingIndex(drag.id), slot, parentId ?? null);
1067
+ return move ? {
1068
+ side,
1069
+ move
1070
+ } : null;
1071
+ };
1072
+ const onDragStart = event => {
1073
+ const row = ownRow();
1074
+ if (!movable() || !row?.contains(event.target)) return;
1075
+ event.stopPropagation();
1076
+ setDragged({
1077
+ id: item.id,
1078
+ parentId: parentId ?? null
1079
+ });
1080
+ if (!event.dataTransfer) return;
1081
+ event.dataTransfer.effectAllowed = "move";
1082
+ event.dataTransfer.setData(NAV_TREE_DRAG_TYPE, item.id);
1083
+ const rect = row.getBoundingClientRect();
1084
+ event.dataTransfer.setDragImage(row, event.clientX - rect.left, event.clientY - rect.top);
1085
+ };
1086
+ const onDragOver = event => {
1087
+ callDragHandler(item.onDragOver, event);
1088
+ const target = dropMove(event);
1089
+ if (!target) return;
1090
+ event.preventDefault();
1091
+ if (event.dataTransfer) event.dataTransfer.dropEffect = "move";
1092
+ claimedDragOver = event;
1093
+ setDropTarget({
1094
+ id: item.id,
1095
+ side: target.side
1096
+ });
1097
+ };
1098
+ const onDrop = event => {
1099
+ callDragHandler(item.onDrop, event);
1100
+ const target = dropMove(event);
1101
+ if (!target) return;
1102
+ event.preventDefault();
1103
+ event.stopPropagation();
1104
+ endDrag();
1105
+ requestMove(target.move, false);
1106
+ };
1107
+ return _$ssr(_tmpl$27, _$ssrAttribute("aria-level", _$escape(depth, true) + 1, false) + _$ssrAttribute("aria-setsize", _$escape(siblings().length, true), false) + _$ssrAttribute("aria-posinset", _$escape(place(), true) + 1, false) + _$ssrAttribute("aria-selected", _$escape(selected(), true), false) + _$ssrAttribute("aria-expanded", hasChildren() ? _$escape(isExpanded(item.id), true) : _$escape(undefined, true), false) + _$ssrAttribute("aria-disabled", item.disabled ? "true" : _$escape(undefined, true), false) + _$ssrAttribute("tabindex", item.disabled ? -1 : tabStopId() === item.id ? 0 : -1, false) + _$ssrAttribute("draggable", movable() ? _$escape(true, true) : _$escape(undefined, true), false) + _$ssrAttribute("data-k2b-nav-tree-id", _$escape(item.id, true), false) + _$ssrAttribute("data-k2b-nav-tree-parent-id", _$escape(parentId, true), false) + _$ssrAttribute("data-k2b-nav-tree-dragging", dragged()?.id === item.id ? "" : _$escape(undefined, true), false) + _$ssrAttribute("data-k2b-nav-tree-drop", dropTarget()?.id === item.id ? _$escape(dropTarget().side, true) : _$escape(undefined, true), false), _$ssrStyleProperty("--k2b-nav-tree-depth:", _$escape(String(props.indented === false ? 0 : depth), true)), _$escape(row()), _$escape(_$createComponent(Show, {
982
1108
  get when() {
983
1109
  return _$memo(() => !!hasChildren())() && isExpanded(item.id);
984
1110
  },
985
1111
  get children() {
986
- return _$ssr(_tmpl$27, _$escape(renderItems(nested, depth + 1, item.id)));
1112
+ return _$ssr(_tmpl$26, _$escape(renderItems(nested, depth + 1, item.id)));
987
1113
  }
988
1114
  })));
989
1115
  }
990
1116
  });
991
- return _$ssr(_tmpl$29, `k2b-app-workspace__nav-tree ${_$escape(props.class ?? "", true)}`, _$ssrAttribute("aria-label", _$escape(props.ariaLabel, true), false), _$escape(renderItems(roots, 0)));
1117
+
1118
+ // Any other click or focus is the person's choice; only the moved item itself keeps its claim.
1119
+ const forgetRefocus = event => {
1120
+ if (event.type === "pointerdown" || event.target?.dataset?.k2bNavTreeId !== refocusId) refocusId = null;
1121
+ };
1122
+ // A row replaced during a drag never receives its dragend; the next pointer movement means the drag is over.
1123
+ const forgetDrag = () => {
1124
+ if (dragged()) endDrag();
1125
+ };
1126
+ onMount(() => {
1127
+ const doc = root?.ownerDocument;
1128
+ doc?.addEventListener("pointerdown", forgetRefocus, true);
1129
+ doc?.addEventListener("focusin", forgetRefocus, true);
1130
+ doc?.addEventListener("pointermove", forgetDrag, true);
1131
+ onCleanup(() => {
1132
+ doc?.removeEventListener("pointerdown", forgetRefocus, true);
1133
+ doc?.removeEventListener("focusin", forgetRefocus, true);
1134
+ doc?.removeEventListener("pointermove", forgetDrag, true);
1135
+ });
1136
+ });
1137
+ return _$ssr(_tmpl$28, `k2b-app-workspace__nav-tree ${_$escape(props.class ?? "", true)}`, _$ssrAttribute("aria-label", _$escape(props.ariaLabel, true), false), _$escape(renderItems(roots, 0)));
992
1138
  };
993
1139
  AppWorkspaceNavTree.Item = AppWorkspaceNavTreeItem;
994
1140
  const AppWorkspaceSidebarIconGrid = props => _$ssrElement("section", _$mergeProps({
@@ -1000,9 +1146,9 @@ const AppWorkspaceSidebarIconGrid = props => _$ssrElement("section", _$mergeProp
1000
1146
  return props.title;
1001
1147
  },
1002
1148
  children: title => _$ssr(_tmpl$12, _$escape(title()))
1003
- })), _$ssr(_tmpl$30, _$ssrAttribute("data-columns", _$escape(props.columns ?? 2, true), false), _$escape(props.children))], false);
1149
+ })), _$ssr(_tmpl$29, _$ssrAttribute("data-columns", _$escape(props.columns ?? 2, true), false), _$escape(props.children))], false);
1004
1150
  const AppWorkspaceSidebarIconAction = props => {
1005
- const content = _$ssr(_tmpl$31, _$ssrAttribute("class", _$escape(iconClass(props.icon), true), false));
1151
+ const content = _$ssr(_tmpl$30, _$ssrAttribute("class", _$escape(iconClass(props.icon), true), false));
1006
1152
  const attrs = () => ({
1007
1153
  class: `k2b-app-workspace__sidebar-icon-action ${props.active ? "is-active" : ""}`,
1008
1154
  title: props.label,
@@ -1068,7 +1214,7 @@ const AppWorkspace = props => {
1068
1214
  return props.resizable !== false;
1069
1215
  },
1070
1216
  get children() {
1071
- return _$ssr(_tmpl$32, `k2b-app-workspace ${_$escape(props.class ?? "", true)}`, _$ssrAttribute("data-mobile-surface", _$escape(props.mobileSurface, true), false) + _$ssrAttribute("data-sidebar-collapsed", initialLayoutState()?.sidebarCollapsed === undefined ? _$escape(undefined, true) : _$escape(String(initialLayoutState()?.sidebarCollapsed), true), false) + _$ssrAttribute("data-workspace-resizable", props.resizable === false ? "false" : "true", false), _$ssrStyle(appWorkspaceLayoutStyle(initialLayoutState())), _$escape(props.children));
1217
+ return _$ssr(_tmpl$31, `k2b-app-workspace ${_$escape(props.class ?? "", true)}`, _$ssrAttribute("data-mobile-surface", _$escape(props.mobileSurface, true), false) + _$ssrAttribute("data-sidebar-collapsed", initialLayoutState()?.sidebarCollapsed === undefined ? _$escape(undefined, true) : _$escape(String(initialLayoutState()?.sidebarCollapsed), true), false) + _$ssrAttribute("data-workspace-resizable", props.resizable === false ? "false" : "true", false), _$ssrStyle(appWorkspaceLayoutStyle(initialLayoutState())), _$escape(props.children));
1072
1218
  }
1073
1219
  });
1074
1220
  };
@@ -1080,7 +1226,7 @@ AppWorkspace.LayoutStateProvider = props => _$createComponent(LayoutStateContext
1080
1226
  return props.children;
1081
1227
  }
1082
1228
  });
1083
- AppWorkspace.Content = props => _$ssr(_tmpl$33, `k2b-app-workspace__content ${_$escape(props.class ?? "", true)}`, _$escape(props.children));
1229
+ AppWorkspace.Content = props => _$ssr(_tmpl$32, `k2b-app-workspace__content ${_$escape(props.class ?? "", true)}`, _$escape(props.children));
1084
1230
  AppWorkspace.Main = AppWorkspaceMain;
1085
1231
  AppWorkspace.MainPane = AppWorkspaceMainPane;
1086
1232
  AppWorkspace.Detail = AppWorkspaceDetail;