@almadar/ui 6.7.0 → 6.9.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.
@@ -3387,8 +3387,17 @@ var init_Typography = __esm({
3387
3387
  weight && weightStyles[weight],
3388
3388
  size && typographySizeStyles[size],
3389
3389
  align && `text-${align}`,
3390
- truncate && "truncate overflow-hidden text-ellipsis",
3391
- overflow && overflowStyles[overflow],
3390
+ // `min-w-0` rides along with truncate/wrap/clamp: a flex or grid
3391
+ // item's default `min-width: auto` refuses to shrink below its
3392
+ // content's intrinsic width, so ellipsis/wrap silently does nothing
3393
+ // in the single most common placement (a row next to a fixed-width
3394
+ // control) unless the item can also shrink to zero. (Spelled out as
3395
+ // just "truncate", not "truncate overflow-hidden text-ellipsis" —
3396
+ // tailwind-merge treats those as the same conflict group and drops
3397
+ // "truncate" as the earlier-declared class, silently losing its
3398
+ // `white-space: nowrap`.)
3399
+ truncate && "truncate min-w-0",
3400
+ overflow && cn(overflowStyles[overflow], overflow !== "visible" && "min-w-0"),
3392
3401
  className
3393
3402
  ),
3394
3403
  style
@@ -5254,7 +5263,7 @@ var init_Button = __esm({
5254
5263
  secondary: [
5255
5264
  "bg-transparent text-accent",
5256
5265
  "border border-accent",
5257
- "hover:bg-accent hover:text-white hover:border-accent",
5266
+ "hover:bg-accent hover:text-accent-foreground hover:border-accent",
5258
5267
  "active:scale-[var(--active-scale)]"
5259
5268
  ].join(" "),
5260
5269
  ghost: [
@@ -13782,6 +13791,10 @@ var init_paintDispatch = __esm({
13782
13791
  });
13783
13792
 
13784
13793
  // lib/drawable/hitTest.ts
13794
+ function shapeDrawnItem(n) {
13795
+ if (n.shape !== "rect") return { pos: n.position, id: n.id };
13796
+ return { pos: n.position, id: n.id, anchor: n.anchor, width: n.width, height: n.height, rotation: n.rotate };
13797
+ }
13785
13798
  function collectDrawnItems(nodes) {
13786
13799
  const out = [];
13787
13800
  for (const n of nodes) {
@@ -13790,6 +13803,8 @@ function collectDrawnItems(nodes) {
13790
13803
  if (isValidScenePos(n.position)) out.push({ pos: n.position, id: n.id, anchor: n.anchor, width: n.width, height: n.height, rotation: n.rotation });
13791
13804
  break;
13792
13805
  case "draw-shape":
13806
+ if (isValidScenePos(n.position)) out.push(shapeDrawnItem(n));
13807
+ break;
13793
13808
  case "draw-text":
13794
13809
  case "draw-group":
13795
13810
  case "draw-mesh":
@@ -13801,6 +13816,10 @@ function collectDrawnItems(nodes) {
13801
13816
  }
13802
13817
  break;
13803
13818
  case "draw-shape-layer":
13819
+ for (const it of n.items) {
13820
+ if (isValidScenePos(it.position)) out.push(shapeDrawnItem(it));
13821
+ }
13822
+ break;
13804
13823
  case "draw-text-layer":
13805
13824
  for (const it of n.items) {
13806
13825
  if (isValidScenePos(it.position)) out.push({ pos: it.position, id: it.id });
@@ -20105,6 +20124,12 @@ var init_EmptyState = __esm({
20105
20124
  });
20106
20125
 
20107
20126
  // lib/editorMotions.ts
20127
+ function isEditorMotion(value) {
20128
+ return EDITOR_MOTION_SET.has(value);
20129
+ }
20130
+ function isEditorOperator(value) {
20131
+ return EDITOR_OPERATOR_SET.has(value);
20132
+ }
20108
20133
  function clamp(value, min, max) {
20109
20134
  return Math.max(min, Math.min(max, value));
20110
20135
  }
@@ -20172,6 +20197,30 @@ function prevParagraphBoundary(lines, fromLineIdx) {
20172
20197
  }
20173
20198
  return 0;
20174
20199
  }
20200
+ function matchBracket(text, pos) {
20201
+ const ch = text[pos];
20202
+ const partner = ch !== void 0 ? BRACKET_PARTNER[ch] : void 0;
20203
+ if (partner === void 0) return null;
20204
+ let depth = 1;
20205
+ if (OPEN_BRACKETS.has(ch)) {
20206
+ for (let i = pos + 1; i < text.length; i++) {
20207
+ if (text[i] === ch) depth++;
20208
+ else if (text[i] === partner) {
20209
+ depth--;
20210
+ if (depth === 0) return i;
20211
+ }
20212
+ }
20213
+ } else {
20214
+ for (let i = pos - 1; i >= 0; i--) {
20215
+ if (text[i] === ch) depth++;
20216
+ else if (text[i] === partner) {
20217
+ depth--;
20218
+ if (depth === 0) return i;
20219
+ }
20220
+ }
20221
+ }
20222
+ return null;
20223
+ }
20175
20224
  function applyMotion(text, caret, motion, count) {
20176
20225
  const n = Math.max(1, count);
20177
20226
  const lines = computeLines(text);
@@ -20233,6 +20282,20 @@ function applyMotion(text, caret, motion, count) {
20233
20282
  }
20234
20283
  return pos;
20235
20284
  }
20285
+ case "match-bracket": {
20286
+ const chAtCaret = text[caret];
20287
+ if (chAtCaret !== void 0 && BRACKET_PARTNER[chAtCaret] !== void 0) {
20288
+ const target = matchBracket(text, caret);
20289
+ return target === null ? caret : target;
20290
+ }
20291
+ for (let i = caret; i < line.end; i++) {
20292
+ if (BRACKET_PARTNER[text[i]] !== void 0) {
20293
+ const target = matchBracket(text, i);
20294
+ return target === null ? caret : target;
20295
+ }
20296
+ }
20297
+ return caret;
20298
+ }
20236
20299
  case "line":
20237
20300
  case "selection":
20238
20301
  return caret;
@@ -20260,8 +20323,8 @@ function motionRange(text, caret, motion, count, selection) {
20260
20323
  const newCaret = applyMotion(text, caret, motion, count);
20261
20324
  let start = Math.min(caret, newCaret);
20262
20325
  let end = Math.max(caret, newCaret);
20263
- if (motion === "word-end" || motion === "line-end") {
20264
- end = Math.min(Math.max(start, newCaret) + 1, text.length);
20326
+ if ((motion === "word-end" || motion === "line-end" || motion === "match-bracket") && newCaret !== caret) {
20327
+ end = Math.min(Math.max(caret, newCaret) + 1, text.length);
20265
20328
  } else if (motion === "word-forward" && newCaret > caret) {
20266
20329
  const startLine = lineIndexAt(lines, caret);
20267
20330
  const endLine = lineIndexAt(lines, newCaret);
@@ -20271,17 +20334,198 @@ function motionRange(text, caret, motion, count, selection) {
20271
20334
  }
20272
20335
  return [start, end];
20273
20336
  }
20274
- function applyOperator(text, range, operator, register) {
20275
- const start = clamp(range[0], 0, text.length);
20276
- const end = clamp(range[1], start, text.length);
20277
- const removed = text.slice(start, end);
20278
- if (operator === "yank") {
20279
- return { text, caret: start, register: removed };
20337
+ function toggleCase(s) {
20338
+ return s.replace(/[a-zA-Z]/g, (c) => c === c.toUpperCase() ? c.toLowerCase() : c.toUpperCase());
20339
+ }
20340
+ function applyJoin(text, caret, count, register, registerLinewise) {
20341
+ const lines = computeLines(text);
20342
+ const startIdx = lineIndexAt(lines, caret);
20343
+ const n = Math.max(2, count);
20344
+ const endIdx = clamp(startIdx + n - 1, 0, lines.length - 1);
20345
+ if (endIdx <= startIdx) {
20346
+ return { text, caret, register, registerLinewise };
20347
+ }
20348
+ const joinCaret = lines[startIdx].end;
20349
+ let joined = text.slice(lines[startIdx].start, lines[startIdx].end);
20350
+ for (let i = startIdx + 1; i <= endIdx; i++) {
20351
+ const raw = text.slice(lines[i].start, lines[i].end);
20352
+ joined += " " + raw.replace(/^[ \t]+/, "");
20353
+ }
20354
+ const newText = text.slice(0, lines[startIdx].start) + joined + text.slice(lines[endIdx].end);
20355
+ return { text: newText, caret: joinCaret, register, registerLinewise };
20356
+ }
20357
+ function applyIndent(text, start, end, operator, register, registerLinewise) {
20358
+ const lines = computeLines(text);
20359
+ const startLineIdx = lineIndexAt(lines, start);
20360
+ const lastTouchedPos = end > start ? end - 1 : start;
20361
+ const endLineIdx = lineIndexAt(lines, lastTouchedPos);
20362
+ let result = text;
20363
+ for (let i = endLineIdx; i >= startLineIdx; i--) {
20364
+ const lineStart = lines[i].start;
20365
+ if (operator === "indent") {
20366
+ result = result.slice(0, lineStart) + INDENT_UNIT + result.slice(lineStart);
20367
+ } else if (result[lineStart] === " ") {
20368
+ result = result.slice(0, lineStart) + result.slice(lineStart + 1);
20369
+ } else {
20370
+ let removeCount = 0;
20371
+ while (removeCount < INDENT_UNIT.length && result[lineStart + removeCount] === " ") removeCount++;
20372
+ result = result.slice(0, lineStart) + result.slice(lineStart + removeCount);
20373
+ }
20374
+ }
20375
+ const newLines = computeLines(result);
20376
+ const caret = firstNonBlank(result, newLines[startLineIdx]);
20377
+ return { text: result, caret, register, registerLinewise };
20378
+ }
20379
+ function applyPut(text, caret, operator, register, registerLinewise, count) {
20380
+ if (register.length === 0) {
20381
+ return { text, caret, register, registerLinewise };
20382
+ }
20383
+ const content = register.repeat(count);
20384
+ const lines = computeLines(text);
20385
+ const line = lines[lineIndexAt(lines, caret)];
20386
+ if (registerLinewise) {
20387
+ if (operator === "put-before") {
20388
+ const insertPos3 = line.start;
20389
+ return {
20390
+ text: text.slice(0, insertPos3) + content + text.slice(insertPos3),
20391
+ caret: insertPos3,
20392
+ register,
20393
+ registerLinewise
20394
+ };
20395
+ }
20396
+ const nextLineIdx = lineIndexAt(lines, caret) + 1;
20397
+ if (nextLineIdx < lines.length) {
20398
+ const insertPos3 = lines[nextLineIdx].start;
20399
+ return {
20400
+ text: text.slice(0, insertPos3) + content + text.slice(insertPos3),
20401
+ caret: insertPos3,
20402
+ register,
20403
+ registerLinewise
20404
+ };
20405
+ }
20406
+ const insertPos2 = text.length;
20407
+ return {
20408
+ text: text.slice(0, insertPos2) + "\n" + content,
20409
+ caret: insertPos2 + 1,
20410
+ register,
20411
+ registerLinewise
20412
+ };
20413
+ }
20414
+ const insertPos = operator === "put-before" ? clamp(caret, 0, text.length) : clamp(caret + 1, line.start, line.end);
20415
+ return {
20416
+ text: text.slice(0, insertPos) + content + text.slice(insertPos),
20417
+ caret: insertPos + content.length - 1,
20418
+ register,
20419
+ registerLinewise
20420
+ };
20421
+ }
20422
+ function applyOperator(input) {
20423
+ const { text, caret, operator, motion, register, registerLinewise } = input;
20424
+ const count = Math.max(1, input.count);
20425
+ const start = clamp(input.range[0], 0, text.length);
20426
+ const end = clamp(input.range[1], start, text.length);
20427
+ switch (operator) {
20428
+ case "yank":
20429
+ return { text, caret: start, register: text.slice(start, end), registerLinewise: motion === "line" };
20430
+ case "delete":
20431
+ case "replace": {
20432
+ const removed = text.slice(start, end);
20433
+ return {
20434
+ text: text.slice(0, start) + text.slice(end),
20435
+ caret: start,
20436
+ register: removed,
20437
+ registerLinewise: motion === "line"
20438
+ };
20439
+ }
20440
+ case "change": {
20441
+ if (motion === "line") {
20442
+ const hasTrailingNewline = end > start && text[end - 1] === "\n";
20443
+ const removeEnd = hasTrailingNewline ? end - 1 : end;
20444
+ const removed2 = text.slice(start, removeEnd);
20445
+ return {
20446
+ text: text.slice(0, start) + text.slice(removeEnd),
20447
+ caret: start,
20448
+ register: removed2,
20449
+ registerLinewise: true
20450
+ };
20451
+ }
20452
+ const removed = text.slice(start, end);
20453
+ return {
20454
+ text: text.slice(0, start) + text.slice(end),
20455
+ caret: start,
20456
+ register: removed,
20457
+ registerLinewise: false
20458
+ };
20459
+ }
20460
+ case "put":
20461
+ case "put-before":
20462
+ return applyPut(text, caret, operator, register, registerLinewise, count);
20463
+ case "join":
20464
+ return applyJoin(text, caret, count, register, registerLinewise);
20465
+ case "toggle-case": {
20466
+ const toggled = toggleCase(text.slice(start, end));
20467
+ return { text: text.slice(0, start) + toggled + text.slice(end), caret: end, register, registerLinewise };
20468
+ }
20469
+ case "indent":
20470
+ case "dedent":
20471
+ return applyIndent(text, start, end, operator, register, registerLinewise);
20472
+ case "undo":
20473
+ case "redo":
20474
+ return { text, caret, register, registerLinewise };
20475
+ default: {
20476
+ const _exhaustive = operator;
20477
+ return _exhaustive;
20478
+ }
20280
20479
  }
20281
- return { text: text.slice(0, start) + text.slice(end), caret: start, register: removed };
20282
20480
  }
20481
+ var EDITOR_MOTIONS, EDITOR_OPERATORS, EDITOR_MOTION_SET, EDITOR_OPERATOR_SET, INDENT_UNIT, BRACKET_PARTNER, OPEN_BRACKETS;
20283
20482
  var init_editorMotions = __esm({
20284
20483
  "lib/editorMotions.ts"() {
20484
+ EDITOR_MOTIONS = [
20485
+ "left",
20486
+ "right",
20487
+ "up",
20488
+ "down",
20489
+ "word-forward",
20490
+ "word-back",
20491
+ "word-end",
20492
+ "line-start",
20493
+ "line-end",
20494
+ "first-nonblank",
20495
+ "doc-start",
20496
+ "doc-end",
20497
+ "paragraph-forward",
20498
+ "paragraph-back",
20499
+ "line",
20500
+ "selection",
20501
+ "match-bracket"
20502
+ ];
20503
+ EDITOR_OPERATORS = [
20504
+ "delete",
20505
+ "yank",
20506
+ "change",
20507
+ "put",
20508
+ "put-before",
20509
+ "undo",
20510
+ "redo",
20511
+ "join",
20512
+ "toggle-case",
20513
+ "indent",
20514
+ "dedent",
20515
+ "replace"
20516
+ ];
20517
+ EDITOR_MOTION_SET = new Set(EDITOR_MOTIONS);
20518
+ EDITOR_OPERATOR_SET = new Set(EDITOR_OPERATORS);
20519
+ INDENT_UNIT = " ";
20520
+ BRACKET_PARTNER = {
20521
+ "(": ")",
20522
+ ")": "(",
20523
+ "[": "]",
20524
+ "]": "[",
20525
+ "{": "}",
20526
+ "}": "{"
20527
+ };
20528
+ OPEN_BRACKETS = /* @__PURE__ */ new Set(["(", "[", "{"]);
20285
20529
  }
20286
20530
  });
20287
20531
  function isMotionPayload(payload) {
@@ -20296,14 +20540,103 @@ function isInsertTextPayload(payload) {
20296
20540
  function isSetModePayload(payload) {
20297
20541
  return !!payload && typeof payload.editorId === "string" && typeof payload.mode === "string" && typeof payload.caret === "string";
20298
20542
  }
20543
+ function typedDelta(prev, next) {
20544
+ const maxPrefix = Math.min(prev.length, next.length);
20545
+ let prefixLen = 0;
20546
+ while (prefixLen < maxPrefix && prev[prefixLen] === next[prefixLen]) prefixLen++;
20547
+ const maxSuffix = Math.min(prev.length - prefixLen, next.length - prefixLen);
20548
+ let suffixLen = 0;
20549
+ while (suffixLen < maxSuffix && prev[prev.length - 1 - suffixLen] === next[next.length - 1 - suffixLen]) {
20550
+ suffixLen++;
20551
+ }
20552
+ const removed = prev.slice(prefixLen, prev.length - suffixLen);
20553
+ const inserted = next.slice(prefixLen, next.length - suffixLen);
20554
+ return removed + inserted;
20555
+ }
20299
20556
  function useEditorCapabilities(args) {
20300
20557
  const [caretMode, setCaretMode] = React96.useState("bar");
20301
20558
  const registerRef = React96.useRef("");
20559
+ const registerLinewiseRef = React96.useRef(false);
20560
+ const pastRef = React96.useRef([]);
20561
+ const futureRef = React96.useRef([]);
20562
+ const openTypingStepRef = React96.useRef(false);
20563
+ const insertSessionOpenRef = React96.useRef(false);
20564
+ const closeOpenTypingStep = React96.useCallback(() => {
20565
+ openTypingStepRef.current = false;
20566
+ }, []);
20567
+ const pushHistoryStep = React96.useCallback(
20568
+ (text, caret) => {
20569
+ closeOpenTypingStep();
20570
+ pastRef.current.push({ text, caret });
20571
+ futureRef.current = [];
20572
+ },
20573
+ [closeOpenTypingStep]
20574
+ );
20575
+ const recordKeystroke = React96.useCallback(
20576
+ (prevText, prevCaret, nextText) => {
20577
+ if (!openTypingStepRef.current) {
20578
+ pastRef.current.push({ text: prevText, caret: prevCaret });
20579
+ futureRef.current = [];
20580
+ openTypingStepRef.current = true;
20581
+ }
20582
+ if (!insertSessionOpenRef.current && /\s/.test(typedDelta(prevText, nextText))) {
20583
+ openTypingStepRef.current = false;
20584
+ }
20585
+ },
20586
+ []
20587
+ );
20588
+ const performUndo = React96.useCallback(
20589
+ (count) => {
20590
+ const ta = args.textareaRef.current;
20591
+ if (!ta) return;
20592
+ closeOpenTypingStep();
20593
+ let moved = false;
20594
+ for (let i = 0; i < Math.max(1, count) && pastRef.current.length > 0; i++) {
20595
+ const prev = pastRef.current.pop();
20596
+ futureRef.current.push({ text: ta.value, caret: ta.selectionStart });
20597
+ ta.value = prev.text;
20598
+ ta.setSelectionRange(prev.caret, prev.caret);
20599
+ moved = true;
20600
+ }
20601
+ if (moved) args.applyChange(ta.value, "capability");
20602
+ },
20603
+ [args, closeOpenTypingStep]
20604
+ );
20605
+ const performRedo = React96.useCallback(
20606
+ (count) => {
20607
+ const ta = args.textareaRef.current;
20608
+ if (!ta) return;
20609
+ closeOpenTypingStep();
20610
+ let moved = false;
20611
+ for (let i = 0; i < Math.max(1, count) && futureRef.current.length > 0; i++) {
20612
+ const next = futureRef.current.pop();
20613
+ pastRef.current.push({ text: ta.value, caret: ta.selectionStart });
20614
+ ta.value = next.text;
20615
+ ta.setSelectionRange(next.caret, next.caret);
20616
+ moved = true;
20617
+ }
20618
+ if (moved) args.applyChange(ta.value, "capability");
20619
+ },
20620
+ [args, closeOpenTypingStep]
20621
+ );
20622
+ const undo = React96.useCallback(() => performUndo(1), [performUndo]);
20623
+ const redo = React96.useCallback(() => performRedo(1), [performRedo]);
20624
+ const wasFocusedRef = React96.useRef(args.focused);
20625
+ React96.useEffect(() => {
20626
+ if (wasFocusedRef.current && !args.focused) {
20627
+ setCaretMode("bar");
20628
+ }
20629
+ wasFocusedRef.current = args.focused;
20630
+ }, [args.focused]);
20302
20631
  useEventListener(`UI:${args.events.onMotion}`, (evt) => {
20303
20632
  if (!args.editorId || !isMotionPayload(evt.payload) || evt.payload.editorId !== args.editorId) return;
20633
+ const { motion, count } = evt.payload;
20634
+ if (!isEditorMotion(motion)) {
20635
+ console.warn(`useEditorCapabilities: ignoring MOTION with unknown motion "${motion}"`);
20636
+ return;
20637
+ }
20304
20638
  const ta = args.textareaRef.current;
20305
20639
  if (!ta) return;
20306
- const { motion, count } = evt.payload;
20307
20640
  const newCaret = applyMotion(ta.value, ta.selectionStart, motion, count);
20308
20641
  if (ta.selectionStart !== ta.selectionEnd) {
20309
20642
  ta.setSelectionRange(ta.selectionStart, newCaret);
@@ -20313,32 +20646,63 @@ function useEditorCapabilities(args) {
20313
20646
  });
20314
20647
  useEventListener(`UI:${args.events.onOperate}`, (evt) => {
20315
20648
  if (!args.editorId || !isOperatePayload(evt.payload) || evt.payload.editorId !== args.editorId) return;
20649
+ const { operator, motion, count } = evt.payload;
20650
+ if (!isEditorOperator(operator)) {
20651
+ console.warn(`useEditorCapabilities: ignoring OPERATE with unknown operator "${operator}"`);
20652
+ return;
20653
+ }
20654
+ if (!isEditorMotion(motion)) {
20655
+ console.warn(`useEditorCapabilities: ignoring OPERATE with unknown motion "${motion}"`);
20656
+ return;
20657
+ }
20316
20658
  const ta = args.textareaRef.current;
20317
20659
  if (!ta) return;
20318
- const { operator, motion, count } = evt.payload;
20660
+ if (operator === "undo") {
20661
+ performUndo(count);
20662
+ return;
20663
+ }
20664
+ if (operator === "redo") {
20665
+ performRedo(count);
20666
+ return;
20667
+ }
20668
+ pushHistoryStep(ta.value, ta.selectionStart);
20319
20669
  const selection = motion === "selection" ? [ta.selectionStart, ta.selectionEnd] : void 0;
20320
20670
  const range = motionRange(ta.value, ta.selectionStart, motion, count, selection);
20321
- const result = applyOperator(ta.value, range, operator, registerRef.current);
20671
+ const result = applyOperator({
20672
+ text: ta.value,
20673
+ caret: ta.selectionStart,
20674
+ range,
20675
+ operator,
20676
+ motion,
20677
+ count,
20678
+ register: registerRef.current,
20679
+ registerLinewise: registerLinewiseRef.current
20680
+ });
20322
20681
  registerRef.current = result.register;
20682
+ registerLinewiseRef.current = result.registerLinewise;
20323
20683
  if (operator === "yank") {
20324
- ta.setSelectionRange(range[0], range[0]);
20684
+ ta.setSelectionRange(result.caret, result.caret);
20325
20685
  } else {
20326
- ta.setRangeText("", range[0], range[1], "end");
20686
+ ta.value = result.text;
20687
+ ta.setSelectionRange(result.caret, result.caret);
20327
20688
  }
20328
- args.applyChange(ta.value);
20689
+ args.applyChange(ta.value, "capability");
20329
20690
  });
20330
20691
  useEventListener(`UI:${args.events.onInsertText}`, (evt) => {
20331
20692
  if (!args.editorId || !isInsertTextPayload(evt.payload) || evt.payload.editorId !== args.editorId) return;
20332
20693
  const ta = args.textareaRef.current;
20333
20694
  if (!ta) return;
20695
+ pushHistoryStep(ta.value, ta.selectionStart);
20334
20696
  ta.setRangeText(evt.payload.text, ta.selectionStart, ta.selectionEnd, "end");
20335
- args.applyChange(ta.value);
20697
+ args.applyChange(ta.value, "capability");
20336
20698
  });
20337
20699
  useEventListener(`UI:${args.events.onSetMode}`, (evt) => {
20338
20700
  if (!args.editorId || !isSetModePayload(evt.payload) || evt.payload.editorId !== args.editorId) return;
20701
+ closeOpenTypingStep();
20702
+ insertSessionOpenRef.current = evt.payload.mode === "INSERT";
20339
20703
  setCaretMode(evt.payload.caret);
20340
20704
  });
20341
- return { caretMode };
20705
+ return { caretMode, recordKeystroke, undo, redo };
20342
20706
  }
20343
20707
  var init_useEditorCapabilities = __esm({
20344
20708
  "components/core/molecules/markdown/useEditorCapabilities.ts"() {
@@ -20754,9 +21118,23 @@ var init_CodeBlock = __esm({
20754
21118
  "paragraph-forward",
20755
21119
  "paragraph-back",
20756
21120
  "line",
20757
- "selection"
21121
+ "selection",
21122
+ "match-bracket"
20758
21123
  ],
20759
- operators = ["delete", "yank", "change"]
21124
+ operators = [
21125
+ "delete",
21126
+ "yank",
21127
+ "change",
21128
+ "put",
21129
+ "put-before",
21130
+ "undo",
21131
+ "redo",
21132
+ "join",
21133
+ "toggle-case",
21134
+ "indent",
21135
+ "dedent",
21136
+ "replace"
21137
+ ]
20760
21138
  }) => {
20761
21139
  const code = typeof rawCode === "string" ? rawCode : String(rawCode ?? "");
20762
21140
  const activeStyle = resolveHighlightStyle(language);
@@ -20790,6 +21168,12 @@ var init_CodeBlock = __esm({
20790
21168
  const lastPropCodeRef = React96.useRef(code);
20791
21169
  const editableTextareaRef = React96.useRef(null);
20792
21170
  const editableOverlayRef = React96.useRef(null);
21171
+ const [isFocused, setIsFocused] = React96.useState(false);
21172
+ const prevCaretRef = React96.useRef(0);
21173
+ const [caretIndex, setCaretIndex] = React96.useState(0);
21174
+ const caretMirrorRef = React96.useRef(null);
21175
+ const caretMarkerRef = React96.useRef(null);
21176
+ const [caretGeometry, setCaretGeometry] = React96.useState(null);
20793
21177
  React96.useEffect(() => {
20794
21178
  if (code !== lastPropCodeRef.current) {
20795
21179
  lastPropCodeRef.current = code;
@@ -20805,23 +21189,77 @@ var init_CodeBlock = __esm({
20805
21189
  ov.scrollLeft = ta.scrollLeft;
20806
21190
  }
20807
21191
  }, []);
20808
- const handleEditableChange = React96.useCallback((v) => {
21192
+ const handleEditableChange = React96.useCallback((v, _origin) => {
20809
21193
  lastPropCodeRef.current = v;
20810
21194
  setEditableValue(v);
21195
+ const ta = editableTextareaRef.current;
21196
+ if (ta) setCaretIndex(ta.selectionStart);
20811
21197
  onChange?.(v);
20812
21198
  }, [onChange]);
20813
- const { caretMode } = useEditorCapabilities({
21199
+ const { caretMode, recordKeystroke, undo, redo } = useEditorCapabilities({
20814
21200
  editorId: editable ? editorId : void 0,
20815
21201
  textareaRef: editableTextareaRef,
20816
21202
  events: { onMotion, onOperate, onInsertText, onSetMode },
21203
+ focused: isFocused,
20817
21204
  applyChange: handleEditableChange
20818
21205
  });
20819
- const [caretIndex, setCaretIndex] = React96.useState(0);
20820
- const caretRowCol = React96.useMemo(() => {
20821
- const before = editableValue.slice(0, Math.min(caretIndex, editableValue.length));
20822
- const lines = before.split("\n");
20823
- return { row: lines.length - 1, col: lines[lines.length - 1].length };
20824
- }, [editableValue, caretIndex]);
21206
+ const handleEditableKeyDown = React96.useCallback(
21207
+ (e) => {
21208
+ const ta = editableTextareaRef.current;
21209
+ if (ta) prevCaretRef.current = ta.selectionStart;
21210
+ const mod = e.metaKey || e.ctrlKey;
21211
+ if (!mod) return;
21212
+ const key = e.key.toLowerCase();
21213
+ if (key === "z" && !e.shiftKey) {
21214
+ e.preventDefault();
21215
+ undo();
21216
+ } else if (key === "z" && e.shiftKey || key === "y") {
21217
+ e.preventDefault();
21218
+ redo();
21219
+ }
21220
+ },
21221
+ [undo, redo]
21222
+ );
21223
+ const showBlockCaret = isFocused && caretMode !== "bar";
21224
+ React96.useLayoutEffect(() => {
21225
+ if (!showBlockCaret) return;
21226
+ const ta = editableTextareaRef.current;
21227
+ const mirror = caretMirrorRef.current;
21228
+ const marker = caretMarkerRef.current;
21229
+ if (!ta || !mirror || !marker) return;
21230
+ const computed = window.getComputedStyle(ta);
21231
+ const MIRRORED_PROPS = [
21232
+ "font-family",
21233
+ "font-size",
21234
+ "font-weight",
21235
+ "font-style",
21236
+ "letter-spacing",
21237
+ "line-height",
21238
+ "padding-top",
21239
+ "padding-right",
21240
+ "padding-bottom",
21241
+ "padding-left",
21242
+ "border-top-width",
21243
+ "border-right-width",
21244
+ "border-bottom-width",
21245
+ "border-left-width",
21246
+ "box-sizing",
21247
+ "width",
21248
+ "white-space",
21249
+ "word-break",
21250
+ "overflow-wrap",
21251
+ "tab-size"
21252
+ ];
21253
+ for (const prop of MIRRORED_PROPS) {
21254
+ mirror.style.setProperty(prop, computed.getPropertyValue(prop));
21255
+ }
21256
+ const lineHeight = parseFloat(computed.getPropertyValue("line-height"));
21257
+ setCaretGeometry({
21258
+ top: marker.offsetTop,
21259
+ left: marker.offsetLeft,
21260
+ lineHeight: Number.isFinite(lineHeight) ? lineHeight : 0
21261
+ });
21262
+ }, [showBlockCaret, editableValue, caretIndex]);
20825
21263
  const errorLineProps = React96.useMemo(() => buildLineProps(errorLines), [errorLines]);
20826
21264
  const viewerLineProps = React96.useMemo(
20827
21265
  () => buildLineProps(errorLines, "px-4 py-0.5 hover:bg-muted/50"),
@@ -21345,11 +21783,24 @@ var init_CodeBlock = __esm({
21345
21783
  {
21346
21784
  ref: editableTextareaRef,
21347
21785
  defaultValue: code,
21348
- onChange: (e) => handleEditableChange(e.target.value),
21786
+ onChange: (e) => {
21787
+ const next = e.target.value;
21788
+ recordKeystroke(editableValue, prevCaretRef.current, next);
21789
+ handleEditableChange(next, "keystroke");
21790
+ },
21349
21791
  onScroll: handleEditableScroll,
21350
21792
  onSelect: (e) => setCaretIndex(e.currentTarget.selectionStart),
21351
- onFocus: editorId ? () => eventBus.emit(`UI:${onEditorFocus}`, { editorId }) : void 0,
21352
- onBlur: editorId ? () => eventBus.emit(`UI:${onEditorBlur}`, { editorId }) : void 0,
21793
+ onKeyUp: (e) => setCaretIndex(e.currentTarget.selectionStart),
21794
+ onClick: (e) => setCaretIndex(e.currentTarget.selectionStart),
21795
+ onKeyDown: handleEditableKeyDown,
21796
+ onFocus: () => {
21797
+ setIsFocused(true);
21798
+ if (editorId) eventBus.emit(`UI:${onEditorFocus}`, { editorId });
21799
+ },
21800
+ onBlur: () => {
21801
+ setIsFocused(false);
21802
+ if (editorId) eventBus.emit(`UI:${onEditorBlur}`, { editorId });
21803
+ },
21353
21804
  spellCheck: false,
21354
21805
  style: {
21355
21806
  position: "absolute",
@@ -21376,16 +21827,39 @@ var init_CodeBlock = __esm({
21376
21827
  },
21377
21828
  editableTextareaKey
21378
21829
  ),
21379
- caretMode !== "bar" && /* @__PURE__ */ jsxRuntime.jsx(
21830
+ showBlockCaret && /* @__PURE__ */ jsxRuntime.jsxs(
21831
+ "div",
21832
+ {
21833
+ ref: caretMirrorRef,
21834
+ "aria-hidden": true,
21835
+ "data-testid": "editor-caret-mirror",
21836
+ style: {
21837
+ position: "absolute",
21838
+ top: 0,
21839
+ left: 0,
21840
+ padding: "1rem",
21841
+ margin: 0,
21842
+ border: "none",
21843
+ visibility: "hidden",
21844
+ pointerEvents: "none"
21845
+ },
21846
+ children: [
21847
+ editableValue.slice(0, caretIndex),
21848
+ /* @__PURE__ */ jsxRuntime.jsx("span", { ref: caretMarkerRef, "data-testid": "editor-caret-marker", children: "\u200B" })
21849
+ ]
21850
+ }
21851
+ ),
21852
+ showBlockCaret && caretGeometry && /* @__PURE__ */ jsxRuntime.jsx(
21380
21853
  "span",
21381
21854
  {
21382
21855
  "aria-hidden": true,
21856
+ "data-testid": "editor-caret",
21383
21857
  style: {
21384
21858
  position: "absolute",
21385
- top: `calc(1rem + ${caretRowCol.row * 19.5}px)`,
21386
- left: `calc(1rem + ${caretRowCol.col}ch)`,
21859
+ top: caretGeometry.top,
21860
+ left: caretGeometry.left,
21387
21861
  width: "1ch",
21388
- height: caretMode === "block" ? "19.5px" : "2px",
21862
+ height: caretMode === "block" ? caretGeometry.lineHeight || "1.2em" : "2px",
21389
21863
  backgroundColor: caretMode === "block" ? "rgba(230, 230, 230, 0.5)" : void 0,
21390
21864
  borderBottom: caretMode === "underline" ? "2px solid #e6e6e6" : void 0,
21391
21865
  pointerEvents: "none"
@@ -29127,6 +29601,7 @@ function SubMenu({
29127
29601
  item.onClick?.();
29128
29602
  },
29129
29603
  "aria-disabled": item.disabled || void 0,
29604
+ title: item.title,
29130
29605
  "data-testid": item.event ? `action-${item.event}` : void 0,
29131
29606
  className: cn(
29132
29607
  "w-full flex items-center gap-3 px-4 py-2 text-start",
@@ -29175,6 +29650,7 @@ function MenuItemRow({
29175
29650
  as: "button",
29176
29651
  onClick: () => onItemClick({ ...item, id: itemId }, itemId),
29177
29652
  "aria-disabled": item.disabled || void 0,
29653
+ title: item.title,
29178
29654
  onMouseEnter: (e) => {
29179
29655
  if (hasSubMenu) openSubMenu(itemId, e.currentTarget);
29180
29656
  },
@@ -36221,13 +36697,13 @@ var init_MapView = __esm({
36221
36697
  shadowSize: [41, 41]
36222
36698
  });
36223
36699
  L.Marker.prototype.options.icon = defaultIcon;
36224
- const { useEffect: useEffect70, useRef: useRef70, useCallback: useCallback100, useState: useState109 } = React96__namespace.default;
36700
+ const { useEffect: useEffect71, useRef: useRef70, useCallback: useCallback101, useState: useState109 } = React96__namespace.default;
36225
36701
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
36226
36702
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
36227
36703
  function MapUpdater({ centerLat, centerLng, zoom }) {
36228
36704
  const map = useMap();
36229
36705
  const prevRef = useRef70({ centerLat, centerLng, zoom });
36230
- useEffect70(() => {
36706
+ useEffect71(() => {
36231
36707
  const prev = prevRef.current;
36232
36708
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
36233
36709
  map.setView([centerLat, centerLng], zoom);
@@ -36238,7 +36714,7 @@ var init_MapView = __esm({
36238
36714
  }
36239
36715
  function MapClickHandler({ onMapClick }) {
36240
36716
  const map = useMap();
36241
- useEffect70(() => {
36717
+ useEffect71(() => {
36242
36718
  if (!onMapClick) return;
36243
36719
  const handler = (e) => {
36244
36720
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -36267,7 +36743,7 @@ var init_MapView = __esm({
36267
36743
  }) {
36268
36744
  const eventBus = useEventBus2();
36269
36745
  const [clickedPosition, setClickedPosition] = useState109(null);
36270
- const handleMapClick = useCallback100((lat, lng) => {
36746
+ const handleMapClick = useCallback101((lat, lng) => {
36271
36747
  if (showClickedPin) {
36272
36748
  setClickedPosition({ lat, lng });
36273
36749
  }
@@ -36276,7 +36752,7 @@ var init_MapView = __esm({
36276
36752
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
36277
36753
  }
36278
36754
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
36279
- const handleMarkerClick = useCallback100((marker) => {
36755
+ const handleMarkerClick = useCallback101((marker) => {
36280
36756
  onMarkerClick?.(marker);
36281
36757
  if (markerClickEvent) {
36282
36758
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -45614,7 +46090,10 @@ var init_FloatingToolbar = __esm({
45614
46090
  positionClasses = {
45615
46091
  "bottom-center": "bottom-6 left-1/2 -translate-x-1/2",
45616
46092
  "bottom-left": "bottom-6 left-6",
45617
- "bottom-right": "bottom-6 right-6"
46093
+ "bottom-right": "bottom-6 right-6",
46094
+ "top-center": "top-6 left-1/2 -translate-x-1/2",
46095
+ "top-left": "top-6 left-6",
46096
+ "top-right": "top-6 right-6"
45618
46097
  };
45619
46098
  FloatingToolbar = ({
45620
46099
  items,
@@ -53275,6 +53754,7 @@ var init_component_registry_generated = __esm({
53275
53754
  "TrendIndicator": TrendIndicator,
53276
53755
  "TypewriterText": TypewriterText,
53277
53756
  "Typography": Typography,
53757
+ "UISlotComponent": UISlotComponent,
53278
53758
  "UISlotRenderer": UISlotRenderer,
53279
53759
  "UploadDropZone": UploadDropZone,
53280
53760
  "VStack": VStack,
@@ -53580,6 +54060,7 @@ function UISlotComponentInner({
53580
54060
  const contained = React96.useContext(SlotContainedContext);
53581
54061
  const schemaCtx = providers.useEntitySchemaOptional();
53582
54062
  const rawContent = slots[slot];
54063
+ const regionClassName = fallback !== void 0 ? cn("contents", className) : className;
53583
54064
  const binding = providers.useEntityBindingSnapshot(rawContent?.sourceTrait);
53584
54065
  const content = React96.useMemo(() => {
53585
54066
  if (!rawContent) return rawContent;
@@ -53628,7 +54109,7 @@ function UISlotComponentInner({
53628
54109
  Box,
53629
54110
  {
53630
54111
  id: `slot-${slot}`,
53631
- className: cn("ui-slot", `ui-slot-${slot}`, className),
54112
+ className: cn("ui-slot", `ui-slot-${slot}`, regionClassName),
53632
54113
  "data-testid": `ui-slot-${slot}`,
53633
54114
  "data-slot-mode": "fallback",
53634
54115
  children: fallback
@@ -53663,7 +54144,7 @@ function UISlotComponentInner({
53663
54144
  Box,
53664
54145
  {
53665
54146
  id: `slot-${slot}-fallback`,
53666
- className: cn("ui-slot", `ui-slot-${slot}-fallback`, className),
54147
+ className: cn("ui-slot", `ui-slot-${slot}-fallback`, regionClassName),
53667
54148
  "data-testid": `ui-slot-${slot}-fallback`,
53668
54149
  "data-slot-mode": "append",
53669
54150
  children: fallback
@@ -53695,7 +54176,7 @@ function UISlotComponentInner({
53695
54176
  Box,
53696
54177
  {
53697
54178
  id: `slot-${slot}`,
53698
- className: cn("ui-slot", `ui-slot-${slot}`, className),
54179
+ className: cn("ui-slot", `ui-slot-${slot}`, regionClassName),
53699
54180
  "data-pattern": content.pattern,
53700
54181
  "data-source-trait": content.sourceTrait,
53701
54182
  "data-testid": `ui-slot-${slot}`,