@simon_he/vue-tui 0.0.1 → 0.0.2

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
@@ -417,32 +417,6 @@ function createNodePathPickerProvider() {
417
417
  };
418
418
  }
419
419
 
420
- //#endregion
421
- //#region src/cli/recording.ts
422
- async function loadFs() {
423
- return import("node:fs");
424
- }
425
- async function writeEventLog(path, events) {
426
- const { writeFileSync } = await loadFs();
427
- const lines = events.map((e) => JSON.stringify(e)).join("\n");
428
- writeFileSync(path, `${lines}${lines ? "\n" : ""}`, "utf8");
429
- }
430
- async function writeSnapshot(path, lines) {
431
- const { writeFileSync } = await loadFs();
432
- writeFileSync(path, `${lines.join("\n")}\n`, "utf8");
433
- }
434
- async function readEventLog(path) {
435
- const { readFileSync } = await loadFs();
436
- const raw = readFileSync(path, "utf8");
437
- const out = [];
438
- for (const line of raw.split(/\r?\n/g)) {
439
- const trimmed = line.trim();
440
- if (!trimmed) continue;
441
- out.push(JSON.parse(trimmed));
442
- }
443
- return out;
444
- }
445
-
446
420
  //#endregion
447
421
  //#region src/utils/path.ts
