@simon_he/vue-tui 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.cjs +135 -389
  2. package/dist/index.js +161 -531
  3. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -3690,60 +3690,6 @@ function intersectRect(a, b) {
3690
3690
  function translateRect(rect, dx, dy) {
3691
3691
  return { x: rect.x + dx, y: rect.y + dy, w: rect.w, h: rect.h };
3692
3692
  }
3693
- function normalizePadding(padding) {
3694
- const nums = [];
3695
- if (typeof padding === "number") {
3696
- nums.push(padding);
3697
- } else if (Array.isArray(padding)) {
3698
- for (const v of padding)
3699
- nums.push(typeof v === "number" ? v : Number(v));
3700
- } else if (typeof padding === "string") {
3701
- for (const part of padding.trim().split(/\s+/g)) {
3702
- if (!part)
3703
- continue;
3704
- nums.push(Number(part));
3705
- }
3706
- }
3707
- const n = nums.map((v) => Number.isFinite(v) ? Math.max(0, Math.floor(v)) : 0);
3708
- const a = n[0] ?? 0;
3709
- const b = n[1] ?? a;
3710
- const c = n[2] ?? a;
3711
- const d = n[3] ?? b;
3712
- if (n.length <= 1)
3713
- return { top: a, right: a, bottom: a, left: a };
3714
- if (n.length === 2)
3715
- return { top: a, right: b, bottom: a, left: b };
3716
- if (n.length === 3)
3717
- return { top: a, right: b, bottom: c, left: b };
3718
- return { top: a, right: b, bottom: c, left: d };
3719
- }
3720
- function clampRectPadding(rect, padding) {
3721
- const requested = normalizePadding(padding);
3722
- const w = Math.max(0, Math.floor(rect.w));
3723
- const h16 = Math.max(0, Math.floor(rect.h));
3724
- const maxTotalX = Math.max(0, w - (w > 0 ? 1 : 0));
3725
- const maxTotalY = Math.max(0, h16 - (h16 > 0 ? 1 : 0));
3726
- if (requested.top === requested.right && requested.top === requested.bottom && requested.top === requested.left) {
3727
- const maxPadX = Math.max(0, Math.floor(maxTotalX / 2));
3728
- const maxPadY = Math.max(0, Math.floor(maxTotalY / 2));
3729
- const pad = Math.min(requested.top, maxPadX, maxPadY);
3730
- return { top: pad, right: pad, bottom: pad, left: pad };
3731
- }
3732
- const left = Math.min(requested.left, maxTotalX);
3733
- const right = Math.min(requested.right, Math.max(0, maxTotalX - left));
3734
- const top = Math.min(requested.top, maxTotalY);
3735
- const bottom = Math.min(requested.bottom, Math.max(0, maxTotalY - top));
3736
- return { top, right, bottom, left };
3737
- }
3738
- function insetRect(rect, padding) {
3739
- const pad = clampRectPadding(rect, padding);
3740
- return {
3741
- x: rect.x + pad.left,
3742
- y: rect.y + pad.top,
3743
- w: Math.max(0, rect.w - pad.left - pad.right),
3744
- h: Math.max(0, rect.h - pad.top - pad.bottom)
3745
- };
3746
- }
3747
3693
 
3748
3694
  // src/vue/components/TAnchor.ts
3749
3695
  var EMPTY_RECT = Object.freeze({ x: 0, y: 0, w: 0, h: 0 });
@@ -3757,7 +3703,6 @@ var TAnchor = (0, import_vue10.defineComponent)({
3757
3703
  w: { type: Number, default: void 0 },
3758
3704
  h: { type: Number, default: void 0 },
3759
3705
  zIndex: { type: Number, default: 0 },
3760
- padding: { type: [Number, String, Array], default: 0 },
3761
3706
  focusable: { type: Boolean, default: false },
3762
3707
  selectable: { type: Boolean, default: void 0 }
3763
3708
  },
@@ -3859,11 +3804,9 @@ var TAnchor = (0, import_vue10.defineComponent)({
3859
3804
  const childStack = (0, import_vue10.computed)(() => render.createStack(parentStack.value, props.zIndex));
3860
3805
  (0, import_vue10.watchEffect)(() => {
3861
3806
  const translated = rawRect.value;
3862
- const content = insetRect(translated, props.padding);
3863
- const contentClip = intersectRect(content, rect.value) ?? EMPTY_RECT;
3864
- childLayout.originX = content.x;
3865
- childLayout.originY = content.y;
3866
- childLayout.clipRect = contentClip;
3807
+ childLayout.originX = translated.x;
3808
+ childLayout.originY = translated.y;
3809
+ childLayout.clipRect = rect.value;
3867
3810
  });
3868
3811
  (0, import_vue10.provide)(LayoutContextKey, childLayout);
3869
3812
  (0, import_vue10.provide)(RenderStackKey, childStack);
@@ -4024,7 +3967,7 @@ var TBox = (0, import_vue12.defineComponent)({
4024
3967
  zIndex: { type: Number, default: 0 },
4025
3968
  border: { type: Boolean, default: true },
4026
3969
  title: { type: String, default: "" },
4027
- padding: { type: [Number, String, Array], default: 0 },
3970
+ padding: { type: Number, default: 0 },
4028
3971
  scrollX: { type: Number, default: 0 },
4029
3972
  scrollY: { type: Number, default: 0 },
4030
3973
  style: { type: Object, default: void 0 },
@@ -4098,14 +4041,17 @@ var TBox = (0, import_vue12.defineComponent)({
4098
4041
  (0, import_vue12.watchEffect)(() => {
4099
4042
  const r = absRect.value;
4100
4043
  const borderInset = props.border ? 1 : 0;
4101
- const inner = {
4102
- x: r.x + borderInset,
4103
- y: r.y + borderInset,
4104
- w: Math.max(0, r.w - borderInset * 2),
4105
- h: Math.max(0, r.h - borderInset * 2)
4044
+ const requestedPad = Math.max(0, Math.floor(props.padding));
4045
+ const maxPadX = Math.max(0, Math.floor((r.w - borderInset * 2 - 1) / 2));
4046
+ const maxPadY = Math.max(0, Math.floor((r.h - borderInset * 2 - 1) / 2));
4047
+ const pad = Math.min(requestedPad, maxPadX, maxPadY);
4048
+ const content = {
4049
+ x: r.x + borderInset + pad,
4050
+ y: r.y + borderInset + pad,
4051
+ w: Math.max(0, r.w - borderInset * 2 - pad * 2),
4052
+ h: Math.max(0, r.h - borderInset * 2 - pad * 2)
4106
4053
  };
4107
- const content = insetRect(inner, props.padding);
4108
- let contentRect2 = intersectRect(content, inner);
4054
+ let contentRect2 = intersectRect(content, r);
4109
4055
  if (parent.clipRect && contentRect2)
4110
4056
  contentRect2 = intersectRect(contentRect2, parent.clipRect);
4111
4057
  if (!contentRect2)
@@ -4179,7 +4125,6 @@ var TText = (0, import_vue13.defineComponent)({
4179
4125
  value: { type: String, required: true },
4180
4126
  w: { type: Number, default: void 0 },
4181
4127
  h: { type: Number, default: void 0 },
4182
- padding: { type: [Number, String, Array], default: 0 },
4183
4128
  style: { type: Object, default: void 0 },
4184
4129
  clear: { type: Boolean, default: true },
4185
4130
  wrap: { type: Boolean, default: false }
@@ -4188,13 +4133,8 @@ var TText = (0, import_vue13.defineComponent)({
4188
4133
  const { terminal, defaultStyle } = useTerminal();
4189
4134
  const layout = useLayout();
4190
4135
  const { visible, rootProps } = useVisibility();
4191
- const pad = (0, import_vue13.computed)(() => normalizePadding(props.padding));
4192
- const contentW = (0, import_vue13.computed)(() => {
4193
- const outerW = props.w ?? computeDefaultWidth(props.value) + pad.value.left + pad.value.right;
4194
- return Math.max(0, Math.floor(outerW) - pad.value.left - pad.value.right);
4195
- });
4196
4136
  const lines = (0, import_vue13.computed)(() => {
4197
- const w = contentW.value;
4137
+ const w = props.w ?? computeDefaultWidth(props.value);
4198
4138
  if (w <= 0)
4199
4139
  return [""];
4200
4140
  if (!props.wrap)
@@ -4203,8 +4143,8 @@ var TText = (0, import_vue13.defineComponent)({
4203
4143
  return wrapByCells(safe, w).map((l) => fitText(l, w));
4204
4144
  });
4205
4145
  const absRect = (0, import_vue13.computed)(() => {
4206
- const width = Math.floor(props.w ?? computeDefaultWidth(props.value) + pad.value.left + pad.value.right);
4207
- const height = Math.floor(props.h ?? (lines.value.length || 1) + pad.value.top + pad.value.bottom);
4146
+ const width = props.w ?? computeDefaultWidth(props.value);
4147
+ const height = props.h ?? (props.wrap ? lines.value.length || 1 : lines.value.length || 1);
4208
4148
  const raw = { x: props.x, y: props.y, w: width, h: height };
4209
4149
  const translated = translateRect(raw, layout.originX, layout.originY);
4210
4150
  if (!layout.clipRect)
@@ -4221,27 +4161,20 @@ var TText = (0, import_vue13.defineComponent)({
4221
4161
  const r = absRect.value;
4222
4162
  if (r.w <= 0 || r.h <= 0)
4223
4163
  return;
4224
- const contentX = r.x + pad.value.left;
4225
- const contentY = r.y + pad.value.top;
4226
- const w = Math.max(0, Math.floor(r.w) - pad.value.left - pad.value.right);
4227
- const h16 = Math.max(0, Math.floor(r.h) - pad.value.top - pad.value.bottom);
4228
4164
  const style = props.style ?? defaultStyle.value;
4229
4165
  const blank = props.clear ? " ".repeat(r.w) : "";
4230
4166
  const out = lines.value;
4231
- const maxLines = Math.min(out.length, h16);
4167
+ const maxLines = Math.min(out.length, Math.max(0, Math.floor(r.h)));
4232
4168
  const paintRow = (y) => {
4233
4169
  const i = y - r.y;
4234
4170
  if (i < 0 || i >= r.h)
4235
4171
  return;
4236
4172
  if (props.clear)
4237
4173
  terminal.write(blank, { x: r.x, y, style });
4238
- const contentRow = y - contentY;
4239
- if (contentRow < 0 || contentRow >= h16)
4240
- return;
4241
- if (contentRow < 0 || contentRow >= maxLines)
4174
+ if (i < 0 || i >= maxLines)
4242
4175
  return;
4243
- const clipped = sliceByCells(out[contentRow] ?? "", w);
4244
- terminal.write(padEndByCells(clipped, w), { x: contentX, y, style });
4176
+ const clipped = sliceByCells(out[i] ?? "", r.w);
4177
+ terminal.write(padEndByCells(clipped, r.w), { x: r.x, y, style });
4245
4178
  };
4246
4179
  if (!dirtyRows) {
4247
4180
  for (let i = 0; i < r.h; i++)
@@ -4267,7 +4200,6 @@ var TView = (0, import_vue14.defineComponent)({
4267
4200
  w: { type: Number, required: true },
4268
4201
  h: { type: Number, required: true },
4269
4202
  zIndex: { type: Number, default: 0 },
4270
- padding: { type: [Number, String, Array], default: 0 },
4271
4203
  scrollX: { type: Number, default: 0 },
4272
4204
  scrollY: { type: Number, default: 0 },
4273
4205
  focusable: { type: Boolean, default: false },
@@ -4360,11 +4292,9 @@ var TView = (0, import_vue14.defineComponent)({
4360
4292
  const childStack = (0, import_vue14.computed)(() => render.createStack(parentStack.value, props.zIndex));
4361
4293
  (0, import_vue14.watchEffect)(() => {
4362
4294
  const translated = rawRect.value;
4363
- const content = insetRect(translated, props.padding);
4364
- const contentClip = intersectRect(content, rect.value) ?? EMPTY_RECT2;
4365
- childLayout.originX = content.x - Math.floor(props.scrollX);
4366
- childLayout.originY = content.y - Math.floor(props.scrollY);
4367
- childLayout.clipRect = contentClip;
4295
+ childLayout.originX = translated.x - Math.floor(props.scrollX);
4296
+ childLayout.originY = translated.y - Math.floor(props.scrollY);
4297
+ childLayout.clipRect = rect.value;
4368
4298
  });
4369
4299
  (0, import_vue14.provide)(LayoutContextKey, childLayout);
4370
4300
  (0, import_vue14.provide)(RenderStackKey, childStack);
@@ -4521,10 +4451,10 @@ function computePosition(opts) {
4521
4451
  }
4522
4452
  function contentRect(opts) {
4523
4453
  const borderInset = opts.border ? 1 : 0;
4524
- const pad = normalizePadding(opts.padding);
4454
+ const pad = Math.max(0, Math.floor(opts.padding));
4525
4455
  return {
4526
- w: Math.max(0, Math.floor(opts.w) - borderInset * 2 - pad.left - pad.right),
4527
- h: Math.max(0, Math.floor(opts.h) - borderInset * 2 - pad.top - pad.bottom)
4456
+ w: Math.max(0, Math.floor(opts.w) - borderInset * 2 - pad * 2),
4457
+ h: Math.max(0, Math.floor(opts.h) - borderInset * 2 - pad * 2)
4528
4458
  };
4529
4459
  }
4530
4460
  var DialogSurface = (0, import_vue16.defineComponent)({
@@ -4534,7 +4464,7 @@ var DialogSurface = (0, import_vue16.defineComponent)({
4534
4464
  w: { type: Number, required: true },
4535
4465
  h: { type: Number, required: true },
4536
4466
  title: { type: String, default: "" },
4537
- padding: { type: [Number, String, Array], default: 1 },
4467
+ padding: { type: Number, default: 1 },
4538
4468
  zIndex: { type: Number, default: 1e3 },
4539
4469
  style: { type: Object, default: void 0 },
4540
4470
  placement: { type: String, default: "center" },
@@ -4767,7 +4697,7 @@ var TDialog = (0, import_vue16.defineComponent)({
4767
4697
  w: { type: Number, required: true },
4768
4698
  h: { type: Number, required: true },
4769
4699
  title: { type: String, default: "" },
4770
- padding: { type: [Number, String, Array], default: 1 },
4700
+ padding: { type: Number, default: 1 },
4771
4701
  zIndex: { type: Number, default: 1e3 },
4772
4702
  style: { type: Object, default: void 0 },
4773
4703
  placement: { type: String, default: "center" },
@@ -5753,7 +5683,6 @@ var TFlow = (0, import_vue18.defineComponent)({
5753
5683
  y: { type: Number, required: true },
5754
5684
  w: { type: Number, required: true },
5755
5685
  h: { type: Number, required: true },
5756
- padding: { type: [Number, String, Array], default: 0 },
5757
5686
  items: { type: Array, required: true },
5758
5687
  direction: { type: String, default: "vertical" },
5759
5688
  gap: { type: Number, default: 0 },
@@ -5761,16 +5690,13 @@ var TFlow = (0, import_vue18.defineComponent)({
5761
5690
  zIndex: { type: Number, default: 0 }
5762
5691
  },
5763
5692
  setup(props, { slots }) {
5764
- const pad = (0, import_vue18.computed)(() => normalizePadding(props.padding));
5765
- const innerW = (0, import_vue18.computed)(() => Math.max(0, Math.floor(props.w) - pad.value.left - pad.value.right));
5766
- const innerH = (0, import_vue18.computed)(() => Math.max(0, Math.floor(props.h) - pad.value.top - pad.value.bottom));
5767
5693
  return () => (0, import_vue18.h)(
5768
5694
  TView,
5769
- { x: props.x, y: props.y, w: props.w, h: props.h, zIndex: props.zIndex, padding: props.padding },
5695
+ { x: props.x, y: props.y, w: props.w, h: props.h, zIndex: props.zIndex },
5770
5696
  () => props.items.map((item, index) => {
5771
5697
  const step = Math.max(0, Math.floor(props.itemSize)) + Math.max(0, Math.floor(props.gap));
5772
5698
  const offset = index * step;
5773
- const childProps = props.direction === "vertical" ? { x: 0, y: offset, w: innerW.value, h: Math.max(0, Math.floor(props.itemSize)) } : { x: offset, y: 0, w: Math.max(0, Math.floor(props.itemSize)), h: innerH.value };
5699
+ const childProps = props.direction === "vertical" ? { x: 0, y: offset, w: props.w, h: Math.max(0, Math.floor(props.itemSize)) } : { x: offset, y: 0, w: Math.max(0, Math.floor(props.itemSize)), h: props.h };
5774
5700
  return (0, import_vue18.h)(
5775
5701
  TView,
5776
5702
  { key: index, ...childProps },
@@ -6131,7 +6057,6 @@ var TInput = (0, import_vue19.defineComponent)({
6131
6057
  w: { type: Number, required: true },
6132
6058
  h: { type: Number, default: 1 },
6133
6059
  zIndex: { type: Number, default: 0 },
6134
- padding: { type: [Number, String, Array], default: "0 1" },
6135
6060
  modelValue: { type: String, required: true },
6136
6061
  cursorToEndOnExternalUpdate: { type: Boolean, default: false },
6137
6062
  placeholder: { type: String, default: "" },
@@ -6165,6 +6090,14 @@ var TInput = (0, import_vue19.defineComponent)({
6165
6090
  const parentEventZ = (0, import_vue19.inject)(EventZIndexContextKey, (0, import_vue19.computed)(() => 0));
6166
6091
  const eventZ = (0, import_vue19.computed)(() => (parentEventZ.value ?? 0) + (props.zIndex ?? 0));
6167
6092
  const promptOverlayStack = render.createStack(render.rootStack, 1e4);
6093
+ const PADDING_X = 1;
6094
+ function measureContent(r) {
6095
+ const wAll = Math.max(0, Math.floor(r.w));
6096
+ const hAll = Math.max(0, Math.floor(r.h));
6097
+ const padX = clamp3(PADDING_X, 0, Math.floor(wAll / 2));
6098
+ const w = Math.max(0, wAll - padX * 2);
6099
+ return { wAll, hAll, padX, w };
6100
+ }
6168
6101
  const focused = (0, import_vue19.ref)(false);
6169
6102
  const cursor = (0, import_vue19.ref)(0);
6170
6103
  const anchor = (0, import_vue19.ref)(null);
@@ -6181,6 +6114,7 @@ var TInput = (0, import_vue19.defineComponent)({
6181
6114
  function getValue() {
6182
6115
  return pendingValue.value ?? props.modelValue;
6183
6116
  }
6117
+ const wrapMode = (0, import_vue19.computed)(() => Math.max(1, Math.floor(props.h)) > 1);
6184
6118
  const rawAbsRect = (0, import_vue19.computed)(() => {
6185
6119
  const raw = { x: props.x, y: props.y, w: props.w, h: Math.max(1, Math.floor(props.h)) };
6186
6120
  return translateRect(raw, layout.originX, layout.originY);
@@ -6424,8 +6358,6 @@ var TInput = (0, import_vue19.defineComponent)({
6424
6358
  return translated;
6425
6359
  return intersectRect(translated, layout.clipRect) ?? { x: 0, y: 0, w: 0, h: 0 };
6426
6360
  });
6427
- const contentRect2 = (0, import_vue19.computed)(() => insetRect(absRect.value, props.padding));
6428
- const wrapMode = (0, import_vue19.computed)(() => Math.max(1, Math.floor(contentRect2.value.h)) > 1);
6429
6361
  const selection = (0, import_vue19.computed)(() => {
6430
6362
  if (anchor.value == null || anchor.value === cursor.value)
6431
6363
  return null;
@@ -6508,16 +6440,10 @@ var TInput = (0, import_vue19.defineComponent)({
6508
6440
  return !focused.value || props.placeholderWhenFocused;
6509
6441
  });
6510
6442
  function ensureCursorVisible() {
6511
- const r = contentRect2.value;
6512
- const w = Math.max(0, Math.floor(r.w));
6513
- const h16 = Math.max(0, Math.floor(r.h));
6514
- if (w <= 0 || h16 <= 0) {
6515
- scrollX.value = 0;
6516
- scrollY.value = 0;
6517
- return;
6518
- }
6519
- const width = w;
6520
- const height = h16;
6443
+ const r = absRect.value;
6444
+ const { hAll, w: contentW } = measureContent(r);
6445
+ const width = Math.max(1, contentW);
6446
+ const height = Math.max(1, hAll);
6521
6447
  const value = getValue();
6522
6448
  const wrap = wrapMode.value;
6523
6449
  const prefix = buildMentionPrefix(props.mentions, width);
@@ -6543,16 +6469,13 @@ var TInput = (0, import_vue19.defineComponent)({
6543
6469
  }
6544
6470
  function setCursorByCell2D(cellX, cellY, extendSelection = false, e) {
6545
6471
  var _a;
6546
- const r = contentRect2.value;
6547
- const w = Math.max(0, Math.floor(r.w));
6548
- const h16 = Math.max(0, Math.floor(r.h));
6549
- if (w <= 0 || h16 <= 0)
6550
- return;
6551
- const width = w;
6472
+ const r = absRect.value;
6473
+ const { w: contentW, padX } = measureContent(r);
6474
+ const width = Math.max(1, contentW);
6552
6475
  const prefix = buildMentionPrefix(props.mentions, width);
6553
6476
  const prefixCells = prefix.cells;
6554
- const localX = clamp3(cellX - r.x, 0, Math.max(0, w - 1));
6555
- const localY = clamp3(cellY - r.y, 0, Math.max(0, h16 - 1));
6477
+ const localX = clamp3(cellX - (r.x + padX), 0, Math.max(0, contentW - 1));
6478
+ const localY = clamp3(cellY - r.y, 0, Math.max(0, r.h - 1));
6556
6479
  const prefixVisible = scrollY.value === 0 && localY === 0 && prefixCells > 0;
6557
6480
  if (prefixVisible && localX < prefixCells) {
6558
6481
  for (const chip of prefix.chips) {
@@ -6602,7 +6525,8 @@ var TInput = (0, import_vue19.defineComponent)({
6602
6525
  compositionText.value = "";
6603
6526
  pendingValue.value = nextValue;
6604
6527
  if (wrapMode.value) {
6605
- const w = Math.max(1, Math.floor(contentRect2.value.w));
6528
+ const { w: contentW } = measureContent(absRect.value);
6529
+ const w = Math.max(1, contentW);
6606
6530
  const prefix = buildMentionPrefix(props.mentions, w);
6607
6531
  const firstW = Math.max(1, w - prefix.cells);
6608
6532
  desiredCol.value = indexToWrappedCellColFirstWidth(nextValue, c, firstW, w).col;
@@ -6627,7 +6551,8 @@ var TInput = (0, import_vue19.defineComponent)({
6627
6551
  }
6628
6552
  cursor.value = clamp3(nextCursor, 0, value.length);
6629
6553
  if (wrapMode.value) {
6630
- const w = Math.max(1, Math.floor(contentRect2.value.w));
6554
+ const { w: contentW } = measureContent(absRect.value);
6555
+ const w = Math.max(1, contentW);
6631
6556
  const prefix = buildMentionPrefix(props.mentions, w);
6632
6557
  const firstW = Math.max(1, w - prefix.cells);
6633
6558
  desiredCol.value = indexToWrappedCellColFirstWidth(value, cursor.value, firstW, w).col;
@@ -6637,7 +6562,7 @@ var TInput = (0, import_vue19.defineComponent)({
6637
6562
  ensureCursorVisible();
6638
6563
  scheduler.invalidate();
6639
6564
  }
6640
- (0, import_vue19.watch)([() => contentRect2.value.w, () => contentRect2.value.h, () => wrapMode.value], () => {
6565
+ (0, import_vue19.watch)([() => absRect.value.w, () => absRect.value.h, () => wrapMode.value], () => {
6641
6566
  ensureCursorVisible();
6642
6567
  scheduler.invalidate();
6643
6568
  });
@@ -6684,15 +6609,14 @@ var TInput = (0, import_vue19.defineComponent)({
6684
6609
  imeAnchor.value = null;
6685
6610
  return;
6686
6611
  }
6687
- const r = contentRect2.value;
6688
- const w = Math.max(0, Math.floor(r.w));
6689
- const h16 = Math.max(0, Math.floor(r.h));
6690
- if (w <= 0 || h16 <= 0) {
6612
+ const r = absRect.value;
6613
+ if (r.w <= 0 || r.h <= 0) {
6691
6614
  imeAnchor.value = null;
6692
6615
  return;
6693
6616
  }
6694
- const width = w;
6695
- const height = h16;
6617
+ const { hAll, padX, w: contentW } = measureContent(r);
6618
+ const width = Math.max(1, contentW);
6619
+ const height = Math.max(1, hAll);
6696
6620
  const placeholderVisible = shouldShowPlaceholder.value;
6697
6621
  const offX = placeholderVisible ? 0 : scrollX.value;
6698
6622
  const offY = placeholderVisible ? 0 : scrollY.value;
@@ -6708,7 +6632,7 @@ var TInput = (0, import_vue19.defineComponent)({
6708
6632
  const cyRaw = pos.line - offY;
6709
6633
  const cx = clamp3(cxRaw, 0, Math.max(0, width - 1));
6710
6634
  const cy = clamp3(cyRaw, 0, Math.max(0, height - 1));
6711
- imeAnchor.value = { cellX: r.x + cx, cellY: r.y + cy };
6635
+ imeAnchor.value = { cellX: r.x + padX + cx, cellY: r.y + cy };
6712
6636
  });
6713
6637
  function acceptPrompt(index) {
6714
6638
  const ctx = activeContext.value;
@@ -6953,7 +6877,8 @@ var TInput = (0, import_vue19.defineComponent)({
6953
6877
  if (e.key === "ArrowUp" || e.key === "ArrowDown") {
6954
6878
  e.preventDefault();
6955
6879
  const wrap = wrapMode.value;
6956
- const width = Math.max(1, Math.floor(contentRect2.value.w));
6880
+ const { w: contentW } = measureContent(absRect.value);
6881
+ const width = Math.max(1, contentW);
6957
6882
  const prefix = buildMentionPrefix(props.mentions, width);
6958
6883
  const firstW = Math.max(1, width - prefix.cells);
6959
6884
  const { line, col, lines } = wrap ? indexToWrappedCellColFirstWidth(value, cursor.value, firstW, width) : indexToLineCellCol(value, cursor.value);
@@ -7188,7 +7113,6 @@ var TInput = (0, import_vue19.defineComponent)({
7188
7113
  props.mentions,
7189
7114
  props.placeholder,
7190
7115
  props.placeholderWhenFocused,
7191
- props.padding,
7192
7116
  props.style,
7193
7117
  props.autoFocus,
7194
7118
  props.cursorBlink,
@@ -7208,10 +7132,8 @@ var TInput = (0, import_vue19.defineComponent)({
7208
7132
  paint: () => {
7209
7133
  if (!visible.value)
7210
7134
  return;
7211
- const rAll = absRect.value;
7212
- const wAll = Math.max(0, Math.floor(rAll.w));
7213
- const hAll = Math.max(0, Math.floor(rAll.h));
7214
- if (wAll <= 0 || hAll <= 0)
7135
+ const r = absRect.value;
7136
+ if (r.w <= 0 || r.h <= 0)
7215
7137
  return;
7216
7138
  const style = props.style ?? defaultStyle.value;
7217
7139
  const placeholderVisible = shouldShowPlaceholder.value;
@@ -7227,21 +7149,14 @@ var TInput = (0, import_vue19.defineComponent)({
7227
7149
  }
7228
7150
  const text = props.secret && !placeholderVisible ? maskText(textRaw) : textRaw;
7229
7151
  const baseStyle = placeholderVisible ? { ...style, fg: "white" } : style;
7230
- const blankAll = " ".repeat(wAll);
7231
- for (let row = 0; row < hAll; row++)
7232
- terminal.write(blankAll, { x: rAll.x, y: rAll.y + row, style: baseStyle });
7233
- const r = contentRect2.value;
7234
- const w = Math.max(0, Math.floor(r.w));
7235
- const h16 = Math.max(0, Math.floor(r.h));
7236
- if (w <= 0 || h16 <= 0)
7237
- return;
7238
- const x0 = r.x;
7239
- const width = Math.max(1, w);
7152
+ const { wAll, padX, w: contentW } = measureContent(r);
7153
+ const x0 = r.x + padX;
7154
+ const width = Math.max(1, contentW);
7240
7155
  const prefix = buildMentionPrefix(props.mentions, width);
7241
7156
  const prefixCells = prefix.cells;
7242
7157
  const firstWidth = Math.max(1, Math.max(1, width) - prefixCells);
7243
7158
  const lines = wrap ? wrapToLinesFirstWidth(text, firstWidth, width) : computeLines(text);
7244
- for (let row = 0; row < h16; row++) {
7159
+ for (let row = 0; row < r.h; row++) {
7245
7160
  const lineIndex = offY + row;
7246
7161
  const info = lines[lineIndex];
7247
7162
  const raw = info ? text.slice(info.start, info.end).replace(/[\r\n]/g, "") : "";
@@ -7250,24 +7165,26 @@ var TInput = (0, import_vue19.defineComponent)({
7250
7165
  const rowTextW = hasPrefix ? firstWidth : width;
7251
7166
  const rawVisible = wrap ? sliceByCellsWindow(raw, 0, rowTextW) : sliceByCellsWindow(raw, offX, rowTextW);
7252
7167
  const visible2 = padEndByCells2(rawVisible, rowTextW);
7253
- const y = r.y + row;
7168
+ terminal.write(" ".repeat(wAll), { x: r.x, y: r.y + row, style: baseStyle });
7169
+ if (contentW <= 0)
7170
+ continue;
7254
7171
  if (hasPrefix) {
7255
7172
  const chipStyle = { ...style, fg: "cyanBright", underline: true };
7256
7173
  for (const chip of prefix.chips) {
7257
7174
  if (!chip.label)
7258
7175
  continue;
7259
- terminal.write(chip.label, { x: x0 + chip.start, y, style: chipStyle });
7176
+ terminal.write(chip.label, { x: x0 + chip.start, y: r.y + row, style: chipStyle });
7260
7177
  }
7261
- terminal.write(visible2, { x: x0 + rowPrefixCells, y, style: baseStyle });
7178
+ terminal.write(visible2, { x: x0 + rowPrefixCells, y: r.y + row, style: baseStyle });
7262
7179
  } else {
7263
- terminal.write(visible2, { x: x0, y, style: baseStyle });
7180
+ terminal.write(visible2, { x: x0, y: r.y + row, style: baseStyle });
7264
7181
  }
7265
7182
  }
7266
7183
  const sel = selection.value;
7267
7184
  if (sel && !placeholderVisible) {
7268
7185
  const bg = "blueBright";
7269
7186
  const fg = "black";
7270
- for (let row = 0; row < h16; row++) {
7187
+ for (let row = 0; row < r.h; row++) {
7271
7188
  const lineIndex = offY + row;
7272
7189
  const info = lines[lineIndex];
7273
7190
  if (!info)
@@ -7282,12 +7199,12 @@ var TInput = (0, import_vue19.defineComponent)({
7282
7199
  for (let i = info.start; i < info.end; ) {
7283
7200
  const ch = readCodePointAt(text, i);
7284
7201
  const nextI = i + ch.length;
7285
- const w2 = charCellWidth(ch);
7286
- if (xCell2 + w2 > rowTextW)
7202
+ const w = charCellWidth(ch);
7203
+ if (xCell2 + w > rowTextW)
7287
7204
  break;
7288
7205
  if (i >= sel.start && i < sel.end)
7289
7206
  terminal.put(xBase + xCell2, y, ch, { ...style, fg, bg });
7290
- xCell2 += w2;
7207
+ xCell2 += w;
7291
7208
  i = nextI;
7292
7209
  if (xCell2 >= rowTextW)
7293
7210
  break;
@@ -7299,8 +7216,8 @@ var TInput = (0, import_vue19.defineComponent)({
7299
7216
  for (let i = info.start; i < info.end; ) {
7300
7217
  const ch = readCodePointAt(text, i);
7301
7218
  const nextI = i + ch.length;
7302
- const w2 = charCellWidth(ch);
7303
- const nextCell = xCell + w2;
7219
+ const w = charCellWidth(ch);
7220
+ const nextCell = xCell + w;
7304
7221
  if (nextCell <= offX) {
7305
7222
  xCell = nextCell;
7306
7223
  i = nextI;
@@ -7309,7 +7226,7 @@ var TInput = (0, import_vue19.defineComponent)({
7309
7226
  if (xCell >= offX + rowTextW)
7310
7227
  break;
7311
7228
  const localX = xCell - offX;
7312
- if (localX + w2 > rowTextW)
7229
+ if (localX + w > rowTextW)
7313
7230
  break;
7314
7231
  if (i >= sel.start && i < sel.end)
7315
7232
  terminal.put(xBase + localX, y, ch, { ...style, fg, bg });
@@ -7330,7 +7247,7 @@ var TInput = (0, import_vue19.defineComponent)({
7330
7247
  const pos = wrap ? indexToWrappedCellColFirstWidth(cursorText, cursor.value, firstW, wAll2) : indexToLineCellCol(cursorText, cursor.value);
7331
7248
  const cx0 = (pos.line === 0 ? prefixCells : 0) + (pos.col - offX);
7332
7249
  const cy = pos.line - offY;
7333
- if (cx0 >= 0 && cx0 < width && cy >= 0 && cy < h16) {
7250
+ if (cx0 >= 0 && cx0 < width && cy >= 0 && cy < r.h) {
7334
7251
  const x = x0 + cx0;
7335
7252
  const y = r.y + cy;
7336
7253
  const avail = Math.max(0, width - cx0);
@@ -7350,7 +7267,7 @@ var TInput = (0, import_vue19.defineComponent)({
7350
7267
  const pos = wrap ? indexToWrappedCellColFirstWidth(cursorText, cursor.value, firstW, wAll2) : indexToLineCellCol(cursorText, cursor.value);
7351
7268
  const cx0 = (pos.line === 0 ? prefixCells : 0) + (pos.col - offX);
7352
7269
  const cy = pos.line - offY;
7353
- if (cx0 >= 0 && cx0 < width && cy >= 0 && cy < h16) {
7270
+ if (cx0 >= 0 && cx0 < width && cy >= 0 && cy < r.h) {
7354
7271
  const y = r.y + cy;
7355
7272
  let x = x0 + cx0;
7356
7273
  let cell = null;
@@ -7484,7 +7401,7 @@ var TInputBox = (0, import_vue20.defineComponent)({
7484
7401
  h: { type: Number, required: true },
7485
7402
  zIndex: { type: Number, default: 0 },
7486
7403
  title: { type: String, default: "" },
7487
- padding: { type: [Number, String, Array], default: 0 },
7404
+ padding: { type: Number, default: 0 },
7488
7405
  modelValue: { type: String, required: true },
7489
7406
  placeholder: { type: String, default: "" },
7490
7407
  style: { type: Object, default: void 0 },
@@ -7495,9 +7412,8 @@ var TInputBox = (0, import_vue20.defineComponent)({
7495
7412
  },
7496
7413
  emits: ["update:modelValue", "input", "change", "keydown", "focus", "blur"],
7497
7414
  setup(props, { emit }) {
7498
- const pad = (0, import_vue20.computed)(() => normalizePadding(props.padding));
7499
- const innerW = (0, import_vue20.computed)(() => Math.max(0, Math.floor(props.w) - 2 - pad.value.left - pad.value.right));
7500
- const innerH = (0, import_vue20.computed)(() => Math.max(1, Math.floor(props.h) - 2 - pad.value.top - pad.value.bottom));
7415
+ const innerW = (0, import_vue20.computed)(() => Math.max(0, Math.floor(props.w) - 2 - Math.max(0, Math.floor(props.padding)) * 2));
7416
+ const innerH = (0, import_vue20.computed)(() => Math.max(1, Math.floor(props.h) - 2 - Math.max(0, Math.floor(props.padding)) * 2));
7501
7417
  return () => (0, import_vue20.h)(
7502
7418
  TBox,
7503
7419
  {
@@ -7547,7 +7463,6 @@ var TList = (0, import_vue21.defineComponent)({
7547
7463
  w: { type: Number, required: true },
7548
7464
  h: { type: Number, required: true },
7549
7465
  zIndex: { type: Number, default: 0 },
7550
- padding: { type: [Number, String, Array], default: 0 },
7551
7466
  items: { type: Array, required: true },
7552
7467
  modelValue: { type: Number, default: 0 },
7553
7468
  style: { type: Object, default: void 0 },
@@ -7574,9 +7489,8 @@ var TList = (0, import_vue21.defineComponent)({
7574
7489
  (0, import_vue21.watchEffect)(() => {
7575
7490
  active.value = clamp4(props.modelValue, 0, Math.max(0, props.items.length - 1));
7576
7491
  });
7577
- const contentRect2 = (0, import_vue21.computed)(() => insetRect(absRect.value, props.padding));
7578
7492
  function ensureActiveVisible() {
7579
- const h16 = Math.max(0, Math.floor(contentRect2.value.h));
7493
+ const h16 = Math.max(0, props.h);
7580
7494
  if (h16 <= 0)
7581
7495
  return;
7582
7496
  const maxTop = Math.max(0, props.items.length - h16);
@@ -7586,7 +7500,7 @@ var TList = (0, import_vue21.defineComponent)({
7586
7500
  else if (active.value >= scrollTop.value + h16)
7587
7501
  scrollTop.value = clamp4(active.value - (h16 - 1), 0, maxTop);
7588
7502
  }
7589
- (0, import_vue21.watch)([() => active.value, () => props.items.length, () => contentRect2.value.h], () => {
7503
+ (0, import_vue21.watch)([() => active.value, () => props.items.length, () => props.h], () => {
7590
7504
  ensureActiveVisible();
7591
7505
  }, { immediate: true });
7592
7506
  function commit(index) {
@@ -7667,9 +7581,7 @@ var TList = (0, import_vue21.defineComponent)({
7667
7581
  focusable: true,
7668
7582
  handlers: {
7669
7583
  click: (e) => {
7670
- const r = contentRect2.value;
7671
- if (e.cellY < r.y || e.cellY >= r.y + r.h)
7672
- return;
7584
+ const r = absRect.value;
7673
7585
  const idx = scrollTop.value + (e.cellY - r.y);
7674
7586
  if (idx >= 0 && idx < props.items.length) {
7675
7587
  active.value = idx;
@@ -7680,9 +7592,7 @@ var TList = (0, import_vue21.defineComponent)({
7680
7592
  }
7681
7593
  },
7682
7594
  dblclick: (e) => {
7683
- const r = contentRect2.value;
7684
- if (e.cellY < r.y || e.cellY >= r.y + r.h)
7685
- return;
7595
+ const r = absRect.value;
7686
7596
  const idx = scrollTop.value + (e.cellY - r.y);
7687
7597
  if (idx >= 0 && idx < props.items.length)
7688
7598
  commit(idx);
@@ -7692,8 +7602,7 @@ var TList = (0, import_vue21.defineComponent)({
7692
7602
  if (!delta)
7693
7603
  return;
7694
7604
  const dir = delta > 0 ? 1 : -1;
7695
- const h16 = Math.max(0, Math.floor(contentRect2.value.h));
7696
- const maxTop = Math.max(0, props.items.length - h16);
7605
+ const maxTop = Math.max(0, props.items.length - props.h);
7697
7606
  const next = clamp4(scrollTop.value + dir, 0, maxTop);
7698
7607
  if (next !== scrollTop.value) {
7699
7608
  scrollTop.value = next;
@@ -7748,16 +7657,10 @@ var TList = (0, import_vue21.defineComponent)({
7748
7657
  paint: () => {
7749
7658
  if (!visible.value)
7750
7659
  return;
7751
- const rAll = absRect.value;
7752
- if (rAll.w <= 0 || rAll.h <= 0)
7753
- return;
7754
- const base = props.style ?? defaultStyle.value;
7755
- const r = contentRect2.value;
7756
- const blankAll = " ".repeat(rAll.w);
7757
- for (let i = 0; i < rAll.h; i++)
7758
- terminal.write(blankAll, { x: rAll.x, y: rAll.y + i, style: base });
7660
+ const r = absRect.value;
7759
7661
  if (r.w <= 0 || r.h <= 0)
7760
7662
  return;
7663
+ const base = props.style ?? defaultStyle.value;
7761
7664
  const top = clamp4(scrollTop.value, 0, Math.max(0, props.items.length - r.h));
7762
7665
  for (let i = 0; i < r.h; i++) {
7763
7666
  const idx = top + i;
@@ -7791,7 +7694,6 @@ var TPathPicker = (0, import_vue22.defineComponent)({
7791
7694
  w: { type: Number, required: true },
7792
7695
  h: { type: Number, required: true },
7793
7696
  zIndex: { type: Number, default: 0 },
7794
- padding: { type: [Number, String, Array], default: 0 },
7795
7697
  workspace: { type: String, required: true },
7796
7698
  mode: { type: String, default: "any" },
7797
7699
  modelValue: { type: String, required: true },
@@ -7815,44 +7717,14 @@ var TPathPicker = (0, import_vue22.defineComponent)({
7815
7717
  const scrollTop = (0, import_vue22.ref)(0);
7816
7718
  const error = (0, import_vue22.ref)(null);
7817
7719
  const inputH = 1;
7818
- const absRect = (0, import_vue22.computed)(() => {
7819
- const raw = { x: props.x, y: props.y, w: props.w, h: props.h };
7720
+ const listH = (0, import_vue22.computed)(() => Math.max(0, Math.floor(props.h) - inputH));
7721
+ const absListRect = (0, import_vue22.computed)(() => {
7722
+ const raw = { x: props.x, y: props.y + inputH, w: props.w, h: listH.value };
7820
7723
  const translated = translateRect(raw, layout.originX, layout.originY);
7821
7724
  if (!layout.clipRect)
7822
7725
  return translated;
7823
7726
  return intersectRect(translated, layout.clipRect) ?? { x: 0, y: 0, w: 0, h: 0 };
7824
7727
  });
7825
- const contentRect2 = (0, import_vue22.computed)(() => insetRect(absRect.value, props.padding));
7826
- const listH = (0, import_vue22.computed)(() => Math.max(0, Math.floor(contentRect2.value.h) - inputH));
7827
- const absListRect = (0, import_vue22.computed)(() => {
7828
- const c = contentRect2.value;
7829
- const listRaw = { x: c.x, y: c.y + inputH, w: c.w, h: listH.value };
7830
- return intersectRect(listRaw, absRect.value) ?? { x: 0, y: 0, w: 0, h: 0 };
7831
- });
7832
- useRenderNode(() => ({
7833
- zIndex: props.zIndex - 1,
7834
- rect: absRect.value,
7835
- deps: [visible.value, absRect.value, props.style, defaultStyle.value],
7836
- paint: (dirtyRows) => {
7837
- if (!visible.value)
7838
- return;
7839
- const r = absRect.value;
7840
- if (r.w <= 0 || r.h <= 0)
7841
- return;
7842
- const base = props.style ?? defaultStyle.value;
7843
- if (!dirtyRows) {
7844
- terminal.fill(r.x, r.y, r.w, r.h, " ", base);
7845
- return;
7846
- }
7847
- const y0 = Math.floor(r.y);
7848
- const y1 = y0 + Math.max(0, Math.floor(r.h));
7849
- for (const y of dirtyRows) {
7850
- if (y < y0 || y >= y1)
7851
- continue;
7852
- terminal.fill(r.x, y, r.w, 1, " ", base);
7853
- }
7854
- }
7855
- }));
7856
7728
  function ensureActiveVisible() {
7857
7729
  const h16 = Math.max(0, listH.value);
7858
7730
  if (h16 <= 0) {
@@ -8062,7 +7934,7 @@ var TPathPicker = (0, import_vue22.defineComponent)({
8062
7934
  deps: [
8063
7935
  visible.value,
8064
7936
  absListRect.value,
8065
- contentRect2.value.w,
7937
+ props.w,
8066
7938
  listH.value,
8067
7939
  props.style,
8068
7940
  defaultStyle.value,
@@ -8095,9 +7967,9 @@ var TPathPicker = (0, import_vue22.defineComponent)({
8095
7967
  }));
8096
7968
  return () => (0, import_vue22.h)("span", rootProps, [
8097
7969
  (0, import_vue22.h)(TInput, {
8098
- "x": contentRect2.value.x,
8099
- "y": contentRect2.value.y,
8100
- "w": contentRect2.value.w,
7970
+ "x": props.x,
7971
+ "y": props.y,
7972
+ "w": props.w,
8101
7973
  "h": inputH,
8102
7974
  "zIndex": props.zIndex,
8103
7975
  "modelValue": props.modelValue,
@@ -8136,7 +8008,6 @@ var TSelect = (0, import_vue23.defineComponent)({
8136
8008
  w: { type: Number, required: true },
8137
8009
  h: { type: Number, required: true },
8138
8010
  zIndex: { type: Number, default: 0 },
8139
- padding: { type: [Number, String, Array], default: 0 },
8140
8011
  options: { type: Array, required: true },
8141
8012
  modelValue: { type: [Number, Array], default: 0 },
8142
8013
  multiple: { type: Boolean, default: false },
@@ -8165,7 +8036,6 @@ var TSelect = (0, import_vue23.defineComponent)({
8165
8036
  return clamp6(first, 0, max);
8166
8037
  })();
8167
8038
  const active = (0, import_vue23.ref)(initialActive);
8168
- const scrollTopLine = (0, import_vue23.ref)(0);
8169
8039
  const absRect = (0, import_vue23.computed)(() => {
8170
8040
  const raw = { x: props.x, y: props.y, w: props.w, h: props.h };
8171
8041
  const translated = translateRect(raw, layout.originX, layout.originY);
@@ -8173,7 +8043,6 @@ var TSelect = (0, import_vue23.defineComponent)({
8173
8043
  return translated;
8174
8044
  return intersectRect(translated, layout.clipRect) ?? { x: 0, y: 0, w: 0, h: 0 };
8175
8045
  });
8176
- const contentRect2 = (0, import_vue23.computed)(() => insetRect(absRect.value, props.padding));
8177
8046
  (0, import_vue23.watchEffect)(() => {
8178
8047
  const max = Math.max(0, props.options.length - 1);
8179
8048
  if (!props.multiple) {
@@ -8183,9 +8052,6 @@ var TSelect = (0, import_vue23.defineComponent)({
8183
8052
  }
8184
8053
  active.value = clamp6(active.value, 0, max);
8185
8054
  });
8186
- function getPrefixCells() {
8187
- return props.multiple ? textCellWidth3("[x] ") : 0;
8188
- }
8189
8055
  function getOptionLabel(opt) {
8190
8056
  return typeof opt === "string" ? opt : opt.label;
8191
8057
  }
@@ -8250,82 +8116,6 @@ var TSelect = (0, import_vue23.defineComponent)({
8250
8116
  else
8251
8117
  commitSingle(index);
8252
8118
  }
8253
- function computeColumnLayout(totalW, hasDetail, prefixCells) {
8254
- totalW = Math.max(0, Math.floor(totalW));
8255
- if (!hasDetail || totalW <= 0)
8256
- return { labelW: totalW, gapW: 0, detailW: 0 };
8257
- const gapW = totalW >= 12 ? 2 : 1;
8258
- const minLabelText = 6;
8259
- const minLabelW = Math.min(totalW, prefixCells + minLabelText);
8260
- const minDetailW = Math.min(12, Math.max(6, Math.floor(totalW / 4)));
8261
- let detailW = clamp6(Math.floor(totalW * 0.35), minDetailW, Math.max(0, totalW - gapW - minLabelW));
8262
- let labelW = Math.max(0, totalW - gapW - detailW);
8263
- if (labelW < minLabelW) {
8264
- detailW = Math.max(0, totalW - gapW - minLabelW);
8265
- labelW = Math.max(0, totalW - gapW - detailW);
8266
- }
8267
- if (detailW <= 0)
8268
- return { labelW: totalW, gapW: 0, detailW: 0 };
8269
- return { labelW, gapW, detailW };
8270
- }
8271
- function safeWrap(text, width) {
8272
- width = Math.max(0, Math.floor(width));
8273
- if (width <= 0)
8274
- return [""];
8275
- return wrapByCells(text, width).map((l) => sliceByCells(l, width));
8276
- }
8277
- const optionLayouts = (0, import_vue23.computed)(() => {
8278
- const r = contentRect2.value;
8279
- const w = Math.max(0, Math.floor(r.w));
8280
- const prefixCells = getPrefixCells();
8281
- const layouts = [];
8282
- const flat = [];
8283
- const starts = Array.from({ length: props.options.length }, () => 0);
8284
- let line = 0;
8285
- for (let i = 0; i < props.options.length; i++) {
8286
- const opt = props.options[i];
8287
- const label = sanitizeInlineText(getOptionLabel(opt));
8288
- const detail = getOptionDetail(opt);
8289
- const safeDetail = detail ? sanitizeInlineText(detail) : "";
8290
- const cols = computeColumnLayout(w, Boolean(safeDetail), prefixCells);
8291
- const labelTextW = Math.max(0, cols.labelW - prefixCells);
8292
- const labelLines = safeWrap(label, labelTextW);
8293
- const detailLines = cols.detailW > 0 && safeDetail ? safeWrap(safeDetail, cols.detailW) : [];
8294
- const height = Math.max(1, labelLines.length, detailLines.length || 0);
8295
- layouts.push({
8296
- labelLines,
8297
- detailLines,
8298
- height,
8299
- labelW: cols.labelW,
8300
- gapW: cols.gapW,
8301
- detailW: cols.detailW
8302
- });
8303
- starts[i] = line;
8304
- for (let j = 0; j < height; j++)
8305
- flat.push({ optionIndex: i, subLine: j });
8306
- line += height;
8307
- }
8308
- return { layouts, flat, starts, totalLines: line, contentW: w, prefixCells };
8309
- });
8310
- function ensureActiveVisible() {
8311
- var _a;
8312
- const viewH = Math.max(0, Math.floor(contentRect2.value.h));
8313
- const { layouts, starts, totalLines } = optionLayouts.value;
8314
- if (viewH <= 0 || totalLines <= 0) {
8315
- scrollTopLine.value = 0;
8316
- return;
8317
- }
8318
- const maxTop = Math.max(0, totalLines - viewH);
8319
- scrollTopLine.value = clamp6(scrollTopLine.value, 0, maxTop);
8320
- const idx = clamp6(active.value, 0, Math.max(0, layouts.length - 1));
8321
- const start = starts[idx] ?? 0;
8322
- const height = ((_a = layouts[idx]) == null ? void 0 : _a.height) ?? 1;
8323
- const end = start + height - 1;
8324
- if (start < scrollTopLine.value)
8325
- scrollTopLine.value = clamp6(start, 0, maxTop);
8326
- else if (end >= scrollTopLine.value + viewH)
8327
- scrollTopLine.value = clamp6(end - (viewH - 1), 0, maxTop);
8328
- }
8329
8119
  function onKeydown(e) {
8330
8120
  emit("keydown", e);
8331
8121
  if (e.key === "ArrowUp") {
@@ -8333,7 +8123,6 @@ var TSelect = (0, import_vue23.defineComponent)({
8333
8123
  active.value = clamp6(active.value - 1, 0, Math.max(0, props.options.length - 1));
8334
8124
  if (!props.multiple)
8335
8125
  emit("update:modelValue", active.value);
8336
- ensureActiveVisible();
8337
8126
  scheduler.invalidate();
8338
8127
  return;
8339
8128
  }
@@ -8342,7 +8131,6 @@ var TSelect = (0, import_vue23.defineComponent)({
8342
8131
  active.value = clamp6(active.value + 1, 0, Math.max(0, props.options.length - 1));
8343
8132
  if (!props.multiple)
8344
8133
  emit("update:modelValue", active.value);
8345
- ensureActiveVisible();
8346
8134
  scheduler.invalidate();
8347
8135
  return;
8348
8136
  }
@@ -8372,34 +8160,13 @@ var TSelect = (0, import_vue23.defineComponent)({
8372
8160
  focusable: true,
8373
8161
  handlers: {
8374
8162
  click: (e) => {
8375
- const r = contentRect2.value;
8376
- if (e.cellY < r.y || e.cellY >= r.y + r.h) {
8377
- emit("close");
8378
- return;
8379
- }
8380
- const localRow = e.cellY - r.y;
8381
- const { flat } = optionLayouts.value;
8382
- const line = flat[scrollTopLine.value + localRow];
8383
- if (line)
8384
- commit(line.optionIndex);
8163
+ const r = absRect.value;
8164
+ const idx = e.cellY - r.y;
8165
+ if (idx >= 0 && idx < props.options.length)
8166
+ commit(idx);
8385
8167
  else
8386
8168
  emit("close");
8387
8169
  },
8388
- wheel: (e) => {
8389
- const delta = e.deltaY ?? 0;
8390
- if (!delta)
8391
- return;
8392
- const dir = delta > 0 ? 1 : -1;
8393
- const r = contentRect2.value;
8394
- const viewH = Math.max(0, Math.floor(r.h));
8395
- const { totalLines } = optionLayouts.value;
8396
- const maxTop = Math.max(0, totalLines - viewH);
8397
- const next = clamp6(scrollTopLine.value + dir, 0, maxTop);
8398
- if (next !== scrollTopLine.value) {
8399
- scrollTopLine.value = next;
8400
- scheduler.invalidate();
8401
- }
8402
- },
8403
8170
  focus: () => {
8404
8171
  focused.value = true;
8405
8172
  emit("focus");
@@ -8415,10 +8182,6 @@ var TSelect = (0, import_vue23.defineComponent)({
8415
8182
  keydown: onKeydown
8416
8183
  }
8417
8184
  }));
8418
- (0, import_vue23.watchEffect)(() => {
8419
- void optionLayouts.value;
8420
- ensureActiveVisible();
8421
- });
8422
8185
  (0, import_vue23.watchEffect)(() => {
8423
8186
  if (!props.autoFocus)
8424
8187
  return;
@@ -8435,67 +8198,50 @@ var TSelect = (0, import_vue23.defineComponent)({
8435
8198
  useRenderNode(() => ({
8436
8199
  zIndex: props.zIndex,
8437
8200
  rect: absRect.value,
8438
- deps: [visible.value, absRect.value, props.w, props.h, props.padding, props.options, props.modelValue, props.multiple, props.multipleEmit, props.style, props.highlightStyle, focused.value, active.value, scrollTopLine.value, optionLayouts.value, defaultStyle.value],
8201
+ deps: [visible.value, absRect.value, props.w, props.h, props.options, props.modelValue, props.multiple, props.multipleEmit, props.style, props.highlightStyle, focused.value, active.value, defaultStyle.value],
8439
8202
  paint: () => {
8440
8203
  if (!visible.value)
8441
8204
  return;
8442
- const rAll = absRect.value;
8443
- if (rAll.w <= 0 || rAll.h <= 0)
8205
+ const r = absRect.value;
8206
+ if (r.w <= 0 || r.h <= 0)
8444
8207
  return;
8445
8208
  const base = props.style ?? defaultStyle.value;
8446
8209
  const highlightBase = props.highlightStyle ?? { ...base, bg: "yellow", fg: "black" };
8447
8210
  const detailDimStyle = { ...base, dim: true };
8448
8211
  const highlightDetailStyle = { ...highlightBase, dim: true };
8449
- const blankAll = " ".repeat(rAll.w);
8450
- for (let i = 0; i < rAll.h; i++)
8451
- terminal.write(blankAll, { x: rAll.x, y: rAll.y + i, style: base });
8452
- const r = contentRect2.value;
8453
- if (r.w <= 0 || r.h <= 0)
8454
- return;
8455
- const { layouts, flat, totalLines, prefixCells } = optionLayouts.value;
8456
8212
  const selectedSet = props.multiple ? new Set(getSelectedIndices()) : null;
8457
- const top = clamp6(scrollTopLine.value, 0, Math.max(0, totalLines - r.h));
8458
- const prefixSpaces = props.multiple ? " ".repeat(prefixCells) : "";
8459
- const blank = " ".repeat(r.w);
8460
- for (let row = 0; row < r.h; row++) {
8461
- const y = r.y + row;
8462
- const lineIdx = top + row;
8463
- const line = flat[lineIdx];
8464
- if (!line) {
8465
- terminal.write(blank, { x: r.x, y, style: base });
8466
- continue;
8467
- }
8468
- const opt = props.options[line.optionIndex];
8213
+ for (let i = 0; i < r.h; i++) {
8214
+ const opt = props.options[i];
8469
8215
  if (!opt) {
8470
- terminal.write(blank, { x: r.x, y, style: base });
8216
+ terminal.write(" ".repeat(r.w), { x: r.x, y: r.y + i, style: base });
8471
8217
  continue;
8472
8218
  }
8473
- const isActiveOpt = line.optionIndex === active.value;
8474
- const isHighlighted = props.multiple ? focused.value && isActiveOpt : isActiveOpt;
8475
- const rowStyle = isHighlighted ? highlightBase : base;
8476
- const detailStyle = isHighlighted ? highlightDetailStyle : detailDimStyle;
8477
- terminal.write(blank, { x: r.x, y, style: rowStyle });
8478
- const layout2 = layouts[line.optionIndex];
8479
- const labelLine = (layout2 == null ? void 0 : layout2.labelLines[line.subLine]) ?? "";
8480
- const prevVisibleLine = row > 0 ? flat[lineIdx - 1] : void 0;
8481
- const isFirstVisibleLineOfOption = !prevVisibleLine || prevVisibleLine.optionIndex !== line.optionIndex;
8482
- const detailLine = (() => {
8483
- if (!layout2 || layout2.detailW <= 0 || layout2.detailLines.length === 0)
8484
- return "";
8485
- if (layout2.detailLines.length === 1)
8486
- return isFirstVisibleLineOfOption ? layout2.detailLines[0] ?? "" : "";
8487
- return layout2.detailLines[line.subLine] ?? "";
8488
- })();
8489
- const isChecked = props.multiple ? selectedSet.has(line.optionIndex) : isActiveOpt;
8219
+ const isActiveRow = i === active.value;
8220
+ const isChecked = props.multiple ? selectedSet.has(i) : isActiveRow;
8221
+ const isHighlighted = props.multiple ? focused.value && isActiveRow : isActiveRow;
8490
8222
  const prefix = props.multiple ? isChecked ? "[x] " : "[ ] " : "";
8491
- const prefixPad = props.multiple ? isFirstVisibleLineOfOption ? prefix : prefixSpaces : "";
8492
- const labelTextW = Math.max(0, ((layout2 == null ? void 0 : layout2.labelW) ?? r.w) - prefixCells);
8493
- const labelText = labelTextW > 0 ? sliceByCells(labelLine, labelTextW) : "";
8494
- const outLabel = padEndByCells(sliceByCells(`${prefixPad}${labelText}`, (layout2 == null ? void 0 : layout2.labelW) ?? r.w), (layout2 == null ? void 0 : layout2.labelW) ?? r.w);
8495
- terminal.write(outLabel, { x: r.x, y, style: rowStyle });
8496
- if (((layout2 == null ? void 0 : layout2.detailW) ?? 0) > 0) {
8497
- const outDetail = padEndByCells(sliceByCells(detailLine, layout2.detailW), layout2.detailW);
8498
- terminal.write(outDetail, { x: r.x + layout2.labelW + layout2.gapW, y, style: detailStyle });
8223
+ const label = sanitizeInlineText(getOptionLabel(opt));
8224
+ const detail = getOptionDetail(opt);
8225
+ const rawDetail = detail ? sanitizeInlineText(detail) : "";
8226
+ const labelText = `${prefix}${label}`;
8227
+ const labelCells = textCellWidth3(labelText);
8228
+ const minGap = 2;
8229
+ const availableForDetail = r.w - labelCells - minGap;
8230
+ if (rawDetail && availableForDetail >= 4) {
8231
+ const labelStyle = isHighlighted ? highlightBase : base;
8232
+ terminal.write(labelText, { x: r.x, y: r.y + i, style: labelStyle });
8233
+ const detailCells = textCellWidth3(rawDetail);
8234
+ const actualDetailWidth = Math.min(detailCells, availableForDetail);
8235
+ const gapWidth = r.w - labelCells - actualDetailWidth;
8236
+ const gapStyle = isHighlighted ? highlightBase : base;
8237
+ terminal.write(" ".repeat(gapWidth), { x: r.x + labelCells, y: r.y + i, style: gapStyle });
8238
+ const detailText = sliceByCells(rawDetail, availableForDetail);
8239
+ const dStyle = isHighlighted ? highlightDetailStyle : detailDimStyle;
8240
+ terminal.write(detailText, { x: r.x + labelCells + gapWidth, y: r.y + i, style: dStyle });
8241
+ } else {
8242
+ const line = padEndByCells(sliceByCells(labelText, r.w), r.w);
8243
+ const style = isHighlighted ? highlightBase : base;
8244
+ terminal.write(line, { x: r.x, y: r.y + i, style });
8499
8245
  }
8500
8246
  }
8501
8247
  }