448
422
  function normalizeSeparators(input) {
@@ -617,37 +591,29 @@ function resolveUserPath(workspaceAbs, input) {
617
591
  }
618
592
 
619
593
  //#endregion
620
- //#region src/core/buffer/width.ts
621
- const fullWidthRanges = [
622
- [4352, 4447],
623
- [9001, 9002],
624
- [11904, 42191],
625
- [44032, 55203],
626
- [63744, 64255],
627
- [65040, 65049],
628
- [65072, 65135],
629
- [65280, 65376],
630
- [65504, 65510]
631
- ];
632
- function isFullWidthCodePoint(codePoint) {
633
- for (const [start, end] of fullWidthRanges) if (codePoint >= start && codePoint <= end) return true;
634
- return false;
594
+ //#region src/cli/recording.ts
595
+ async function loadFs() {
596
+ return import("node:fs");
635
597
  }
636
- function isEmojiLike(codePoint) {
637
- return codePoint >= 127744 && codePoint <= 129791 || codePoint >= 127462 && codePoint <= 127487;
598
+ async function writeEventLog(path, events) {
599
+ const { writeFileSync } = await loadFs();
600
+ const lines = events.map((e) => JSON.stringify(e)).join("\n");
601
+ writeFileSync(path, `${lines}${lines ? "\n" : ""}`, "utf8");
638
602
  }
639
- let extendedPictographicRe = null;
640
- try {
641
- extendedPictographicRe = new RegExp("\\p{Extended_Pictographic}", "u");
642
- } catch {
643
- extendedPictographicRe = null;
603
+ async function writeSnapshot(path, lines) {
604
+ const { writeFileSync } = await loadFs();
605
+ writeFileSync(path, `${lines.join("\n")}\n`, "utf8");
644
606
  }
645
- function charCellWidth(text) {
646
- if (!text) return 1;
647
- if (extendedPictographicRe === null || extendedPictographicRe === void 0 ? void 0 : extendedPictographicRe.test(text)) return 2;
648
- const codePoint = text.codePointAt(0);
649
- if (codePoint == null) return 1;
650
- return isFullWidthCodePoint(codePoint) || isEmojiLike(codePoint) ? 2 : 1;
607
+ async function readEventLog(path) {
608
+ const { readFileSync } = await loadFs();
609
+ const raw = readFileSync(path, "utf8");
610
+ const out = [];
611
+ for (const line of raw.split(/\r?\n/g)) {
612
+ const trimmed = line.trim();
613
+ if (!trimmed) continue;
614
+ out.push(JSON.parse(trimmed));
615
+ }
616
+ return out;
651
617
  }
652
618
 
653
619
  //#endregion
@@ -953,6 +919,40 @@ function parseAnsiSgr(input, baseStyle = {}) {
953
919
  return segments;
954
920
  }
955
921
 
922
+ //#endregion
923
+ //#region src/core/buffer/width.ts
924
+ const fullWidthRanges = [
925
+ [4352, 4447],
926
+ [9001, 9002],
927
+ [11904, 42191],
928
+ [44032, 55203],
929
+ [63744, 64255],
930
+ [65040, 65049],
931
+ [65072, 65135],
932
+ [65280, 65376],
933
+ [65504, 65510]
934
+ ];
935
+ function isFullWidthCodePoint(codePoint) {
936
+ for (const [start, end] of fullWidthRanges) if (codePoint >= start && codePoint <= end) return true;
937
+ return false;
938
+ }
939
+ function isEmojiLike(codePoint) {
940
+ return codePoint >= 127744 && codePoint <= 129791 || codePoint >= 127462 && codePoint <= 127487;
941
+ }
942
+ let extendedPictographicRe = null;
943
+ try {
944
+ extendedPictographicRe = new RegExp("\\p{Extended_Pictographic}", "u");
945
+ } catch {
946
+ extendedPictographicRe = null;
947
+ }
948
+ function charCellWidth(text) {
949
+ if (!text) return 1;
950
+ if (extendedPictographicRe === null || extendedPictographicRe === void 0 ? void 0 : extendedPictographicRe.test(text)) return 2;
951
+ const codePoint = text.codePointAt(0);
952
+ if (codePoint == null) return 1;
953
+ return isFullWidthCodePoint(codePoint) || isEmojiLike(codePoint) ? 2 : 1;
954
+ }
955
+
956
956
  //#endregion
957
957
  //#region src/core/buffer/buffer.ts
958
958
  const DEFAULT_STYLE = Object.freeze({});
@@ -3611,6 +3611,81 @@ function translateRect(rect, dx, dy) {
3611
3611
  h: rect.h
3612
3612
  };
3613
3613
  }
3614
+ function normalizePadding(padding) {
3615
+ const nums = [];
3616
+ if (typeof padding === "number") nums.push(padding);
3617
+ else if (Array.isArray(padding)) for (const v of padding) nums.push(typeof v === "number" ? v : Number(v));
3618
+ else if (typeof padding === "string") for (const part of padding.trim().split(/\s+/g)) {
3619
+ if (!part) continue;
3620
+ nums.push(Number(part));
3621
+ }
3622
+ const n = nums.map((v) => Number.isFinite(v) ? Math.max(0, Math.floor(v)) : 0);
3623
+ const a = n[0] ?? 0;
3624
+ const b = n[1] ?? a;
3625
+ const c = n[2] ?? a;
3626
+ const d = n[3] ?? b;
3627
+ if (n.length <= 1) return {
3628
+ top: a,
3629
+ right: a,
3630
+ bottom: a,
3631
+ left: a
3632
+ };
3633
+ if (n.length === 2) return {
3634
+ top: a,
3635
+ right: b,
3636
+ bottom: a,
3637
+ left: b
3638
+ };
3639
+ if (n.length === 3) return {
3640
+ top: a,
3641
+ right: b,
3642
+ bottom: c,
3643
+ left: b
3644
+ };
3645
+ return {
3646
+ top: a,
3647
+ right: b,
3648
+ bottom: c,
3649
+ left: d
3650
+ };
3651
+ }
3652
+ function clampRectPadding(rect, padding) {
3653
+ const requested = normalizePadding(padding);
3654
+ const w = Math.max(0, Math.floor(rect.w));
3655
+ const h$1 = Math.max(0, Math.floor(rect.h));
3656
+ const maxTotalX = Math.max(0, w - (w > 0 ? 1 : 0));
3657
+ const maxTotalY = Math.max(0, h$1 - (h$1 > 0 ? 1 : 0));
3658
+ if (requested.top === requested.right && requested.top === requested.bottom && requested.top === requested.left) {
3659
+ const maxPadX = Math.max(0, Math.floor(maxTotalX / 2));
3660
+ const maxPadY = Math.max(0, Math.floor(maxTotalY / 2));
3661
+ const pad = Math.min(requested.top, maxPadX, maxPadY);
3662
+ return {
3663
+ top: pad,
3664
+ right: pad,
3665
+ bottom: pad,
3666
+ left: pad
3667
+ };
3668
+ }
3669
+ const left = Math.min(requested.left, maxTotalX);
3670
+ const right = Math.min(requested.right, Math.max(0, maxTotalX - left));
3671
+ const top = Math.min(requested.top, maxTotalY);
3672
+ const bottom = Math.min(requested.bottom, Math.max(0, maxTotalY - top));
3673
+ return {
3674
+ top,
3675
+ right,
3676
+ bottom,
3677
+ left
3678
+ };
3679
+ }
3680
+ function insetRect(rect, padding) {
3681
+ const pad = clampRectPadding(rect, padding);
3682
+ return {
3683
+ x: rect.x + pad.left,
3684
+ y: rect.y + pad.top,
3685
+ w: Math.max(0, rect.w - pad.left - pad.right),
3686
+ h: Math.max(0, rect.h - pad.top - pad.bottom)
3687
+ };
3688
+ }
3614
3689
 
3615
3690
  //#endregion
3616
3691
  //#region src/vue/components/TAnchor.ts
@@ -3651,6 +3726,14 @@ const TAnchor = defineComponent({
3651
3726
  type: Number,
3652
3727
  default: 0
3653
3728
  },
3729
+ padding: {
3730
+ type: [
3731
+ Number,
3732
+ String,
3733
+ Array
3734
+ ],
3735
+ default: 0
3736
+ },
3654
3737
  focusable: {
3655
3738
  type: Boolean,
3656
3739
  default: false
@@ -3762,9 +3845,11 @@ const TAnchor = defineComponent({
3762
3845
  const childStack = computed(() => render.createStack(parentStack.value, props.zIndex));
3763
3846
  watchEffect(() => {
3764
3847
  const translated = rawRect.value;
3765
- childLayout.originX = translated.x;
3766
- childLayout.originY = translated.y;
3767
- childLayout.clipRect = rect.value;
3848
+ const content = insetRect(translated, props.padding);
3849
+ const contentClip = intersectRect(content, rect.value) ?? EMPTY_RECT$1;
3850
+ childLayout.originX = content.x;
3851
+ childLayout.originY = content.y;
3852
+ childLayout.clipRect = contentClip;
3768
3853
  });
3769
3854
  provide(LayoutContextKey, childLayout);
3770
3855
  provide(RenderStackKey, childStack);
@@ -3947,7 +4032,11 @@ const TBox = defineComponent({
3947
4032
  default: ""
3948
4033
  },
3949
4034
  padding: {
3950
- type: Number,
4035
+ type: [
4036
+ Number,
4037
+ String,
4038
+ Array
4039
+ ],
3951
4040
  default: 0
3952
4041
  },
3953
4042
  scrollX: {
@@ -4040,17 +4129,14 @@ const TBox = defineComponent({
4040
4129
  watchEffect(() => {
4041
4130
  const r = absRect.value;
4042
4131
  const borderInset = props.border ? 1 : 0;
4043
- const requestedPad = Math.max(0, Math.floor(props.padding));
4044
- const maxPadX = Math.max(0, Math.floor((r.w - borderInset * 2 - 1) / 2));
4045
- const maxPadY = Math.max(0, Math.floor((r.h - borderInset * 2 - 1) / 2));
4046
- const pad = Math.min(requestedPad, maxPadX, maxPadY);
4047
- const content = {
4048
- x: r.x + borderInset + pad,
4049
- y: r.y + borderInset + pad,
4050
- w: Math.max(0, r.w - borderInset * 2 - pad * 2),
4051
- h: Math.max(0, r.h - borderInset * 2 - pad * 2)
4132
+ const inner = {
4133
+ x: r.x + borderInset,
4134
+ y: r.y + borderInset,
4135
+ w: Math.max(0, r.w - borderInset * 2),
4136
+ h: Math.max(0, r.h - borderInset * 2)
4052
4137
  };
4053
- let contentRect$1 = intersectRect(content, r);
4138
+ const content = insetRect(inner, props.padding);
4139
+ let contentRect$1 = intersectRect(content, inner);
4054
4140
  if (parent.clipRect && contentRect$1) contentRect$1 = intersectRect(contentRect$1, parent.clipRect);
4055
4141
  if (!contentRect$1) contentRect$1 = {
4056
4142
  x: 0,
@@ -4144,6 +4230,14 @@ const TText = defineComponent({
4144
4230
  type: Number,
4145
4231
  default: void 0
4146
4232
  },
4233
+ padding: {
4234
+ type: [
4235
+ Number,
4236
+ String,
4237
+ Array
4238
+ ],
4239
+ default: 0
4240
+ },
4147
4241
  style: {
4148
4242
  type: Object,
4149
4243
  default: void 0
@@ -4161,16 +4255,21 @@ const TText = defineComponent({
4161
4255
  const { terminal, defaultStyle } = useTerminal();
4162
4256
  const layout = useLayout();
4163
4257
  const { visible, rootProps } = useVisibility();
4258
+ const pad = computed(() => normalizePadding(props.padding));
4259
+ const contentW = computed(() => {
4260
+ const outerW = props.w ?? computeDefaultWidth(props.value) + pad.value.left + pad.value.right;
4261
+ return Math.max(0, Math.floor(outerW) - pad.value.left - pad.value.right);
4262
+ });
4164
4263
  const lines = computed(() => {
4165
- const w = props.w ?? computeDefaultWidth(props.value);
4264
+ const w = contentW.value;
4166
4265
  if (w <= 0) return [""];
4167
4266
  if (!props.wrap) return splitLines(props.value).map((l) => fitText(l, w));
4168
4267
  const safe = sanitizeTextBlock(props.value);
4169
4268
  return wrapByCells(safe, w).map((l) => fitText(l, w));
4170
4269
  });
4171
4270
  const absRect = computed(() => {
4172
- const width = props.w ?? computeDefaultWidth(props.value);
4173
- const height = props.h ?? (props.wrap ? lines.value.length || 1 : lines.value.length || 1);
4271
+ const width = Math.floor(props.w ?? computeDefaultWidth(props.value) + pad.value.left + pad.value.right);
4272
+ const height = Math.floor(props.h ?? (lines.value.length || 1) + pad.value.top + pad.value.bottom);
4174
4273
  const raw = {
4175
4274
  x: props.x,
4176
4275
  y: props.y,
@@ -4203,10 +4302,14 @@ const TText = defineComponent({
4203
4302
  if (!visible.value) return;
4204
4303
  const r = absRect.value;
4205
4304
  if (r.w <= 0 || r.h <= 0) return;
4305
+ const contentX = r.x + pad.value.left;
4306
+ const contentY = r.y + pad.value.top;
4307
+ const w = Math.max(0, Math.floor(r.w) - pad.value.left - pad.value.right);
4308
+ const h$1 = Math.max(0, Math.floor(r.h) - pad.value.top - pad.value.bottom);
4206
4309
  const style = props.style ?? defaultStyle.value;
4207
4310
  const blank = props.clear ? " ".repeat(r.w) : "";
4208
4311
  const out = lines.value;
4209
- const maxLines = Math.min(out.length, Math.max(0, Math.floor(r.h)));
4312
+ const maxLines = Math.min(out.length, h$1);
4210
4313
  const paintRow = (y) => {
4211
4314
  const i = y - r.y;
4212
4315
  if (i < 0 || i >= r.h) return;
@@ -4215,10 +4318,12 @@ const TText = defineComponent({
4215
4318
  y,
4216
4319
  style
4217
4320
  });
4218
- if (i < 0 || i >= maxLines) return;
4219
- const clipped = sliceByCells(out[i] ?? "", r.w);
4220
- terminal.write(padEndByCells(clipped, r.w), {
4221
- x: r.x,
4321
+ const contentRow = y - contentY;
4322
+ if (contentRow < 0 || contentRow >= h$1) return;
4323
+ if (contentRow < 0 || contentRow >= maxLines) return;
4324
+ const clipped = sliceByCells(out[contentRow] ?? "", w);
4325
+ terminal.write(padEndByCells(clipped, w), {
4326
+ x: contentX,
4222
4327
  y,
4223
4328
  style
4224
4329
  });
@@ -4265,6 +4370,14 @@ const TView = defineComponent({
4265
4370
  type: Number,
4266
4371
  default: 0
4267
4372
  },
4373
+ padding: {
4374
+ type: [
4375
+ Number,
4376
+ String,
4377
+ Array
4378
+ ],
4379
+ default: 0
4380
+ },
4268
4381
  scrollX: {
4269
4382
  type: Number,
4270
4383
  default: 0
@@ -4372,9 +4485,11 @@ const TView = defineComponent({
4372
4485
  const childStack = computed(() => render.createStack(parentStack.value, props.zIndex));
4373
4486
  watchEffect(() => {
4374
4487
  const translated = rawRect.value;
4375
- childLayout.originX = translated.x - Math.floor(props.scrollX);
4376
- childLayout.originY = translated.y - Math.floor(props.scrollY);
4377
- childLayout.clipRect = rect.value;
4488
+ const content = insetRect(translated, props.padding);
4489
+ const contentClip = intersectRect(content, rect.value) ?? EMPTY_RECT;
4490
+ childLayout.originX = content.x - Math.floor(props.scrollX);
4491
+ childLayout.originY = content.y - Math.floor(props.scrollY);
4492
+ childLayout.clipRect = contentClip;
4378
4493
  });
4379
4494
  provide(LayoutContextKey, childLayout);
4380
4495
  provide(RenderStackKey, childStack);
@@ -4580,10 +4695,10 @@ function computePosition(opts) {
4580
4695
  }
4581
4696
  function contentRect(opts) {
4582
4697
  const borderInset = opts.border ? 1 : 0;
4583
- const pad = Math.max(0, Math.floor(opts.padding));
4698
+ const pad = normalizePadding(opts.padding);
4584
4699
  return {
4585
- w: Math.max(0, Math.floor(opts.w) - borderInset * 2 - pad * 2),
4586
- h: Math.max(0, Math.floor(opts.h) - borderInset * 2 - pad * 2)
4700
+ w: Math.max(0, Math.floor(opts.w) - borderInset * 2 - pad.left - pad.right),
4701
+ h: Math.max(0, Math.floor(opts.h) - borderInset * 2 - pad.top - pad.bottom)
4587
4702
  };
4588
4703
  }
4589
4704
  const DialogSurface = defineComponent({
@@ -4606,7 +4721,11 @@ const DialogSurface = defineComponent({
4606
4721
  default: ""
4607
4722
  },
4608
4723
  padding: {
4609
- type: Number,
4724
+ type: [
4725
+ Number,
4726
+ String,
4727
+ Array
4728
+ ],
4610
4729
  default: 1
4611
4730
  },
4612
4731
  zIndex: {
@@ -4880,7 +4999,11 @@ const TDialog = defineComponent({
4880
4999
  default: ""
4881
5000
  },
4882
5001
  padding: {
4883
- type: Number,
5002
+ type: [
5003
+ Number,
5004
+ String,
5005
+ Array
5006
+ ],
4884
5007
  default: 1
4885
5008
  },
4886
5009
  zIndex: {
@@ -5947,6 +6070,14 @@ const TFlow = defineComponent({
5947
6070
  type: Number,
5948
6071
  required: true
5949
6072
  },
6073
+ padding: {
6074
+ type: [
6075
+ Number,
6076
+ String,
6077
+ Array
6078
+ ],
6079
+ default: 0
6080
+ },
5950
6081
  items: {
5951
6082
  type: Array,
5952
6083
  required: true
@@ -5969,25 +6100,29 @@ const TFlow = defineComponent({
5969
6100
  }
5970
6101
  },
5971
6102
  setup(props, { slots }) {
6103
+ const pad = computed(() => normalizePadding(props.padding));
6104
+ const innerW = computed(() => Math.max(0, Math.floor(props.w) - pad.value.left - pad.value.right));
6105
+ const innerH = computed(() => Math.max(0, Math.floor(props.h) - pad.value.top - pad.value.bottom));
5972
6106
  return () => h(TView, {
5973
6107
  x: props.x,
5974
6108
  y: props.y,
5975
6109
  w: props.w,
5976
6110
  h: props.h,
5977
- zIndex: props.zIndex
6111
+ zIndex: props.zIndex,
6112
+ padding: props.padding
5978
6113
  }, () => props.items.map((item, index) => {
5979
6114
  const step = Math.max(0, Math.floor(props.itemSize)) + Math.max(0, Math.floor(props.gap));
5980
6115
  const offset = index * step;
5981
6116
  const childProps = props.direction === "vertical" ? {
5982
6117
  x: 0,
5983
6118
  y: offset,
5984
- w: props.w,
6119
+ w: innerW.value,
5985
6120
  h: Math.max(0, Math.floor(props.itemSize))
5986
6121
  } : {
5987
6122
  x: offset,
5988
6123
  y: 0,
5989
6124
  w: Math.max(0, Math.floor(props.itemSize)),
5990
- h: props.h
6125
+ h: innerH.value
5991
6126
  };
5992
6127
  return h(TView, {
5993
6128
  key: index,
@@ -6395,6 +6530,14 @@ const TInput = defineComponent({
6395
6530
  type: Number,
6396
6531
  default: 0
6397
6532
  },
6533
+ padding: {
6534
+ type: [
6535
+ Number,
6536
+ String,
6537
+ Array
6538
+ ],
6539
+ default: "0 1"
6540
+ },
6398
6541
  modelValue: {
6399
6542
  type: String,
6400
6543
  required: true
@@ -6506,19 +6649,6 @@ const TInput = defineComponent({
6506
6649
  const parentEventZ = inject(EventZIndexContextKey, computed(() => 0));
6507
6650
  const eventZ = computed(() => (parentEventZ.value ?? 0) + (props.zIndex ?? 0));
6508
6651
  const promptOverlayStack = render.createStack(render.rootStack, 1e4);
6509
- const PADDING_X = 1;
6510
- function measureContent(r) {
6511
- const wAll = Math.max(0, Math.floor(r.w));
6512
- const hAll = Math.max(0, Math.floor(r.h));
6513
- const padX = clamp$3(PADDING_X, 0, Math.floor(wAll / 2));
6514
- const w = Math.max(0, wAll - padX * 2);
6515
- return {
6516
- wAll,
6517
- hAll,
6518
- padX,
6519
- w
6520
- };
6521
- }
6522
6652
  const focused = ref(false);
6523
6653
  const cursor = ref(0);
6524
6654
  const anchor = ref(null);
@@ -6535,7 +6665,6 @@ const TInput = defineComponent({
6535
6665
  function getValue() {
6536
6666
  return pendingValue.value ?? props.modelValue;
6537
6667
  }
6538
- const wrapMode = computed(() => Math.max(1, Math.floor(props.h)) > 1);
6539
6668
  const rawAbsRect = computed(() => {
6540
6669
  const raw = {
6541
6670
  x: props.x,
@@ -6797,6 +6926,8 @@ const TInput = defineComponent({
6797
6926
  h: 0
6798
6927
  };
6799
6928
  });
6929
+ const contentRect$1 = computed(() => insetRect(absRect.value, props.padding));
6930
+ const wrapMode = computed(() => Math.max(1, Math.floor(contentRect$1.value.h)) > 1);
6800
6931
  const selection = computed(() => {
6801
6932
  if (anchor.value == null || anchor.value === cursor.value) return null;
6802
6933
  const start = Math.min(anchor.value, cursor.value);
@@ -6872,10 +7003,16 @@ const TInput = defineComponent({
6872
7003
  return !focused.value || props.placeholderWhenFocused;
6873
7004
  });
6874
7005
  function ensureCursorVisible() {
6875
- const r = absRect.value;
6876
- const { hAll, w: contentW } = measureContent(r);
6877
- const width = Math.max(1, contentW);
6878
- const height = Math.max(1, hAll);
7006
+ const r = contentRect$1.value;
7007
+ const w = Math.max(0, Math.floor(r.w));
7008
+ const h$1 = Math.max(0, Math.floor(r.h));
7009
+ if (w <= 0 || h$1 <= 0) {
7010
+ scrollX.value = 0;
7011
+ scrollY.value = 0;
7012
+ return;
7013
+ }
7014
+ const width = w;
7015
+ const height = h$1;
6879
7016
  const value = getValue();
6880
7017
  const wrap = wrapMode.value;
6881
7018
  const prefix = buildMentionPrefix(props.mentions, width);
@@ -6894,13 +7031,15 @@ const TInput = defineComponent({
6894
7031
  else if (line > scrollY.value + height - 1) scrollY.value = clamp$3(line - (height - 1), 0, maxTop);
6895
7032
  }
6896
7033
  function setCursorByCell2D(cellX, cellY, extendSelection = false, e) {
6897
- const r = absRect.value;
6898
- const { w: contentW, padX } = measureContent(r);
6899
- const width = Math.max(1, contentW);
7034
+ const r = contentRect$1.value;
7035
+ const w = Math.max(0, Math.floor(r.w));
7036
+ const h$1 = Math.max(0, Math.floor(r.h));
7037
+ if (w <= 0 || h$1 <= 0) return;
7038
+ const width = w;
6900
7039
  const prefix = buildMentionPrefix(props.mentions, width);
6901
7040
  const prefixCells = prefix.cells;
6902
- const localX = clamp$3(cellX - (r.x + padX), 0, Math.max(0, contentW - 1));
6903
- const localY = clamp$3(cellY - r.y, 0, Math.max(0, r.h - 1));
7041
+ const localX = clamp$3(cellX - r.x, 0, Math.max(0, w - 1));
7042
+ const localY = clamp$3(cellY - r.y, 0, Math.max(0, h$1 - 1));
6904
7043
  const prefixVisible = scrollY.value === 0 && localY === 0 && prefixCells > 0;
6905
7044
  if (prefixVisible && localX < prefixCells) {
6906
7045
  for (const chip of prefix.chips) if (localX >= chip.start && localX < chip.end) {
@@ -6946,8 +7085,7 @@ const TInput = defineComponent({
6946
7085
  compositionText.value = "";
6947
7086
  pendingValue.value = nextValue;
6948
7087
  if (wrapMode.value) {
6949
- const { w: contentW } = measureContent(absRect.value);
6950
- const w = Math.max(1, contentW);
7088
+ const w = Math.max(1, Math.floor(contentRect$1.value.w));
6951
7089
  const prefix = buildMentionPrefix(props.mentions, w);
6952
7090
  const firstW = Math.max(1, w - prefix.cells);
6953
7091
  desiredCol.value = indexToWrappedCellColFirstWidth(nextValue, c, firstW, w).col;
@@ -6966,8 +7104,7 @@ const TInput = defineComponent({
6966
7104
  } else anchor.value = null;
6967
7105
  cursor.value = clamp$3(nextCursor, 0, value.length);
6968
7106
  if (wrapMode.value) {
6969
- const { w: contentW } = measureContent(absRect.value);
6970
- const w = Math.max(1, contentW);
7107
+ const w = Math.max(1, Math.floor(contentRect$1.value.w));
6971
7108
  const prefix = buildMentionPrefix(props.mentions, w);
6972
7109
  const firstW = Math.max(1, w - prefix.cells);
6973
7110
  desiredCol.value = indexToWrappedCellColFirstWidth(value, cursor.value, firstW, w).col;
@@ -6976,8 +7113,8 @@ const TInput = defineComponent({
6976
7113
  scheduler.invalidate();
6977
7114
  }
6978
7115
  watch([
6979
- () => absRect.value.w,
6980
- () => absRect.value.h,
7116
+ () => contentRect$1.value.w,
7117
+ () => contentRect$1.value.h,
6981
7118
  () => wrapMode.value
6982
7119
  ], () => {
6983
7120
  ensureCursorVisible();
@@ -7026,14 +7163,15 @@ const TInput = defineComponent({
7026
7163
  if (imeAnchor.value) imeAnchor.value = null;
7027
7164
  return;
7028
7165
  }
7029
- const r = absRect.value;
7030
- if (r.w <= 0 || r.h <= 0) {
7166
+ const r = contentRect$1.value;
7167
+ const w = Math.max(0, Math.floor(r.w));
7168
+ const h$1 = Math.max(0, Math.floor(r.h));
7169
+ if (w <= 0 || h$1 <= 0) {
7031
7170
  imeAnchor.value = null;
7032
7171
  return;
7033
7172
  }
7034
- const { hAll, padX, w: contentW } = measureContent(r);
7035
- const width = Math.max(1, contentW);
7036
- const height = Math.max(1, hAll);
7173
+ const width = w;
7174
+ const height = h$1;
7037
7175
  const placeholderVisible = shouldShowPlaceholder.value;
7038
7176
  const offX = placeholderVisible ? 0 : scrollX.value;
7039
7177
  const offY = placeholderVisible ? 0 : scrollY.value;
@@ -7050,7 +7188,7 @@ const TInput = defineComponent({
7050
7188
  const cx = clamp$3(cxRaw, 0, Math.max(0, width - 1));
7051
7189
  const cy = clamp$3(cyRaw, 0, Math.max(0, height - 1));
7052
7190
  imeAnchor.value = {
7053
- cellX: r.x + padX + cx,
7191
+ cellX: r.x + cx,
7054
7192
  cellY: r.y + cy
7055
7193
  };
7056
7194
  });
@@ -7182,6 +7320,28 @@ const TInput = defineComponent({
7182
7320
  return;
7183
7321
  }
7184
7322
  }
7323
+ const base64EncodeBytes = (bytes) => {
7324
+ const table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
7325
+ let out = "";
7326
+ for (let i = 0; i < bytes.length; i += 3) {
7327
+ const a = bytes[i] ?? 0;
7328
+ const b = bytes[i + 1] ?? 0;
7329
+ const c = bytes[i + 2] ?? 0;
7330
+ const triple = a << 16 | b << 8 | c;
7331
+ out += table[triple >> 18 & 63] ?? "";
7332
+ out += table[triple >> 12 & 63] ?? "";
7333
+ out += i + 1 < bytes.length ? table[triple >> 6 & 63] ?? "" : "=";
7334
+ out += i + 2 < bytes.length ? table[triple & 63] ?? "" : "=";
7335
+ }
7336
+ return out;
7337
+ };
7338
+ const base64EncodeText = (text) => {
7339
+ if (typeof TextEncoder !== "undefined") return base64EncodeBytes(new TextEncoder().encode(text));
7340
+ const bin = unescape(encodeURIComponent(text));
7341
+ const bytes = new Uint8Array(bin.length);
7342
+ for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
7343
+ return base64EncodeBytes(bytes);
7344
+ };
7185
7345
  const copyText = async (text) => {
7186
7346
  var _nav$clipboard, _proc$stdout, _proc$stdout2, _doc$body;
7187
7347
  if (!text) return false;
@@ -7192,7 +7352,7 @@ const TInput = defineComponent({
7192
7352
  } catch {}
7193
7353
  const proc = globalThis.process;
7194
7354
  if ((proc === null || proc === void 0 || (_proc$stdout = proc.stdout) === null || _proc$stdout === void 0 ? void 0 : _proc$stdout.write) && (proc === null || proc === void 0 || (_proc$stdout2 = proc.stdout) === null || _proc$stdout2 === void 0 ? void 0 : _proc$stdout2.isTTY)) try {
7195
- const base64 = typeof Buffer !== "undefined" ? Buffer.from(text, "utf-8").toString("base64") : btoa(unescape(encodeURIComponent(text)));
7355
+ const base64 = base64EncodeText(text);
7196
7356
  proc.stdout.write(`\u001B]52;c;${base64}\u0007`);
7197
7357
  return true;
7198
7358
  } catch {}
@@ -7259,8 +7419,7 @@ const TInput = defineComponent({
7259
7419
  if (e.key === "ArrowUp" || e.key === "ArrowDown") {
7260
7420
  e.preventDefault();
7261
7421
  const wrap = wrapMode.value;
7262
- const { w: contentW } = measureContent(absRect.value);
7263
- const width = Math.max(1, contentW);
7422
+ const width = Math.max(1, Math.floor(contentRect$1.value.w));
7264
7423
  const prefix = buildMentionPrefix(props.mentions, width);
7265
7424
  const firstW = Math.max(1, width - prefix.cells);
7266
7425
  const { line, col, lines } = wrap ? indexToWrappedCellColFirstWidth(value, cursor.value, firstW, width) : indexToLineCellCol(value, cursor.value);
@@ -7478,6 +7637,7 @@ const TInput = defineComponent({
7478
7637
  props.mentions,
7479
7638
  props.placeholder,
7480
7639
  props.placeholderWhenFocused,
7640
+ props.padding,
7481
7641
  props.style,
7482
7642
  props.autoFocus,
7483
7643
  props.cursorBlink,
@@ -7496,8 +7656,10 @@ const TInput = defineComponent({
7496
7656
  ],
7497
7657
  paint: () => {
7498
7658
  if (!visible.value) return;
7499
- const r = absRect.value;
7500
- if (r.w <= 0 || r.h <= 0) return;
7659
+ const rAll = absRect.value;
7660
+ const wAll = Math.max(0, Math.floor(rAll.w));
7661
+ const hAll = Math.max(0, Math.floor(rAll.h));
7662
+ if (wAll <= 0 || hAll <= 0) return;
7501
7663
  const style = props.style ?? defaultStyle.value;
7502
7664
  const placeholderVisible = shouldShowPlaceholder.value;
7503
7665
  const offX = placeholderVisible ? 0 : scrollX.value;
@@ -7515,14 +7677,23 @@ const TInput = defineComponent({
7515
7677
  ...style,
7516
7678
  fg: "white"
7517
7679
  } : style;
7518
- const { wAll, padX, w: contentW } = measureContent(r);
7519
- const x0 = r.x + padX;
7520
- const width = Math.max(1, contentW);
7680
+ const blankAll = " ".repeat(wAll);
7681
+ for (let row = 0; row < hAll; row++) terminal.write(blankAll, {
7682
+ x: rAll.x,
7683
+ y: rAll.y + row,
7684
+ style: baseStyle
7685
+ });
7686
+ const r = contentRect$1.value;
7687
+ const w = Math.max(0, Math.floor(r.w));
7688
+ const h$1 = Math.max(0, Math.floor(r.h));
7689
+ if (w <= 0 || h$1 <= 0) return;
7690
+ const x0 = r.x;
7691
+ const width = Math.max(1, w);
7521
7692
  const prefix = buildMentionPrefix(props.mentions, width);
7522
7693
  const prefixCells = prefix.cells;
7523
7694
  const firstWidth = Math.max(1, Math.max(1, width) - prefixCells);
7524
7695
  const lines = wrap ? wrapToLinesFirstWidth(text, firstWidth, width) : computeLines(text);
7525
- for (let row = 0; row < r.h; row++) {
7696
+ for (let row = 0; row < h$1; row++) {
7526
7697
  const lineIndex = offY + row;
7527
7698
  const info = lines[lineIndex];
7528
7699
  const raw = info ? text.slice(info.start, info.end).replace(/[\r\n]/g, "") : "";
@@ -7531,12 +7702,7 @@ const TInput = defineComponent({
7531
7702
  const rowTextW = hasPrefix ? firstWidth : width;
7532
7703
  const rawVisible = wrap ? sliceByCellsWindow(raw, 0, rowTextW) : sliceByCellsWindow(raw, offX, rowTextW);
7533
7704
  const visible$1 = padEndByCells$1(rawVisible, rowTextW);
7534
- terminal.write(" ".repeat(wAll), {
7535
- x: r.x,
7536
- y: r.y + row,
7537
- style: baseStyle
7538
- });
7539
- if (contentW <= 0) continue;
7705
+ const y = r.y + row;
7540
7706
  if (hasPrefix) {
7541
7707
  const chipStyle = {
7542
7708
  ...style,
@@ -7547,18 +7713,18 @@ const TInput = defineComponent({
7547
7713
  if (!chip.label) continue;
7548
7714
  terminal.write(chip.label, {
7549
7715
  x: x0 + chip.start,
7550
- y: r.y + row,
7716
+ y,
7551
7717
  style: chipStyle
7552
7718
  });
7553
7719
  }
7554
7720
  terminal.write(visible$1, {
7555
7721
  x: x0 + rowPrefixCells,
7556
- y: r.y + row,
7722
+ y,
7557
7723
  style: baseStyle
7558
7724
  });
7559
7725
  } else terminal.write(visible$1, {
7560
7726
  x: x0,
7561
- y: r.y + row,
7727
+ y,
7562
7728
  style: baseStyle
7563
7729
  });
7564
7730
  }
@@ -7566,7 +7732,7 @@ const TInput = defineComponent({
7566
7732
  if (sel && !placeholderVisible) {
7567
7733
  const bg = "blueBright";
7568
7734
  const fg = "black";
7569
- for (let row = 0; row < r.h; row++) {
7735
+ for (let row = 0; row < h$1; row++) {
7570
7736
  const lineIndex = offY + row;
7571
7737
  const info = lines[lineIndex];
7572
7738
  if (!info) continue;
@@ -7580,14 +7746,14 @@ const TInput = defineComponent({
7580
7746
  for (let i = info.start; i < info.end;) {
7581
7747
  const ch = readCodePointAt(text, i);
7582
7748
  const nextI = i + ch.length;
7583
- const w = charCellWidth(ch);
7584
- if (xCell$1 + w > rowTextW) break;
7749
+ const w$1 = charCellWidth(ch);
7750
+ if (xCell$1 + w$1 > rowTextW) break;
7585
7751
  if (i >= sel.start && i < sel.end) terminal.put(xBase + xCell$1, y, ch, {
7586
7752
  ...style,
7587
7753
  fg,
7588
7754
  bg
7589
7755
  });
7590
- xCell$1 += w;
7756
+ xCell$1 += w$1;
7591
7757
  i = nextI;
7592
7758
  if (xCell$1 >= rowTextW) break;
7593
7759
  }
@@ -7598,8 +7764,8 @@ const TInput = defineComponent({
7598
7764
  for (let i = info.start; i < info.end;) {
7599
7765
  const ch = readCodePointAt(text, i);
7600
7766
  const nextI = i + ch.length;
7601
- const w = charCellWidth(ch);
7602
- const nextCell = xCell + w;
7767
+ const w$1 = charCellWidth(ch);
7768
+ const nextCell = xCell + w$1;
7603
7769
  if (nextCell <= offX) {
7604
7770
  xCell = nextCell;
7605
7771
  i = nextI;
@@ -7607,7 +7773,7 @@ const TInput = defineComponent({
7607
7773
  }
7608
7774
  if (xCell >= offX + rowTextW) break;
7609
7775
  const localX = xCell - offX;
7610
- if (localX + w > rowTextW) break;
7776
+ if (localX + w$1 > rowTextW) break;
7611
7777
  if (i >= sel.start && i < sel.end) terminal.put(xBase + localX, y, ch, {
7612
7778
  ...style,
7613
7779
  fg,
@@ -7629,7 +7795,7 @@ const TInput = defineComponent({
7629
7795
  const pos = wrap ? indexToWrappedCellColFirstWidth(cursorText, cursor.value, firstW, wAll$1) : indexToLineCellCol(cursorText, cursor.value);
7630
7796
  const cx0 = (pos.line === 0 ? prefixCells : 0) + (pos.col - offX);
7631
7797
  const cy = pos.line - offY;
7632
- if (cx0 >= 0 && cx0 < width && cy >= 0 && cy < r.h) {
7798
+ if (cx0 >= 0 && cx0 < width && cy >= 0 && cy < h$1) {
7633
7799
  const x = x0 + cx0;
7634
7800
  const y = r.y + cy;
7635
7801
  const avail = Math.max(0, width - cx0);
@@ -7657,7 +7823,7 @@ const TInput = defineComponent({
7657
7823
  const pos = wrap ? indexToWrappedCellColFirstWidth(cursorText, cursor.value, firstW, wAll$1) : indexToLineCellCol(cursorText, cursor.value);
7658
7824
  const cx0 = (pos.line === 0 ? prefixCells : 0) + (pos.col - offX);
7659
7825
  const cy = pos.line - offY;
7660
- if (cx0 >= 0 && cx0 < width && cy >= 0 && cy < r.h) {
7826
+ if (cx0 >= 0 && cx0 < width && cy >= 0 && cy < h$1) {
7661
7827
  const y = r.y + cy;
7662
7828
  let x = x0 + cx0;
7663
7829
  let cell = null;
@@ -7861,7 +8027,11 @@ const TInputBox = defineComponent({
7861
8027
  default: ""
7862
8028
  },
7863
8029
  padding: {
7864
- type: Number,
8030
+ type: [
8031
+ Number,
8032
+ String,
8033
+ Array
8034
+ ],
7865
8035
  default: 0
7866
8036
  },
7867
8037
  modelValue: {
@@ -7902,8 +8072,9 @@ const TInputBox = defineComponent({
7902
8072
  "blur"
7903
8073
  ],
7904
8074
  setup(props, { emit }) {
7905
- const innerW = computed(() => Math.max(0, Math.floor(props.w) - 2 - Math.max(0, Math.floor(props.padding)) * 2));
7906
- const innerH = computed(() => Math.max(1, Math.floor(props.h) - 2 - Math.max(0, Math.floor(props.padding)) * 2));
8075
+ const pad = computed(() => normalizePadding(props.padding));
8076
+ const innerW = computed(() => Math.max(0, Math.floor(props.w) - 2 - pad.value.left - pad.value.right));
8077
+ const innerH = computed(() => Math.max(1, Math.floor(props.h) - 2 - pad.value.top - pad.value.bottom));
7907
8078
  return () => h(TBox, {
7908
8079
  x: props.x,
7909
8080
  y: props.y,
@@ -7964,6 +8135,14 @@ const TList = defineComponent({
7964
8135
  type: Number,
7965
8136
  default: 0
7966
8137
  },
8138
+ padding: {
8139
+ type: [
8140
+ Number,
8141
+ String,
8142
+ Array
8143
+ ],
8144
+ default: 0
8145
+ },
7967
8146
  items: {
7968
8147
  type: Array,
7969
8148
  required: true
@@ -8022,8 +8201,9 @@ const TList = defineComponent({
8022
8201
  watchEffect(() => {
8023
8202
  active.value = clamp$2(props.modelValue, 0, Math.max(0, props.items.length - 1));
8024
8203
  });
8204
+ const contentRect$1 = computed(() => insetRect(absRect.value, props.padding));
8025
8205
  function ensureActiveVisible() {
8026
- const h$1 = Math.max(0, props.h);
8206
+ const h$1 = Math.max(0, Math.floor(contentRect$1.value.h));
8027
8207
  if (h$1 <= 0) return;
8028
8208
  const maxTop = Math.max(0, props.items.length - h$1);
8029
8209
  scrollTop.value = clamp$2(scrollTop.value, 0, maxTop);
@@ -8033,7 +8213,7 @@ const TList = defineComponent({
8033
8213
  watch([
8034
8214
  () => active.value,
8035
8215
  () => props.items.length,
8036
- () => props.h
8216
+ () => contentRect$1.value.h
8037
8217
  ], () => {
8038
8218
  ensureActiveVisible();
8039
8219
  }, { immediate: true });
@@ -8118,7 +8298,8 @@ const TList = defineComponent({
8118
8298
  focusable: true,
8119
8299
  handlers: {
8120
8300
  click: (e) => {
8121
- const r = absRect.value;
8301
+ const r = contentRect$1.value;
8302
+ if (e.cellY < r.y || e.cellY >= r.y + r.h) return;
8122
8303
  const idx = scrollTop.value + (e.cellY - r.y);
8123
8304
  if (idx >= 0 && idx < props.items.length) {
8124
8305
  active.value = idx;
@@ -8127,7 +8308,8 @@ const TList = defineComponent({
8127
8308
  } else emit("close");
8128
8309
  },
8129
8310
  dblclick: (e) => {
8130
- const r = absRect.value;
8311
+ const r = contentRect$1.value;
8312
+ if (e.cellY < r.y || e.cellY >= r.y + r.h) return;
8131
8313
  const idx = scrollTop.value + (e.cellY - r.y);
8132
8314
  if (idx >= 0 && idx < props.items.length) commit(idx);
8133
8315
  },
@@ -8135,7 +8317,8 @@ const TList = defineComponent({
8135
8317
  const delta = e.deltaY ?? 0;
8136
8318
  if (!delta) return;
8137
8319
  const dir = delta > 0 ? 1 : -1;
8138
- const maxTop = Math.max(0, props.items.length - props.h);
8320
+ const h$1 = Math.max(0, Math.floor(contentRect$1.value.h));
8321
+ const maxTop = Math.max(0, props.items.length - h$1);
8139
8322
  const next = clamp$2(scrollTop.value + dir, 0, maxTop);
8140
8323
  if (next !== scrollTop.value) {
8141
8324
  scrollTop.value = next;
@@ -8184,9 +8367,17 @@ const TList = defineComponent({
8184
8367
  ],
8185
8368
  paint: () => {
8186
8369
  if (!visible.value) return;
8187
- const r = absRect.value;
8188
- if (r.w <= 0 || r.h <= 0) return;
8370
+ const rAll = absRect.value;
8371
+ if (rAll.w <= 0 || rAll.h <= 0) return;
8189
8372
  const base = props.style ?? defaultStyle.value;
8373
+ const r = contentRect$1.value;
8374
+ const blankAll = " ".repeat(rAll.w);
8375
+ for (let i = 0; i < rAll.h; i++) terminal.write(blankAll, {
8376
+ x: rAll.x,
8377
+ y: rAll.y + i,
8378
+ style: base
8379
+ });
8380
+ if (r.w <= 0 || r.h <= 0) return;
8190
8381
  const top = clamp$2(scrollTop.value, 0, Math.max(0, props.items.length - r.h));
8191
8382
  for (let i = 0; i < r.h; i++) {
8192
8383
  const idx = top + i;
@@ -8247,6 +8438,14 @@ const TPathPicker = defineComponent({
8247
8438
  type: Number,
8248
8439
  default: 0
8249
8440
  },
8441
+ padding: {
8442
+ type: [
8443
+ Number,
8444
+ String,
8445
+ Array
8446
+ ],
8447
+ default: 0
8448
+ },
8250
8449
  workspace: {
8251
8450
  type: String,
8252
8451
  required: true
@@ -8304,13 +8503,12 @@ const TPathPicker = defineComponent({
8304
8503
  const scrollTop = ref(0);
8305
8504
  const error = ref(null);
8306
8505
  const inputH = 1;
8307
- const listH = computed(() => Math.max(0, Math.floor(props.h) - inputH));
8308
- const absListRect = computed(() => {
8506
+ const absRect = computed(() => {
8309
8507
  const raw = {
8310
8508
  x: props.x,
8311
- y: props.y + inputH,
8509
+ y: props.y,
8312
8510
  w: props.w,
8313
- h: listH.value
8511
+ h: props.h
8314
8512
  };
8315
8513
  const translated = translateRect(raw, layout.originX, layout.originY);
8316
8514
  if (!layout.clipRect) return translated;
@@ -8321,6 +8519,49 @@ const TPathPicker = defineComponent({
8321
8519
  h: 0
8322
8520
  };
8323
8521
  });
8522
+ const contentRect$1 = computed(() => insetRect(absRect.value, props.padding));
8523
+ const listH = computed(() => Math.max(0, Math.floor(contentRect$1.value.h) - inputH));
8524
+ const absListRect = computed(() => {
8525
+ const c = contentRect$1.value;
8526
+ const listRaw = {
8527
+ x: c.x,
8528
+ y: c.y + inputH,
8529
+ w: c.w,
8530
+ h: listH.value
8531
+ };
8532
+ return intersectRect(listRaw, absRect.value) ?? {
8533
+ x: 0,
8534
+ y: 0,
8535
+ w: 0,
8536
+ h: 0
8537
+ };
8538
+ });
8539
+ useRenderNode(() => ({
8540
+ zIndex: props.zIndex - 1,
8541
+ rect: absRect.value,
8542
+ deps: [
8543
+ visible.value,
8544
+ absRect.value,
8545
+ props.style,
8546
+ defaultStyle.value
8547
+ ],
8548
+ paint: (dirtyRows) => {
8549
+ if (!visible.value) return;
8550
+ const r = absRect.value;
8551
+ if (r.w <= 0 || r.h <= 0) return;
8552
+ const base = props.style ?? defaultStyle.value;
8553
+ if (!dirtyRows) {
8554
+ terminal.fill(r.x, r.y, r.w, r.h, " ", base);
8555
+ return;
8556
+ }
8557
+ const y0 = Math.floor(r.y);
8558
+ const y1 = y0 + Math.max(0, Math.floor(r.h));
8559
+ for (const y of dirtyRows) {
8560
+ if (y < y0 || y >= y1) continue;
8561
+ terminal.fill(r.x, y, r.w, 1, " ", base);
8562
+ }
8563
+ }
8564
+ }));
8324
8565
  function ensureActiveVisible() {
8325
8566
  const h$1 = Math.max(0, listH.value);
8326
8567
  if (h$1 <= 0) {
@@ -8538,7 +8779,7 @@ const TPathPicker = defineComponent({
8538
8779
  deps: [
8539
8780
  visible.value,
8540
8781
  absListRect.value,
8541
- props.w,
8782
+ contentRect$1.value.w,
8542
8783
  listH.value,
8543
8784
  props.style,
8544
8785
  defaultStyle.value,
@@ -8582,9 +8823,9 @@ const TPathPicker = defineComponent({
8582
8823
  }
8583
8824
  }));
8584
8825
  return () => h("span", rootProps, [h(TInput, {
8585
- "x": props.x,
8586
- "y": props.y,
8587
- "w": props.w,
8826
+ "x": contentRect$1.value.x,
8827
+ "y": contentRect$1.value.y,
8828
+ "w": contentRect$1.value.w,
8588
8829
  "h": inputH,
8589
8830
  "zIndex": props.zIndex,
8590
8831
  "modelValue": props.modelValue,
@@ -8634,14 +8875,30 @@ const TSelect = defineComponent({
8634
8875
  type: Number,
8635
8876
  default: 0
8636
8877
  },
8878
+ padding: {
8879
+ type: [
8880
+ Number,
8881
+ String,
8882
+ Array
8883
+ ],
8884
+ default: 0
8885
+ },
8637
8886
  options: {
8638
8887
  type: Array,
8639
8888
  required: true
8640
8889
  },
8641
8890
  modelValue: {
8642
- type: Number,
8891
+ type: [Number, Array],
8643
8892
  default: 0
8644
8893
  },
8894
+ multiple: {
8895
+ type: Boolean,
8896
+ default: false
8897
+ },
8898
+ multipleEmit: {
8899
+ type: String,
8900
+ default: "value"
8901
+ },
8645
8902
  style: {
8646
8903
  type: Object,
8647
8904
  default: void 0
@@ -8662,6 +8919,7 @@ const TSelect = defineComponent({
8662
8919
  emits: [
8663
8920
  "update:modelValue",
8664
8921
  "change",
8922
+ "confirm",
8665
8923
  "close",
8666
8924
  "focus",
8667
8925
  "blur",
@@ -8674,7 +8932,18 @@ const TSelect = defineComponent({
8674
8932
  const parentEventZ = inject(EventZIndexContextKey, computed(() => 0));
8675
8933
  const eventZ = computed(() => (parentEventZ.value ?? 0) + (props.zIndex ?? 0));
8676
8934
  const focused = ref(false);
8677
- const active = ref(props.modelValue);
8935
+ const initialActive = (() => {
8936
+ const max = Math.max(0, props.options.length - 1);
8937
+ if (!props.multiple) {
8938
+ const idx = typeof props.modelValue === "number" ? props.modelValue : 0;
8939
+ return clamp(idx, 0, max);
8940
+ }
8941
+ const selected = Array.isArray(props.modelValue) ? props.modelValue : [];
8942
+ const first = selected[0] ?? 0;
8943
+ return clamp(first, 0, max);
8944
+ })();
8945
+ const active = ref(initialActive);
8946
+ const scrollTopLine = ref(0);
8678
8947
  const absRect = computed(() => {
8679
8948
  const raw = {
8680
8949
  x: props.x,
@@ -8691,39 +8960,201 @@ const TSelect = defineComponent({
8691
8960
  h: 0
8692
8961
  };
8693
8962
  });
8963
+ const contentRect$1 = computed(() => insetRect(absRect.value, props.padding));
8694
8964
  watchEffect(() => {
8695
- active.value = clamp(props.modelValue, 0, Math.max(0, props.options.length - 1));
8965
+ const max = Math.max(0, props.options.length - 1);
8966
+ if (!props.multiple) {
8967
+ const idx = typeof props.modelValue === "number" ? props.modelValue : 0;
8968
+ active.value = clamp(idx, 0, max);
8969
+ return;
8970
+ }
8971
+ active.value = clamp(active.value, 0, max);
8696
8972
  });
8973
+ function getPrefixCells() {
8974
+ return props.multiple ? textCellWidth$1("[x] ") : 0;
8975
+ }
8697
8976
  function getOptionLabel(opt) {
8698
8977
  return typeof opt === "string" ? opt : opt.label;
8699
8978
  }
8700
8979
  function getOptionDetail(opt) {
8701
8980
  return typeof opt === "string" ? void 0 : opt.detail;
8702
8981
  }
8703
- function commit(index) {
8982
+ function commitSingle(index) {
8704
8983
  const next = clamp(index, 0, Math.max(0, props.options.length - 1));
8705
8984
  active.value = next;
8706
8985
  emit("update:modelValue", next);
8707
8986
  const opt = props.options[next];
8708
8987
  emit("change", opt ? getOptionLabel(opt) : null);
8709
8988
  }
8989
+ function getSelectedIndices() {
8990
+ if (!props.multiple) return [];
8991
+ const max = Math.max(0, props.options.length - 1);
8992
+ const raw = Array.isArray(props.modelValue) ? props.modelValue : [];
8993
+ const set = new Set();
8994
+ for (const v of raw) {
8995
+ if (typeof v !== "number" || !Number.isFinite(v)) continue;
8996
+ set.add(clamp(Math.trunc(v), 0, max));
8997
+ }
8998
+ return [...set].sort((a, b) => a - b);
8999
+ }
9000
+ function makeMultiplePayload(indices) {
9001
+ const values = indices.map((i) => props.options[i]).filter(Boolean).map((opt) => getOptionLabel(opt));
9002
+ return {
9003
+ indices,
9004
+ values
9005
+ };
9006
+ }
9007
+ function emitMultiple(name, indices) {
9008
+ const payload = makeMultiplePayload(indices);
9009
+ if (props.multipleEmit === "index") {
9010
+ emit(name, payload.indices);
9011
+ return;
9012
+ }
9013
+ if (props.multipleEmit === "both") {
9014
+ emit(name, payload);
9015
+ return;
9016
+ }
9017
+ emit(name, payload.values);
9018
+ }
9019
+ function toggleMultiple(index) {
9020
+ const nextIndex = clamp(index, 0, Math.max(0, props.options.length - 1));
9021
+ active.value = nextIndex;
9022
+ const set = new Set(getSelectedIndices());
9023
+ if (set.has(nextIndex)) set.delete(nextIndex);
9024
+ else set.add(nextIndex);
9025
+ const indices = [...set].sort((a, b) => a - b);
9026
+ emit("update:modelValue", indices);
9027
+ emitMultiple("change", indices);
9028
+ }
9029
+ function confirmMultiple() {
9030
+ const indices = getSelectedIndices();
9031
+ emitMultiple("confirm", indices);
9032
+ }
9033
+ function commit(index) {
9034
+ if (props.multiple) toggleMultiple(index);
9035
+ else commitSingle(index);
9036
+ }
9037
+ function computeColumnLayout(totalW, hasDetail, prefixCells) {
9038
+ totalW = Math.max(0, Math.floor(totalW));
9039
+ if (!hasDetail || totalW <= 0) return {
9040
+ labelW: totalW,
9041
+ gapW: 0,
9042
+ detailW: 0
9043
+ };
9044
+ const gapW = totalW >= 12 ? 2 : 1;
9045
+ const minLabelText = 6;
9046
+ const minLabelW = Math.min(totalW, prefixCells + minLabelText);
9047
+ const minDetailW = Math.min(12, Math.max(6, Math.floor(totalW / 4)));
9048
+ let detailW = clamp(Math.floor(totalW * .35), minDetailW, Math.max(0, totalW - gapW - minLabelW));
9049
+ let labelW = Math.max(0, totalW - gapW - detailW);
9050
+ if (labelW < minLabelW) {
9051
+ detailW = Math.max(0, totalW - gapW - minLabelW);
9052
+ labelW = Math.max(0, totalW - gapW - detailW);
9053
+ }
9054
+ if (detailW <= 0) return {
9055
+ labelW: totalW,
9056
+ gapW: 0,
9057
+ detailW: 0
9058
+ };
9059
+ return {
9060
+ labelW,
9061
+ gapW,
9062
+ detailW
9063
+ };
9064
+ }
9065
+ function safeWrap(text, width) {
9066
+ width = Math.max(0, Math.floor(width));
9067
+ if (width <= 0) return [""];
9068
+ return wrapByCells(text, width).map((l) => sliceByCells(l, width));
9069
+ }
9070
+ const optionLayouts = computed(() => {
9071
+ const r = contentRect$1.value;
9072
+ const w = Math.max(0, Math.floor(r.w));
9073
+ const prefixCells = getPrefixCells();
9074
+ const layouts = [];
9075
+ const flat = [];
9076
+ const starts = Array.from({ length: props.options.length }, () => 0);
9077
+ let line = 0;
9078
+ for (let i = 0; i < props.options.length; i++) {
9079
+ const opt = props.options[i];
9080
+ const label = sanitizeInlineText(getOptionLabel(opt));
9081
+ const detail = getOptionDetail(opt);
9082
+ const safeDetail = detail ? sanitizeInlineText(detail) : "";
9083
+ const cols = computeColumnLayout(w, Boolean(safeDetail), prefixCells);
9084
+ const labelTextW = Math.max(0, cols.labelW - prefixCells);
9085
+ const labelLines = safeWrap(label, labelTextW);
9086
+ const detailLines = cols.detailW > 0 && safeDetail ? safeWrap(safeDetail, cols.detailW) : [];
9087
+ const height = Math.max(1, labelLines.length, detailLines.length || 0);
9088
+ layouts.push({
9089
+ labelLines,
9090
+ detailLines,
9091
+ height,
9092
+ labelW: cols.labelW,
9093
+ gapW: cols.gapW,
9094
+ detailW: cols.detailW
9095
+ });
9096
+ starts[i] = line;
9097
+ for (let j = 0; j < height; j++) flat.push({
9098
+ optionIndex: i,
9099
+ subLine: j
9100
+ });
9101
+ line += height;
9102
+ }
9103
+ return {
9104
+ layouts,
9105
+ flat,
9106
+ starts,
9107
+ totalLines: line,
9108
+ contentW: w,
9109
+ prefixCells
9110
+ };
9111
+ });
9112
+ function ensureActiveVisible() {
9113
+ var _layouts$idx;
9114
+ const viewH = Math.max(0, Math.floor(contentRect$1.value.h));
9115
+ const { layouts, starts, totalLines } = optionLayouts.value;
9116
+ if (viewH <= 0 || totalLines <= 0) {
9117
+ scrollTopLine.value = 0;
9118
+ return;
9119
+ }
9120
+ const maxTop = Math.max(0, totalLines - viewH);
9121
+ scrollTopLine.value = clamp(scrollTopLine.value, 0, maxTop);
9122
+ const idx = clamp(active.value, 0, Math.max(0, layouts.length - 1));
9123
+ const start = starts[idx] ?? 0;
9124
+ const height = ((_layouts$idx = layouts[idx]) === null || _layouts$idx === void 0 ? void 0 : _layouts$idx.height) ?? 1;
9125
+ const end = start + height - 1;
9126
+ if (start < scrollTopLine.value) scrollTopLine.value = clamp(start, 0, maxTop);
9127
+ else if (end >= scrollTopLine.value + viewH) scrollTopLine.value = clamp(end - (viewH - 1), 0, maxTop);
9128
+ }
8710
9129
  function onKeydown(e) {
8711
9130
  emit("keydown", e);
8712
9131
  if (e.key === "ArrowUp") {
8713
9132
  e.preventDefault();
8714
9133
  active.value = clamp(active.value - 1, 0, Math.max(0, props.options.length - 1));
8715
- emit("update:modelValue", active.value);
9134
+ if (!props.multiple) emit("update:modelValue", active.value);
9135
+ ensureActiveVisible();
8716
9136
  scheduler.invalidate();
8717
9137
  return;
8718
9138
  }
8719
9139
  if (e.key === "ArrowDown") {
8720
9140
  e.preventDefault();
8721
9141
  active.value = clamp(active.value + 1, 0, Math.max(0, props.options.length - 1));
8722
- emit("update:modelValue", active.value);
9142
+ if (!props.multiple) emit("update:modelValue", active.value);
9143
+ ensureActiveVisible();
8723
9144
  scheduler.invalidate();
8724
9145
  return;
8725
9146
  }
9147
+ if (props.multiple && (e.code === "Space" || e.key === " " || e.key === "Spacebar")) {
9148
+ e.preventDefault();
9149
+ toggleMultiple(active.value);
9150
+ return;
9151
+ }
8726
9152
  if (e.key === "Enter") {
9153
+ if (props.multiple) {
9154
+ e.preventDefault();
9155
+ confirmMultiple();
9156
+ return;
9157
+ }
8727
9158
  commit(active.value);
8728
9159
  return;
8729
9160
  }
@@ -8739,11 +9170,31 @@ const TSelect = defineComponent({
8739
9170
  focusable: true,
8740
9171
  handlers: {
8741
9172
  click: (e) => {
8742
- const r = absRect.value;
8743
- const idx = e.cellY - r.y;
8744
- if (idx >= 0 && idx < props.options.length) commit(idx);
9173
+ const r = contentRect$1.value;
9174
+ if (e.cellY < r.y || e.cellY >= r.y + r.h) {
9175
+ emit("close");
9176
+ return;
9177
+ }
9178
+ const localRow = e.cellY - r.y;
9179
+ const { flat } = optionLayouts.value;
9180
+ const line = flat[scrollTopLine.value + localRow];
9181
+ if (line) commit(line.optionIndex);
8745
9182
  else emit("close");
8746
9183
  },
9184
+ wheel: (e) => {
9185
+ const delta = e.deltaY ?? 0;
9186
+ if (!delta) return;
9187
+ const dir = delta > 0 ? 1 : -1;
9188
+ const r = contentRect$1.value;
9189
+ const viewH = Math.max(0, Math.floor(r.h));
9190
+ const { totalLines } = optionLayouts.value;
9191
+ const maxTop = Math.max(0, totalLines - viewH);
9192
+ const next = clamp(scrollTopLine.value + dir, 0, maxTop);
9193
+ if (next !== scrollTopLine.value) {
9194
+ scrollTopLine.value = next;
9195
+ scheduler.invalidate();
9196
+ }
9197
+ },
8747
9198
  focus: () => {
8748
9199
  focused.value = true;
8749
9200
  emit("focus");
@@ -8758,6 +9209,10 @@ const TSelect = defineComponent({
8758
9209
  keydown: onKeydown
8759
9210
  }
8760
9211
  }));
9212
+ watchEffect(() => {
9213
+ optionLayouts.value;
9214
+ ensureActiveVisible();
9215
+ });
8761
9216
  watchEffect(() => {
8762
9217
  if (!props.autoFocus) return;
8763
9218
  if (!visible.value) return;
@@ -8775,20 +9230,25 @@ const TSelect = defineComponent({
8775
9230
  absRect.value,
8776
9231
  props.w,
8777
9232
  props.h,
9233
+ props.padding,
8778
9234
  props.options,
8779
9235
  props.modelValue,
9236
+ props.multiple,
9237
+ props.multipleEmit,
8780
9238
  props.style,
8781
9239
  props.highlightStyle,
8782
9240
  focused.value,
8783
9241
  active.value,
9242
+ scrollTopLine.value,
9243
+ optionLayouts.value,
8784
9244
  defaultStyle.value
8785
9245
  ],
8786
9246
  paint: () => {
8787
9247
  if (!visible.value) return;
8788
- const r = absRect.value;
8789
- if (r.w <= 0 || r.h <= 0) return;
9248
+ const rAll = absRect.value;
9249
+ if (rAll.w <= 0 || rAll.h <= 0) return;
8790
9250
  const base = props.style ?? defaultStyle.value;
8791
- const selectedBase = props.highlightStyle ?? {
9251
+ const highlightBase = props.highlightStyle ?? {
8792
9252
  ...base,
8793
9253
  bg: "yellow",
8794
9254
  fg: "black"
@@ -8797,57 +9257,79 @@ const TSelect = defineComponent({
8797
9257
  ...base,
8798
9258
  dim: true
8799
9259
  };
8800
- const selectedDetailStyle = {
8801
- ...selectedBase,
9260
+ const highlightDetailStyle = {
9261
+ ...highlightBase,
8802
9262
  dim: true
8803
9263
  };
8804
- for (let i = 0; i < r.h; i++) {
8805
- const opt = props.options[i];
8806
- if (!opt) {
8807
- terminal.write(" ".repeat(r.w), {
9264
+ const blankAll = " ".repeat(rAll.w);
9265
+ for (let i = 0; i < rAll.h; i++) terminal.write(blankAll, {
9266
+ x: rAll.x,
9267
+ y: rAll.y + i,
9268
+ style: base
9269
+ });
9270
+ const r = contentRect$1.value;
9271
+ if (r.w <= 0 || r.h <= 0) return;
9272
+ const { layouts, flat, totalLines, prefixCells } = optionLayouts.value;
9273
+ const selectedSet = props.multiple ? new Set(getSelectedIndices()) : null;
9274
+ const top = clamp(scrollTopLine.value, 0, Math.max(0, totalLines - r.h));
9275
+ const prefixSpaces = props.multiple ? " ".repeat(prefixCells) : "";
9276
+ const blank = " ".repeat(r.w);
9277
+ for (let row = 0; row < r.h; row++) {
9278
+ const y = r.y + row;
9279
+ const lineIdx = top + row;
9280
+ const line = flat[lineIdx];
9281
+ if (!line) {
9282
+ terminal.write(blank, {
8808
9283
  x: r.x,
8809
- y: r.y + i,
9284
+ y,
8810
9285
  style: base
8811
9286
  });
8812
9287
  continue;
8813
9288
  }
8814
- const isSelected = i === active.value;
8815
- const label = sanitizeInlineText(getOptionLabel(opt));
8816
- const detail = getOptionDetail(opt);
8817
- const rawDetail = detail ? sanitizeInlineText(detail) : "";
8818
- const labelCells = textCellWidth$1(label);
8819
- const minGap = 2;
8820
- const availableForDetail = r.w - labelCells - minGap;
8821
- if (rawDetail && availableForDetail >= 4) {
8822
- const labelStyle = isSelected ? selectedBase : base;
8823
- terminal.write(label, {
9289
+ const opt = props.options[line.optionIndex];
9290
+ if (!opt) {
9291
+ terminal.write(blank, {
8824
9292
  x: r.x,
8825
- y: r.y + i,
8826
- style: labelStyle
8827
- });
8828
- const detailCells = textCellWidth$1(rawDetail);
8829
- const actualDetailWidth = Math.min(detailCells, availableForDetail);
8830
- const gapWidth = r.w - labelCells - actualDetailWidth;
8831
- const gapStyle = isSelected ? selectedBase : base;
8832
- terminal.write(" ".repeat(gapWidth), {
8833
- x: r.x + labelCells,
8834
- y: r.y + i,
8835
- style: gapStyle
8836
- });
8837
- const detailText = sliceByCells(rawDetail, availableForDetail);
8838
- const dStyle = isSelected ? selectedDetailStyle : detailDimStyle;
8839
- terminal.write(detailText, {
8840
- x: r.x + labelCells + gapWidth,
8841
- y: r.y + i,
8842
- style: dStyle
9293
+ y,
9294
+ style: base
8843
9295
  });
8844
- } else {
8845
- const line = padEndByCells(sliceByCells(label, r.w), r.w);
8846
- const style = isSelected ? selectedBase : base;
8847
- terminal.write(line, {
8848
- x: r.x,
8849
- y: r.y + i,
8850
- style
9296
+ continue;
9297
+ }
9298
+ const isActiveOpt = line.optionIndex === active.value;
9299
+ const isHighlighted = props.multiple ? focused.value && isActiveOpt : isActiveOpt;
9300
+ const rowStyle = isHighlighted ? highlightBase : base;
9301
+ const detailStyle = isHighlighted ? highlightDetailStyle : detailDimStyle;
9302
+ terminal.write(blank, {
9303
+ x: r.x,
9304
+ y,
9305
+ style: rowStyle
9306
+ });
9307
+ const layout$1 = layouts[line.optionIndex];
9308
+ const labelLine = (layout$1 === null || layout$1 === void 0 ? void 0 : layout$1.labelLines[line.subLine]) ?? "";
9309
+ const prevVisibleLine = row > 0 ? flat[lineIdx - 1] : void 0;
9310
+ const isFirstVisibleLineOfOption = !prevVisibleLine || prevVisibleLine.optionIndex !== line.optionIndex;
9311
+ const detailLine = (() => {
9312
+ if (!layout$1 || layout$1.detailW <= 0 || layout$1.detailLines.length === 0) return "";
9313
+ if (layout$1.detailLines.length === 1) return isFirstVisibleLineOfOption ? layout$1.detailLines[0] ?? "" : "";
9314
+ return layout$1.detailLines[line.subLine] ?? "";
9315
+ })();
9316
+ const isChecked = props.multiple ? selectedSet.has(line.optionIndex) : isActiveOpt;
9317
+ const prefix = props.multiple ? isChecked ? "[x] " : "[ ] " : "";
9318
+ const prefixPad = props.multiple ? isFirstVisibleLineOfOption ? prefix : prefixSpaces : "";
9319
+ const labelTextW = Math.max(0, ((layout$1 === null || layout$1 === void 0 ? void 0 : layout$1.labelW) ?? r.w) - prefixCells);
9320
+ const labelText = labelTextW > 0 ? sliceByCells(labelLine, labelTextW) : "";
9321
+ const outLabel = padEndByCells(sliceByCells(`${prefixPad}${labelText}`, (layout$1 === null || layout$1 === void 0 ? void 0 : layout$1.labelW) ?? r.w), (layout$1 === null || layout$1 === void 0 ? void 0 : layout$1.labelW) ?? r.w);
9322
+ terminal.write(outLabel, {
9323
+ x: r.x,
9324
+ y,
9325
+ style: rowStyle
9326
+ });
9327
+ if (((layout$1 === null || layout$1 === void 0 ? void 0 : layout$1.detailW) ?? 0) > 0) {
9328
+ const outDetail = padEndByCells(sliceByCells(detailLine, layout$1.detailW), layout$1.detailW);
9329
+ terminal.write(outDetail, {
9330
+ x: r.x + layout$1.labelW + layout$1.gapW,
9331
+ y,
9332
+ style: detailStyle
8851
9333
  });
8852
9334
  }
8853
9335
  }