@astryx-vue/lab 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { MaybeRef, VNodeChild } from "vue";
2
- import { FieldStatusInput, IconSize, PopoverAlignment, PopoverPlacement, SyntaxThemeDefinition } from "@astryx-vue/core";
2
+ import { FieldStatusInput, IconSize, PopoverAlignment, PopoverPlacement, SyntaxThemeDefinition, TokenLine } from "@astryx-vue/core";
3
3
  //#region src/CircularProgress/types.d.ts
4
4
  /** Variants a theme can extend through declaration merging in TypeScript. */
5
5
  interface CircularProgressVariantMap {
@@ -77,14 +77,47 @@ declare const __VLS_base$6: import("vue").DefineComponent<CircularProgressProps,
77
77
  isIndeterminate: boolean;
78
78
  isDisabled: boolean;
79
79
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
80
- declare const __VLS_export$11: __VLS_WithSlots$6<typeof __VLS_base$6, __VLS_Slots$6>;
81
- declare const _default: typeof __VLS_export$11;
80
+ declare const __VLS_export$12: __VLS_WithSlots$6<typeof __VLS_base$6, __VLS_Slots$6>;
81
+ declare const _default: typeof __VLS_export$12;
82
82
  type __VLS_WithSlots$6<T, S> = T & {
83
83
  new (): {
84
84
  $slots: S;
85
85
  };
86
86
  };
87
87
  //#endregion
88
+ //#region src/CodeEditor/types.d.ts
89
+ interface CodeEditorProps {
90
+ /** 受控文本。 */
91
+ value: string;
92
+ /** 高亮语言,默认 plaintext。 */
93
+ language?: string;
94
+ /** 可编辑区域的无障碍名称。 */
95
+ label: string;
96
+ hasLineNumbers?: boolean;
97
+ isReadOnly?: boolean;
98
+ placeholder?: string;
99
+ maxHeight?: number | string;
100
+ size?: 'sm' | 'md';
101
+ tokenizer?: (code: string, language: string) => TokenLine[];
102
+ /** 告诉读屏用户如何离开 Tab 缩进区域。 */
103
+ escapeHint?: string;
104
+ }
105
+ interface CodeEditorEmits {
106
+ change: [value: string];
107
+ }
108
+ //#endregion
109
+ //#region src/CodeEditor/CodeEditor.vue.d.ts
110
+ declare const __VLS_export$11: import("vue").DefineComponent<CodeEditorProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
111
+ change: (value: string) => any;
112
+ }, string, import("vue").PublicProps, Readonly<CodeEditorProps> & Readonly<{
113
+ onChange?: ((value: string) => any) | undefined;
114
+ }>, {
115
+ size: "md" | "sm";
116
+ language: string;
117
+ escapeHint: string;
118
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
119
+ declare const _default$1: typeof __VLS_export$11;
120
+ //#endregion
88
121
  //#region src/DiffViewer/diffTypes.d.ts
89
122
  /**
90
123
  * @file diffTypes.ts
@@ -300,9 +333,9 @@ declare const __VLS_base$5: import("vue").DefineComponent<DiffViewerProps, {}, {
300
333
  }>, {
301
334
  label: string;
302
335
  maxHeight: string | number;
303
- viewMode: DiffViewerViewMode;
304
336
  language: string;
305
337
  hasLineNumbers: boolean;
338
+ viewMode: DiffViewerViewMode;
306
339
  isWrapped: boolean;
307
340
  inlineHighlight: DiffViewerInlineHighlight;
308
341
  changeIndicator: DiffViewerChangeIndicator;
@@ -314,7 +347,7 @@ declare const __VLS_base$5: import("vue").DefineComponent<DiffViewerProps, {}, {
314
347
  renderLineAnnotation: (ref: DiffViewerLineRef) => import("vue").VNodeChild;
315
348
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
316
349
  declare const __VLS_export$10: __VLS_WithSlots$5<typeof __VLS_base$5, __VLS_Slots$5>;
317
- declare const _default$1: typeof __VLS_export$10;
350
+ declare const _default$2: typeof __VLS_export$10;
318
351
  type __VLS_WithSlots$5<T, S> = T & {
319
352
  new (): {
320
353
  $slots: S;
@@ -399,7 +432,7 @@ declare const __VLS_base$4: import("vue").DefineComponent<InfoTipProps, {}, {},
399
432
  content: string;
400
433
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
401
434
  declare const __VLS_export$9: __VLS_WithSlots$4<typeof __VLS_base$4, __VLS_Slots$4>;
402
- declare const _default$2: typeof __VLS_export$9;
435
+ declare const _default$3: typeof __VLS_export$9;
403
436
  type __VLS_WithSlots$4<T, S> = T & {
404
437
  new (): {
405
438
  $slots: S;
@@ -529,7 +562,7 @@ declare const __VLS_export$8: <T>(__VLS_props: NonNullable<Awaited<typeof __VLS_
529
562
  }>) => import("vue").VNode & {
530
563
  __ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
531
564
  };
532
- declare const _default$3: typeof __VLS_export$8;
565
+ declare const _default$4: typeof __VLS_export$8;
533
566
  type __VLS_PrettifyLocal$3<T> = (T extends any ? { [K in keyof T]: T[K]; } : { [K in keyof T as K]: T[K]; }) & {};
534
567
  //#endregion
535
568
  //#region src/LogStream/types.d.ts
@@ -615,7 +648,7 @@ declare const __VLS_base$3: import("vue").DefineComponent<__VLS_PublicProps, {},
615
648
  hasTimestamps: boolean;
616
649
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
617
650
  declare const __VLS_export$7: __VLS_WithSlots$3<typeof __VLS_base$3, __VLS_Slots$3>;
618
- declare const _default$4: typeof __VLS_export$7;
651
+ declare const _default$5: typeof __VLS_export$7;
619
652
  type __VLS_WithSlots$3<T, S> = T & {
620
653
  new (): {
621
654
  $slots: S;
@@ -706,7 +739,7 @@ declare const __VLS_export$6: <T extends SearchableItem>(__VLS_props: NonNullabl
706
739
  }>) => import("vue").VNode & {
707
740
  __ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
708
741
  };
709
- declare const _default$5: typeof __VLS_export$6;
742
+ declare const _default$6: typeof __VLS_export$6;
710
743
  type __VLS_PrettifyLocal$2<T> = (T extends any ? { [K in keyof T]: T[K]; } : { [K in keyof T as K]: T[K]; }) & {};
711
744
  //#endregion
712
745
  //#region src/Stat/types.d.ts
@@ -758,7 +791,7 @@ declare const __VLS_base$2: import("vue").DefineComponent<StatProps, {}, {}, {},
758
791
  size: StatSize;
759
792
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
760
793
  declare const __VLS_export$5: __VLS_WithSlots$2<typeof __VLS_base$2, __VLS_Slots$2>;
761
- declare const _default$7: typeof __VLS_export$5;
794
+ declare const _default$8: typeof __VLS_export$5;
762
795
  type __VLS_WithSlots$2<T, S> = T & {
763
796
  new (): {
764
797
  $slots: S;
@@ -906,7 +939,7 @@ declare const __VLS_export$4: import("vue").DefineComponent<SVGIconProps, {}, {}
906
939
  variation: SVGIconVariation;
907
940
  color: SVGIconColor;
908
941
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
909
- declare const _default$6: typeof __VLS_export$4;
942
+ declare const _default$7: typeof __VLS_export$4;
910
943
  //#endregion
911
944
  //#region src/SVGIcon/types-xif.d.ts
912
945
  /**
@@ -1090,7 +1123,7 @@ declare const __VLS_base$1: import("vue").DefineComponent<TourProps, {}, {}, {},
1090
1123
  isStepCountShown: boolean;
1091
1124
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
1092
1125
  declare const __VLS_export$3: __VLS_WithSlots$1<typeof __VLS_base$1, __VLS_Slots$1>;
1093
- declare const _default$8: typeof __VLS_export$3;
1126
+ declare const _default$9: typeof __VLS_export$3;
1094
1127
  type __VLS_WithSlots$1<T, S> = T & {
1095
1128
  new (): {
1096
1129
  $slots: S;
@@ -1101,7 +1134,7 @@ type __VLS_WithSlots$1<T, S> = T & {
1101
1134
  type __VLS_Slots = TourStepSlots;
1102
1135
  declare const __VLS_base: any;
1103
1136
  declare const __VLS_export$2: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
1104
- declare const _default$9: typeof __VLS_export$2;
1137
+ declare const _default$10: typeof __VLS_export$2;
1105
1138
  type __VLS_WithSlots<T, S> = T & {
1106
1139
  new (): {
1107
1140
  $slots: S;
@@ -1203,7 +1236,7 @@ declare const __VLS_export$1: <T extends string>(__VLS_props: NonNullable<Awaite
1203
1236
  }>) => import("vue").VNode & {
1204
1237
  __ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
1205
1238
  };
1206
- declare const _default$10: typeof __VLS_export$1;
1239
+ declare const _default$11: typeof __VLS_export$1;
1207
1240
  type __VLS_PrettifyLocal$1<T> = (T extends any ? { [K in keyof T]: T[K]; } : { [K in keyof T as K]: T[K]; }) & {};
1208
1241
  //#endregion
1209
1242
  //#region src/TransferList/TransferListSelector.vue.d.ts
@@ -1259,7 +1292,7 @@ declare const __VLS_export: <T extends string>(__VLS_props: NonNullable<Awaited<
1259
1292
  }>) => import("vue").VNode & {
1260
1293
  __ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
1261
1294
  };
1262
- declare const _default$11: typeof __VLS_export;
1295
+ declare const _default$12: typeof __VLS_export;
1263
1296
  type __VLS_PrettifyLocal<T> = (T extends any ? { [K in keyof T]: T[K]; } : { [K in keyof T as K]: T[K]; }) & {};
1264
1297
  //#endregion
1265
- export { _default as CircularProgress, type CircularProgressProps, type CircularProgressSize, type CircularProgressVariant, type CircularProgressVariantMap, type CreateDiffFileOptions, _default$1 as DiffViewer, type DiffViewerChangeIndicator, type DiffViewerEmits, type DiffViewerFile, type DiffViewerFileStatus, type DiffViewerHunk, type DiffViewerInlineHighlight, type DiffViewerLine, type DiffViewerLineKind, type DiffViewerLineRef, type DiffViewerProps, type DiffViewerSide, type DiffViewerSlots, type DiffViewerViewMode, type IconShape, type IconShapeRole, _default$2 as InfoTip, type InfoTipProps, type InfoTipSize, type InlineDiffGranularity, type InlineDiffResult, type InlineRange, _default$3 as ListInput, type ListInputChange, type ListInputColumn, type ListInputColumnWidth, type ListInputEmits, type ListInputProps, type ListInputRenderContext, type ListInputValueContext, type LogEntry, _default$4 as LogStream, type LogStreamEmits, type LogStreamLevel, type LogStreamProps, _default$5 as MobileTokenizer, type MobileTokenizerChange, type MobileTokenizerEmits, type MobileTokenizerProps, type PanelGroup, type PanelRow, type PathPersonality, _default$6 as SVGIcon, type SVGIconColor, type SVGIconDef, type SVGIconProps, type SVGIconSize, type SVGIconVariation, type SearchSource, type SearchableItem, _default$7 as Stat, type StatDelta, type StatDeltaDirection, type StatDeltaSentiment, type StatProps, type StatSize, _default$8 as Tour, type TourDismissSource, type TourEmits, type TourProps, type TourSlots, _default$9 as TourStep, type TourStepProps, type TourStepSlots, _default$10 as TransferList, type TransferListEmits, type TransferListOption, type TransferListProps, _default$11 as TransferListSelector, type TransferListSlots, type UseTourReturn, type XIFAnimate, type XIFIcon, type XIFPath, type XIFPersonality, type XIFSlot };
1298
+ export { _default as CircularProgress, type CircularProgressProps, type CircularProgressSize, type CircularProgressVariant, type CircularProgressVariantMap, _default$1 as CodeEditor, type CodeEditorEmits, type CodeEditorProps, type CreateDiffFileOptions, _default$2 as DiffViewer, type DiffViewerChangeIndicator, type DiffViewerEmits, type DiffViewerFile, type DiffViewerFileStatus, type DiffViewerHunk, type DiffViewerInlineHighlight, type DiffViewerLine, type DiffViewerLineKind, type DiffViewerLineRef, type DiffViewerProps, type DiffViewerSide, type DiffViewerSlots, type DiffViewerViewMode, type IconShape, type IconShapeRole, _default$3 as InfoTip, type InfoTipProps, type InfoTipSize, type InlineDiffGranularity, type InlineDiffResult, type InlineRange, _default$4 as ListInput, type ListInputChange, type ListInputColumn, type ListInputColumnWidth, type ListInputEmits, type ListInputProps, type ListInputRenderContext, type ListInputValueContext, type LogEntry, _default$5 as LogStream, type LogStreamEmits, type LogStreamLevel, type LogStreamProps, _default$6 as MobileTokenizer, type MobileTokenizerChange, type MobileTokenizerEmits, type MobileTokenizerProps, type PanelGroup, type PanelRow, type PathPersonality, _default$7 as SVGIcon, type SVGIconColor, type SVGIconDef, type SVGIconProps, type SVGIconSize, type SVGIconVariation, type SearchSource, type SearchableItem, _default$8 as Stat, type StatDelta, type StatDeltaDirection, type StatDeltaSentiment, type StatProps, type StatSize, _default$9 as Tour, type TourDismissSource, type TourEmits, type TourProps, type TourSlots, _default$10 as TourStep, type TourStepProps, type TourStepSlots, _default$11 as TransferList, type TransferListEmits, type TransferListOption, type TransferListProps, _default$12 as TransferListSelector, type TransferListSlots, type UseTourReturn, type XIFAnimate, type XIFIcon, type XIFPath, type XIFPersonality, type XIFSlot };
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Fragment, Teleport, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createSlots, createTextVNode, createVNode, defineComponent, getCurrentInstance, guardReactiveProps, h, inject, mergeModels, mergeProps, nextTick, normalizeClass, normalizeProps, normalizeStyle, onBeforeUnmount, onMounted, onScopeDispose, openBlock, provide, ref, renderList, renderSlot, resolveDynamicComponent, shallowRef, toDisplayString, toValue, unref, useAttrs, useId, useModel, useSlots, useTemplateRef, watch, withCtx } from "vue";
2
2
  import "../styles/circular-progress.css";
3
- import { AlertDialog, BottomSheet, Button, CheckboxIndicator, ComplexSelector, EmptyState, Field, FieldStatus, HStack, Heading, Icon, IconButton, Item, List, Popover, SyntaxTheme, Text, TextInput, Token, Tooltip, VStack, VisuallyHidden, tokenize, useAnnounce, useScrollableArea, useTranslator } from "@astryx-vue/core";
3
+ import { AlertDialog, BottomSheet, Button, CheckboxIndicator, ComplexSelector, EmptyState, Field, FieldStatus, HStack, Heading, Icon, IconButton, Item, List, Popover, SYNC_TOKENIZE_THRESHOLD, SyntaxTheme, Text, TextInput, Token, Tooltip, VStack, VisuallyHidden, applyHighlightRangesFlat, tokenize, tokenizeAsync, useAnnounce, useScrollableArea, useTranslator } from "@astryx-vue/core";
4
+ import "../styles/code-editor.css";
4
5
  import "../styles/diff-viewer.css";
5
6
  import "../styles/info-tip.css";
6
7
  import "../styles/list-input.css";
@@ -33,9 +34,9 @@ function defaultFormatValueLabel(value, max) {
33
34
  }
34
35
  //#endregion
35
36
  //#region src/CircularProgress/CircularProgress.vue
36
- const _hoisted_1$15 = ["data-size", "data-variant"];
37
- const _hoisted_2$13 = { class: "astryx-circular-progress__ring" };
38
- const _hoisted_3$11 = [
37
+ const _hoisted_1$16 = ["data-size", "data-variant"];
38
+ const _hoisted_2$14 = { class: "astryx-circular-progress__ring" };
39
+ const _hoisted_3$12 = [
39
40
  "aria-valuenow",
40
41
  "aria-valuemin",
41
42
  "aria-valuemax",
@@ -44,14 +45,14 @@ const _hoisted_3$11 = [
44
45
  "height",
45
46
  "viewBox"
46
47
  ];
47
- const _hoisted_4$10 = [
48
+ const _hoisted_4$11 = [
48
49
  "data-variant",
49
50
  "cx",
50
51
  "cy",
51
52
  "r",
52
53
  "stroke-width"
53
54
  ];
54
- const _hoisted_5$9 = [
55
+ const _hoisted_5$10 = [
55
56
  "data-variant",
56
57
  "cx",
57
58
  "cy",
@@ -139,7 +140,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
139
140
  "astryx-circular-progress__visually-hidden": !showLabel.value,
140
141
  "astryx-circular-progress__label--disabled": showLabel.value && __props.isDisabled
141
142
  }])
142
- }, toDisplayString(__props.label), 3), createElementVNode("div", _hoisted_2$13, [(openBlock(), createElementBlock("svg", {
143
+ }, toDisplayString(__props.label), 3), createElementVNode("div", _hoisted_2$14, [(openBlock(), createElementBlock("svg", {
143
144
  class: normalizeClass(["astryx-circular-progress__svg", { "astryx-circular-progress__svg--indeterminate": __props.isIndeterminate }]),
144
145
  role: "progressbar",
145
146
  "aria-labelledby": labelId,
@@ -157,7 +158,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
157
158
  cy: geometry.value.center,
158
159
  r: geometry.value.radius,
159
160
  "stroke-width": geometry.value.strokeWidth
160
- }, null, 8, _hoisted_4$10), __props.isIndeterminate ? (openBlock(), createElementBlock("circle", {
161
+ }, null, 8, _hoisted_4$11), __props.isIndeterminate ? (openBlock(), createElementBlock("circle", {
161
162
  key: 0,
162
163
  class: "astryx-circular-progress__fill astryx-circular-progress__fill--indeterminate",
163
164
  "data-variant": fillVariant.value,
@@ -165,7 +166,7 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
165
166
  cy: geometry.value.center,
166
167
  r: geometry.value.radius,
167
168
  "stroke-width": geometry.value.strokeWidth
168
- }, null, 8, _hoisted_5$9)) : (openBlock(), createElementBlock("circle", {
169
+ }, null, 8, _hoisted_5$10)) : (openBlock(), createElementBlock("circle", {
169
170
  key: 1,
170
171
  class: "astryx-circular-progress__fill",
171
172
  "data-variant": fillVariant.value,
@@ -175,10 +176,278 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
175
176
  "stroke-width": geometry.value.strokeWidth,
176
177
  "stroke-dasharray": geometry.value.circumference,
177
178
  "stroke-dashoffset": dashoffset.value
178
- }, null, 8, _hoisted_6$8))], 10, _hoisted_3$11)), _ctx.$slots.default || hasCenterContent.value ? (openBlock(), createElementBlock("div", _hoisted_7$6, [renderSlot(_ctx.$slots, "default", {}, () => [hasCenterContent.value ? (openBlock(), createElementBlock("span", {
179
+ }, null, 8, _hoisted_6$8))], 10, _hoisted_3$12)), _ctx.$slots.default || hasCenterContent.value ? (openBlock(), createElementBlock("div", _hoisted_7$6, [renderSlot(_ctx.$slots, "default", {}, () => [hasCenterContent.value ? (openBlock(), createElementBlock("span", {
179
180
  key: 0,
180
181
  class: normalizeClass(["astryx-circular-progress__value", { "astryx-circular-progress__value--disabled": __props.isDisabled }])
181
- }, toDisplayString(valueText.value), 3)) : createCommentVNode("v-if", true)])])) : createCommentVNode("v-if", true)])], 16, _hoisted_1$15);
182
+ }, toDisplayString(valueText.value), 3)) : createCommentVNode("v-if", true)])])) : createCommentVNode("v-if", true)])], 16, _hoisted_1$16);
183
+ };
184
+ }
185
+ });
186
+ //#endregion
187
+ //#region src/CodeEditor/keyboard.ts
188
+ /** 把 caret 放到元素文本的字符偏移处。 */
189
+ function setCaretAtTextOffset(element, offset) {
190
+ const selection = element.ownerDocument.defaultView?.getSelection();
191
+ if (!selection) return;
192
+ const range = element.ownerDocument.createRange();
193
+ const walker = element.ownerDocument.createTreeWalker(element, NodeFilter.SHOW_TEXT);
194
+ let node = walker.nextNode();
195
+ let remaining = Math.max(0, offset);
196
+ while (node) {
197
+ const length = node.textContent?.length ?? 0;
198
+ if (remaining <= length) {
199
+ range.setStart(node, remaining);
200
+ range.collapse(true);
201
+ selection.removeAllRanges();
202
+ selection.addRange(range);
203
+ return;
204
+ }
205
+ remaining -= length;
206
+ node = walker.nextNode();
207
+ }
208
+ range.selectNodeContents(element);
209
+ range.collapse(false);
210
+ selection.removeAllRanges();
211
+ selection.addRange(range);
212
+ }
213
+ /** 当前 caret 在元素纯文本里的偏移。 */
214
+ function caretTextOffset(element) {
215
+ const selection = element.ownerDocument.defaultView?.getSelection();
216
+ if (!selection || selection.rangeCount === 0) return null;
217
+ const range = selection.getRangeAt(0);
218
+ const before = range.cloneRange();
219
+ before.selectNodeContents(element);
220
+ before.setEnd(range.startContainer, range.startOffset);
221
+ return before.toString().length;
222
+ }
223
+ /** 用一段文本替换当前选区,并把 caret 放在新文本后面。 */
224
+ function insertAtCaret(element, text) {
225
+ const selection = element.ownerDocument.defaultView?.getSelection();
226
+ if (!selection || selection.rangeCount === 0) return;
227
+ const range = selection.getRangeAt(0);
228
+ range.deleteContents();
229
+ const node = element.ownerDocument.createTextNode(text);
230
+ range.insertNode(node);
231
+ range.setStartAfter(node);
232
+ range.setEndAfter(node);
233
+ selection.removeAllRanges();
234
+ selection.addRange(range);
235
+ }
236
+ /** 去掉当前行开头最多两个空格,返回新文本和 caret 应到的位置。 */
237
+ function outdentCurrentLine(element) {
238
+ const offset = caretTextOffset(element);
239
+ if (offset == null) return null;
240
+ const value = element.textContent ?? "";
241
+ const lineStart = value.lastIndexOf("\n", offset - 1) + 1;
242
+ const line = value.slice(lineStart);
243
+ const removeCount = line.startsWith(" ") ? 2 : line.startsWith(" ") ? 1 : 0;
244
+ if (removeCount === 0) return null;
245
+ return {
246
+ value: `${value.slice(0, lineStart)}${value.slice(lineStart + removeCount)}`,
247
+ caret: offset - Math.min(removeCount, Math.max(0, offset - lineStart))
248
+ };
249
+ }
250
+ //#endregion
251
+ //#region src/CodeEditor/CodeEditor.vue
252
+ const _hoisted_1$15 = ["data-size", "data-language"];
253
+ const _hoisted_2$13 = {
254
+ key: 0,
255
+ class: "astryx-code-editor__gutter",
256
+ "aria-hidden": "true"
257
+ };
258
+ const _hoisted_3$11 = { class: "astryx-code-editor__body" };
259
+ const _hoisted_4$10 = {
260
+ key: 0,
261
+ class: "astryx-code-editor__placeholder",
262
+ "aria-hidden": "true"
263
+ };
264
+ const _hoisted_5$9 = [
265
+ "contenteditable",
266
+ "aria-label",
267
+ "aria-readonly",
268
+ "aria-describedby"
269
+ ];
270
+ const _sfc_main$1 = /*@__PURE__*/ defineComponent({
271
+ name: "AstryxCodeEditor",
272
+ __name: "CodeEditor",
273
+ props: {
274
+ value: {},
275
+ language: { default: "plaintext" },
276
+ label: {},
277
+ hasLineNumbers: { type: Boolean },
278
+ isReadOnly: { type: Boolean },
279
+ placeholder: {},
280
+ maxHeight: {},
281
+ size: { default: "md" },
282
+ tokenizer: {},
283
+ escapeHint: { default: "Press Escape then Tab to move focus out of the editor." }
284
+ },
285
+ emits: ["change"],
286
+ setup(__props, { emit: __emit }) {
287
+ const props = __props;
288
+ const emit = __emit;
289
+ const editor = useTemplateRef("editor");
290
+ const focused = ref(false);
291
+ const composing = ref(false);
292
+ const tabMovesFocus = ref(false);
293
+ const hintId = `astryx-code-editor-${useId()}-escape`;
294
+ const lines = computed(() => props.value.split("\n"));
295
+ const showPlaceholder = computed(() => props.value === "" && Boolean(props.placeholder));
296
+ const containerStyle = computed(() => props.maxHeight == null ? void 0 : { maxHeight: typeof props.maxHeight === "number" ? `${props.maxHeight}px` : props.maxHeight });
297
+ const AUTO_CLOSE_PAIRS = {
298
+ "(": ")",
299
+ "[": "]",
300
+ "{": "}",
301
+ "\"": "\"",
302
+ "'": "'",
303
+ "`": "`"
304
+ };
305
+ const MODE_NEUTRAL_KEYS = /* @__PURE__ */ new Set([
306
+ "Shift",
307
+ "Control",
308
+ "Alt",
309
+ "Meta",
310
+ "CapsLock"
311
+ ]);
312
+ let highlightCleanup;
313
+ let highlightAbort;
314
+ function hasHighlightAPI() {
315
+ return typeof CSS !== "undefined" && "highlights" in CSS && typeof Highlight !== "undefined";
316
+ }
317
+ function paint(tokens, element) {
318
+ highlightCleanup?.();
319
+ highlightCleanup = tokens.length === 0 ? void 0 : applyHighlightRangesFlat(element, tokens);
320
+ }
321
+ function paintCurrent() {
322
+ const element = editor.value;
323
+ if (!element || !hasHighlightAPI()) return;
324
+ highlightAbort?.abort();
325
+ highlightCleanup?.();
326
+ highlightCleanup = void 0;
327
+ const custom = props.tokenizer;
328
+ if (props.value.length >= SYNC_TOKENIZE_THRESHOLD && custom == null) {
329
+ const controller = new AbortController();
330
+ highlightAbort = controller;
331
+ tokenizeAsync(props.value, props.language, controller.signal).then((tokens) => {
332
+ if (!controller.signal.aborted && editor.value === element) paint(tokens, element);
333
+ });
334
+ return;
335
+ }
336
+ paint((custom ?? tokenize)(props.value, props.language), element);
337
+ }
338
+ watch([
339
+ () => props.value,
340
+ () => props.language,
341
+ () => props.tokenizer
342
+ ], () => {
343
+ nextTick(paintCurrent);
344
+ }, {
345
+ immediate: true,
346
+ flush: "post"
347
+ });
348
+ watch(() => props.value, (value) => {
349
+ const element = editor.value;
350
+ if (element && element.textContent !== value) element.textContent = value;
351
+ }, { flush: "post" });
352
+ onBeforeUnmount(() => {
353
+ highlightAbort?.abort();
354
+ highlightCleanup?.();
355
+ });
356
+ function commit() {
357
+ emit("change", editor.value?.textContent ?? "");
358
+ }
359
+ function onInput() {
360
+ if (!composing.value) commit();
361
+ }
362
+ function onKeydown(event) {
363
+ if (props.isReadOnly) return;
364
+ if (event.key === "Escape") {
365
+ if (!event.defaultPrevented) tabMovesFocus.value = true;
366
+ return;
367
+ }
368
+ const element = editor.value;
369
+ if (!element) return;
370
+ if (event.key === "Tab") {
371
+ if (tabMovesFocus.value) {
372
+ tabMovesFocus.value = false;
373
+ return;
374
+ }
375
+ event.preventDefault();
376
+ if (event.shiftKey) {
377
+ const result = outdentCurrentLine(element);
378
+ if (result == null) return;
379
+ element.textContent = result.value;
380
+ setCaretAtTextOffset(element, result.caret);
381
+ } else insertAtCaret(element, " ");
382
+ commit();
383
+ return;
384
+ }
385
+ if (!MODE_NEUTRAL_KEYS.has(event.key)) tabMovesFocus.value = false;
386
+ if (event.key === "Enter") {
387
+ event.preventDefault();
388
+ const offset = caretTextOffset(element);
389
+ if (offset == null) return;
390
+ const before = (element.textContent ?? "").slice(0, offset);
391
+ insertAtCaret(element, `\n${before.slice(before.lastIndexOf("\n") + 1).match(/^\s*/)?.[0] ?? ""}`);
392
+ commit();
393
+ return;
394
+ }
395
+ const close = AUTO_CLOSE_PAIRS[event.key];
396
+ if (close) {
397
+ const selection = element.ownerDocument.defaultView?.getSelection();
398
+ if (!selection || selection.rangeCount === 0 || !selection.getRangeAt(0).collapsed) return;
399
+ event.preventDefault();
400
+ insertAtCaret(element, `${event.key}${close}`);
401
+ const offset = caretTextOffset(element);
402
+ if (offset != null) setCaretAtTextOffset(element, offset - close.length);
403
+ commit();
404
+ }
405
+ }
406
+ return (_ctx, _cache) => {
407
+ return openBlock(), createElementBlock("div", {
408
+ class: normalizeClass(["astryx-code-editor", { "is-focused": focused.value }]),
409
+ "data-size": __props.size,
410
+ "data-language": __props.language
411
+ }, [createElementVNode("div", {
412
+ class: "astryx-code-editor__container",
413
+ style: normalizeStyle(containerStyle.value)
414
+ }, [__props.hasLineNumbers ? (openBlock(), createElementBlock("div", _hoisted_2$13, [(openBlock(true), createElementBlock(Fragment, null, renderList(lines.value, (_, index) => {
415
+ return openBlock(), createElementBlock("div", { key: index }, toDisplayString(index + 1), 1);
416
+ }), 128))])) : createCommentVNode("v-if", true), createElementVNode("div", _hoisted_3$11, [
417
+ showPlaceholder.value ? (openBlock(), createElementBlock("div", _hoisted_4$10, toDisplayString(__props.placeholder), 1)) : createCommentVNode("v-if", true),
418
+ createElementVNode("code", {
419
+ ref_key: "editor",
420
+ ref: editor,
421
+ class: "astryx-code-editor__input",
422
+ contenteditable: __props.isReadOnly ? "false" : "plaintext-only",
423
+ tabindex: "0",
424
+ role: "textbox",
425
+ "aria-multiline": "true",
426
+ "aria-label": __props.label,
427
+ "aria-readonly": __props.isReadOnly || void 0,
428
+ "aria-describedby": __props.isReadOnly ? void 0 : hintId,
429
+ spellcheck: "false",
430
+ onInput,
431
+ onKeydown,
432
+ onFocus: _cache[0] || (_cache[0] = ($event) => focused.value = true),
433
+ onBlur: _cache[1] || (_cache[1] = ($event) => {
434
+ focused.value = false;
435
+ tabMovesFocus.value = false;
436
+ }),
437
+ onCompositionstart: _cache[2] || (_cache[2] = ($event) => composing.value = true),
438
+ onCompositionend: _cache[3] || (_cache[3] = ($event) => {
439
+ composing.value = false;
440
+ onInput();
441
+ })
442
+ }, toDisplayString(__props.value), 41, _hoisted_5$9),
443
+ !__props.isReadOnly ? (openBlock(), createBlock(unref(VisuallyHidden), {
444
+ key: 1,
445
+ id: hintId
446
+ }, {
447
+ default: withCtx(() => [createTextVNode(toDisplayString(__props.escapeHint), 1)]),
448
+ _: 1
449
+ })) : createCommentVNode("v-if", true)
450
+ ])], 4)], 10, _hoisted_1$15);
182
451
  };
183
452
  }
184
453
  });
@@ -531,7 +800,7 @@ const _hoisted_47 = {
531
800
  key: 0,
532
801
  class: "astryx-diff-viewer__annotation-content"
533
802
  };
534
- const _sfc_main$16 = /*@__PURE__*/ defineComponent({
803
+ const _sfc_main$17 = /*@__PURE__*/ defineComponent({
535
804
  name: "AstryxDiffViewerTable",
536
805
  __name: "DiffViewerTable",
537
806
  props: {
@@ -1262,7 +1531,7 @@ const _hoisted_11$1 = {
1262
1531
  class: "astryx-diff-viewer__message"
1263
1532
  };
1264
1533
  const _hoisted_12 = ["aria-label"];
1265
- const _sfc_main$1 = /*@__PURE__*/ defineComponent({
1534
+ const _sfc_main$2 = /*@__PURE__*/ defineComponent({
1266
1535
  name: "AstryxDiffViewer",
1267
1536
  inheritAttrs: false,
1268
1537
  __name: "DiffViewer",
@@ -1451,7 +1720,7 @@ const _sfc_main$1 = /*@__PURE__*/ defineComponent({
1451
1720
  }), [createElementVNode("div", {
1452
1721
  ref: unref(scroll).content,
1453
1722
  class: "astryx-diff-viewer__content"
1454
- }, [createVNode(_sfc_main$16, {
1723
+ }, [createVNode(_sfc_main$17, {
1455
1724
  "view-mode": __props.viewMode,
1456
1725
  "unified-rows": unifiedRows.value,
1457
1726
  "split-rows": splitRows.value,
@@ -1773,7 +2042,7 @@ function parseUnifiedDiff(patch) {
1773
2042
  //#endregion
1774
2043
  //#region src/InfoTip/InfoTip.vue
1775
2044
  const _hoisted_1$12 = ["aria-label"];
1776
- const _sfc_main$2 = /*@__PURE__*/ defineComponent({
2045
+ const _sfc_main$3 = /*@__PURE__*/ defineComponent({
1777
2046
  name: "AstryxInfoTip",
1778
2047
  __name: "InfoTip",
1779
2048
  props: {
@@ -1900,7 +2169,7 @@ const _hoisted_7$3 = {
1900
2169
  key: 0,
1901
2170
  class: "astryx-list-input__control-cell astryx-list-input__control-cell--reorder"
1902
2171
  };
1903
- const _sfc_main$15 = /*@__PURE__*/ defineComponent({
2172
+ const _sfc_main$16 = /*@__PURE__*/ defineComponent({
1904
2173
  name: "AstryxListInputRow",
1905
2174
  __name: "ListInputRow",
1906
2175
  props: {
@@ -2346,7 +2615,7 @@ const _hoisted_5$5 = {
2346
2615
  class: "astryx-list-input__action-content"
2347
2616
  };
2348
2617
  const focusableFieldSelector = "[data-list-input-cell] input, [data-list-input-cell] select, [data-list-input-cell] textarea, [data-list-input-cell] button, [data-list-input-cell] [tabindex]:not([tabindex=\"-1\"])";
2349
- const _sfc_main$3 = /*@__PURE__*/ defineComponent({
2618
+ const _sfc_main$4 = /*@__PURE__*/ defineComponent({
2350
2619
  name: "AstryxListInput",
2351
2620
  inheritAttrs: false,
2352
2621
  __name: "ListInput",
@@ -2624,7 +2893,7 @@ const _sfc_main$3 = /*@__PURE__*/ defineComponent({
2624
2893
  description: unref(t)("@astryx.listInput.emptyDescription", { itemName: __props.itemName }),
2625
2894
  "is-compact": ""
2626
2895
  }, null, 8, ["title", "description"])])) : createCommentVNode("v-if", true), (openBlock(true), createElementBlock(Fragment, null, renderList(unref(reorder).displayValue.value, (item, index) => {
2627
- return openBlock(), createBlock(_sfc_main$15, {
2896
+ return openBlock(), createBlock(_sfc_main$16, {
2628
2897
  key: itemKeyOf(item),
2629
2898
  item,
2630
2899
  index,
@@ -2739,7 +3008,7 @@ const _hoisted_11 = {
2739
3008
  key: 0,
2740
3009
  class: "astryx-log-stream__detail"
2741
3010
  };
2742
- const _sfc_main$14 = /*@__PURE__*/ defineComponent({
3011
+ const _sfc_main$15 = /*@__PURE__*/ defineComponent({
2743
3012
  name: "AstryxLogStreamRow",
2744
3013
  __name: "LogStreamRow",
2745
3014
  props: {
@@ -2843,7 +3112,7 @@ function useLogFollow(options) {
2843
3112
  //#region src/LogStream/LogStream.vue
2844
3113
  const _hoisted_1$8 = ["data-variant"];
2845
3114
  const _hoisted_2$7 = ["aria-label", "aria-live"];
2846
- const _sfc_main$4 = /*@__PURE__*/ defineComponent({
3115
+ const _sfc_main$5 = /*@__PURE__*/ defineComponent({
2847
3116
  name: "AstryxLogStream",
2848
3117
  inheritAttrs: false,
2849
3118
  __name: "LogStream",
@@ -2909,7 +3178,7 @@ const _sfc_main$4 = /*@__PURE__*/ defineComponent({
2909
3178
  style: normalizeStyle(scrollerStyle.value),
2910
3179
  onScrollPassive: _cache[0] || (_cache[0] = (...args) => unref(handleScroll) && unref(handleScroll)(...args))
2911
3180
  }, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.entries, (entry) => {
2912
- return renderSlot(_ctx.$slots, "entry", { entry }, () => [createVNode(_sfc_main$14, {
3181
+ return renderSlot(_ctx.$slots, "entry", { entry }, () => [createVNode(_sfc_main$15, {
2913
3182
  entry,
2914
3183
  "has-timestamps": __props.hasTimestamps,
2915
3184
  "has-source": hasSource.value,
@@ -3075,7 +3344,7 @@ const _hoisted_10$1 = {
3075
3344
  class: "astryx-mobile-tokenizer__footer"
3076
3345
  };
3077
3346
  const CREATABLE_ID_PREFIX = "__xds_create__";
3078
- const _sfc_main$5 = /*@__PURE__*/ defineComponent({
3347
+ const _sfc_main$6 = /*@__PURE__*/ defineComponent({
3079
3348
  name: "AstryxMobileTokenizer",
3080
3349
  inheritAttrs: false,
3081
3350
  __name: "MobileTokenizer",
@@ -3398,7 +3667,7 @@ const _hoisted_10 = {
3398
3667
  key: 1,
3399
3668
  class: "astryx-stat__media"
3400
3669
  };
3401
- const _sfc_main$7 = /*@__PURE__*/ defineComponent({
3670
+ const _sfc_main$8 = /*@__PURE__*/ defineComponent({
3402
3671
  name: "AstryxStat",
3403
3672
  inheritAttrs: false,
3404
3673
  __name: "Stat",
@@ -4255,7 +4524,7 @@ const _hoisted_6$1 = {
4255
4524
  key: 1,
4256
4525
  class: "astryx-svg-icon__layer astryx-svg-icon__layer--secondary-stroke"
4257
4526
  };
4258
- const _sfc_main$6 = /*@__PURE__*/ defineComponent({
4527
+ const _sfc_main$7 = /*@__PURE__*/ defineComponent({
4259
4528
  name: "AstryxSvgIcon",
4260
4529
  inheritAttrs: false,
4261
4530
  __name: "SVGIcon",
@@ -4491,7 +4760,7 @@ const xifExamples = [
4491
4760
  const TOUR_CONTEXT_KEY = Symbol("AstryxTour");
4492
4761
  //#endregion
4493
4762
  //#region src/Tour/Tour.vue
4494
- const _sfc_main$8 = /*@__PURE__*/ defineComponent({
4763
+ const _sfc_main$9 = /*@__PURE__*/ defineComponent({
4495
4764
  name: "AstryxTour",
4496
4765
  __name: "Tour",
4497
4766
  props: {
@@ -4555,7 +4824,7 @@ const _sfc_main$8 = /*@__PURE__*/ defineComponent({
4555
4824
  //#endregion
4556
4825
  //#region src/Tour/TourHighlight.vue
4557
4826
  const _hoisted_1$4 = ["data-testid"];
4558
- const _sfc_main$13 = /*@__PURE__*/ defineComponent({
4827
+ const _sfc_main$14 = /*@__PURE__*/ defineComponent({
4559
4828
  __name: "TourHighlight",
4560
4829
  props: {
4561
4830
  rect: {},
@@ -4599,7 +4868,7 @@ const _sfc_main$13 = /*@__PURE__*/ defineComponent({
4599
4868
  //#region src/Tour/TourStep.vue
4600
4869
  const _hoisted_1$3 = ["data-testid"];
4601
4870
  const _hoisted_2$3 = { key: 1 };
4602
- const _sfc_main$9 = /*@__PURE__*/ defineComponent({
4871
+ const _sfc_main$10 = /*@__PURE__*/ defineComponent({
4603
4872
  name: "AstryxTourStep",
4604
4873
  __name: "TourStep",
4605
4874
  props: {
@@ -4666,7 +4935,7 @@ const _sfc_main$9 = /*@__PURE__*/ defineComponent({
4666
4935
  if (!open) tour?.onDismiss("close");
4667
4936
  }
4668
4937
  return (_ctx, _cache) => {
4669
- return unref(tour) && isActiveStep.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createVNode(_sfc_main$13, {
4938
+ return unref(tour) && isActiveStep.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createVNode(_sfc_main$14, {
4670
4939
  rect: rect.value,
4671
4940
  "has-backdrop": unref(tour).hasBackdrop.value,
4672
4941
  onBackdropClick: _cache[0] || (_cache[0] = ($event) => unref(tour).onDismiss("backdrop"))
@@ -4833,7 +5102,7 @@ const _hoisted_5 = {
4833
5102
  class: "astryx-transfer-list-panel__group-heading"
4834
5103
  };
4835
5104
  const _hoisted_6 = ["data-transfer-list-empty"];
4836
- const _sfc_main$12 = /*@__PURE__*/ defineComponent({
5105
+ const _sfc_main$13 = /*@__PURE__*/ defineComponent({
4837
5106
  name: "AstryxTransferListPanel",
4838
5107
  __name: "TransferListPanel",
4839
5108
  props: {
@@ -5305,7 +5574,7 @@ const _hoisted_3$1 = {
5305
5574
  class: "astryx-transfer-list__controls"
5306
5575
  };
5307
5576
  const _hoisted_4 = { class: "astryx-transfer-list-collection" };
5308
- const _sfc_main$10 = /*@__PURE__*/ defineComponent({
5577
+ const _sfc_main$11 = /*@__PURE__*/ defineComponent({
5309
5578
  name: "AstryxTransferList",
5310
5579
  inheritAttrs: false,
5311
5580
  __name: "TransferList",
@@ -5579,7 +5848,7 @@ const _sfc_main$10 = /*@__PURE__*/ defineComponent({
5579
5848
  default: withCtx(() => [createTextVNode(toDisplayString(unref(t)("@astryx.transferList.reorderInstructions")), 1)]),
5580
5849
  _: 1
5581
5850
  }),
5582
- createElementVNode("div", _hoisted_4, [createVNode(_sfc_main$12, {
5851
+ createElementVNode("div", _hoisted_4, [createVNode(_sfc_main$13, {
5583
5852
  side: "selected",
5584
5853
  "heading-id": selectedHeadingId,
5585
5854
  label: selectedLabel.value,
@@ -5615,7 +5884,7 @@ const _sfc_main$10 = /*@__PURE__*/ defineComponent({
5615
5884
  "dragging-value",
5616
5885
  "grabbed-value",
5617
5886
  "drop-target"
5618
- ]), createVNode(_sfc_main$12, {
5887
+ ]), createVNode(_sfc_main$13, {
5619
5888
  side: "available",
5620
5889
  "heading-id": availableHeadingId,
5621
5890
  label: availableLabel.value,
@@ -5670,7 +5939,7 @@ const _hoisted_3 = {
5670
5939
  class: "astryx-transfer-list-selector__footer"
5671
5940
  };
5672
5941
  const DEFAULT_SELECTOR_WIDTH = "min(41rem, calc(100vw - 32px))";
5673
- const _sfc_main$11 = /*@__PURE__*/ defineComponent({
5942
+ const _sfc_main$12 = /*@__PURE__*/ defineComponent({
5674
5943
  name: "AstryxTransferListSelector",
5675
5944
  __name: "TransferListSelector",
5676
5945
  props: {
@@ -5807,7 +6076,7 @@ const _sfc_main$11 = /*@__PURE__*/ defineComponent({
5807
6076
  }, [createElementVNode("fieldset", {
5808
6077
  class: "astryx-transfer-list-selector__fieldset",
5809
6078
  disabled: __props.isDisabled
5810
- }, [createVNode(_sfc_main$10, {
6079
+ }, [createVNode(_sfc_main$11, {
5811
6080
  label: __props.label,
5812
6081
  "is-label-hidden": "",
5813
6082
  options: __props.options,
@@ -5886,4 +6155,4 @@ const _sfc_main$11 = /*@__PURE__*/ defineComponent({
5886
6155
  }
5887
6156
  });
5888
6157
  //#endregion
5889
- export { _sfc_main as CircularProgress, _sfc_main$1 as DiffViewer, _sfc_main$2 as InfoTip, _sfc_main$3 as ListInput, _sfc_main$4 as LogStream, _sfc_main$5 as MobileTokenizer, _sfc_main$6 as SVGIcon, _sfc_main$7 as Stat, _sfc_main$8 as Tour, _sfc_main$9 as TourStep, _sfc_main$10 as TransferList, _sfc_main$11 as TransferListSelector, addCurvature, adjustTension, applyPersonality, bellIcon, calendarIcon, checkIcon, createDiffFile, eyeIcon, folderIcon, heartIcon, homeIcon, lockIcon, mailIcon, menuIcon, parseUnifiedDiff, roundCorners, searchIcon, settingsIcon, shieldIcon, starIcon, starterIcons, useTour, xIcon, xifBell, xifBellOverride, xifCheck, xifExamples, xifFile, xifHome, xifShield, xifStar };
6158
+ export { _sfc_main as CircularProgress, _sfc_main$1 as CodeEditor, _sfc_main$2 as DiffViewer, _sfc_main$3 as InfoTip, _sfc_main$4 as ListInput, _sfc_main$5 as LogStream, _sfc_main$6 as MobileTokenizer, _sfc_main$7 as SVGIcon, _sfc_main$8 as Stat, _sfc_main$9 as Tour, _sfc_main$10 as TourStep, _sfc_main$11 as TransferList, _sfc_main$12 as TransferListSelector, addCurvature, adjustTension, applyPersonality, bellIcon, calendarIcon, checkIcon, createDiffFile, eyeIcon, folderIcon, heartIcon, homeIcon, lockIcon, mailIcon, menuIcon, parseUnifiedDiff, roundCorners, searchIcon, settingsIcon, shieldIcon, starIcon, starterIcons, useTour, xIcon, xifBell, xifBellOverride, xifCheck, xifExamples, xifFile, xifHome, xifShield, xifStar };
package/lab.css CHANGED
@@ -10,3 +10,4 @@
10
10
  @import './styles/list-input.css';
11
11
  @import './styles/diff-viewer.css';
12
12
  @import './styles/tour.css';
13
+ @import './styles/code-editor.css';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@astryx-vue/lab",
3
3
  "type": "module",
4
- "version": "0.4.0",
4
+ "version": "0.5.0",
5
5
  "description": "Astryx lab components for Vue 3 — experimental components that may change. Ships only under the canary dist-tag.",
6
6
  "author": "ShenQingchuan",
7
7
  "license": "MIT",
@@ -53,7 +53,7 @@
53
53
  ],
54
54
  "peerDependencies": {
55
55
  "vue": "^3.5.0",
56
- "@astryx-vue/core": "^0.7.12"
56
+ "@astryx-vue/core": "^0.7.13"
57
57
  },
58
58
  "devDependencies": {
59
59
  "@types/node": "26.6.4",
@@ -64,7 +64,7 @@
64
64
  "unplugin-vue": "8.0.0",
65
65
  "vue": "3.5.43",
66
66
  "vue-tsc": "3.3.12",
67
- "@astryx-vue/core": "^0.7.12"
67
+ "@astryx-vue/core": "^0.7.13"
68
68
  },
69
69
  "scripts": {
70
70
  "build": "tsdown",
@@ -0,0 +1,95 @@
1
+ @layer astryx.reset, astryx.tokens, astryx.base, astryx.components, astryx.overrides;
2
+
3
+ @layer astryx.components {
4
+ .astryx-code-editor {
5
+ position: relative;
6
+ display: flex;
7
+ border: var(--astryx-border-width, 1px) solid var(--astryx-color-border, #ebebeb);
8
+ border-radius: var(--astryx-radius-element, 0.625rem);
9
+ background: var(--astryx-color-background-muted, #f5f5f5);
10
+ overflow: hidden;
11
+ }
12
+
13
+ .astryx-code-editor.is-focused {
14
+ border-color: var(--astryx-color-accent, #0064e0);
15
+ box-shadow: 0 0 0 1px var(--astryx-color-accent, #0064e0);
16
+ }
17
+
18
+ .astryx-code-editor__container {
19
+ display: flex;
20
+ flex: 1;
21
+ min-width: 0;
22
+ overflow: auto;
23
+ }
24
+
25
+ .astryx-code-editor__gutter {
26
+ flex-shrink: 0;
27
+ padding-block: var(--astryx-spacing-3, 12px);
28
+ padding-inline-start: var(--astryx-spacing-4, 16px);
29
+ padding-inline-end: var(--astryx-spacing-3, 12px);
30
+ border-inline-end: var(--astryx-border-width, 1px) solid var(--astryx-color-border, #ebebeb);
31
+ color: var(--astryx-color-text-disabled, #a3a3a3);
32
+ font-family: var(--astryx-font-family-code, monospace);
33
+ line-height: var(--astryx-text-code-leading, 1.5);
34
+ text-align: end;
35
+ user-select: none;
36
+ }
37
+
38
+ .astryx-code-editor__body {
39
+ position: relative;
40
+ flex: 1;
41
+ min-width: 0;
42
+ }
43
+
44
+ .astryx-code-editor__input {
45
+ display: block;
46
+ min-height: 3em;
47
+ margin: 0;
48
+ padding-block: var(--astryx-spacing-3, 12px);
49
+ padding-inline: var(--astryx-spacing-4, 16px);
50
+ color: var(--astryx-color-text-primary, #171717);
51
+ caret-color: var(--astryx-color-text-primary, #171717);
52
+ font-family: var(--astryx-font-family-code, monospace);
53
+ line-height: var(--astryx-text-code-leading, 1.5);
54
+ tab-size: 2;
55
+ white-space: pre;
56
+ word-break: normal;
57
+ overflow-wrap: normal;
58
+ outline: none;
59
+ }
60
+
61
+ .astryx-code-editor__input,
62
+ .astryx-code-editor__gutter {
63
+ font-size: var(--astryx-text-code-size, 14px);
64
+ }
65
+
66
+ .astryx-code-editor[data-size='sm'] .astryx-code-editor__input,
67
+ .astryx-code-editor[data-size='sm'] .astryx-code-editor__gutter {
68
+ font-size: var(--astryx-font-size-sm, 12px);
69
+ }
70
+
71
+ .astryx-code-editor__placeholder {
72
+ position: absolute;
73
+ top: var(--astryx-spacing-3, 12px);
74
+ inset-inline-start: var(--astryx-spacing-4, 16px);
75
+ color: var(--astryx-color-text-disabled, #a3a3a3);
76
+ font-family: var(--astryx-font-family-code, monospace);
77
+ line-height: var(--astryx-text-code-leading, 1.5);
78
+ pointer-events: none;
79
+ user-select: none;
80
+ }
81
+ }
82
+
83
+ .astryx-code-editor__input::highlight(astryx-keyword) { color: var(--astryx-code-syntax-keyword, #52237b); }
84
+ .astryx-code-editor__input::highlight(astryx-string) { color: var(--astryx-code-syntax-string, #004800); }
85
+ .astryx-code-editor__input::highlight(astryx-comment) { color: var(--astryx-code-syntax-comment, #605f52); }
86
+ .astryx-code-editor__input::highlight(astryx-number) { color: var(--astryx-code-syntax-number, #622e00); }
87
+ .astryx-code-editor__input::highlight(astryx-function) { color: var(--astryx-code-syntax-function, #203a6c); }
88
+ .astryx-code-editor__input::highlight(astryx-type) { color: var(--astryx-code-syntax-type, #52237b); }
89
+ .astryx-code-editor__input::highlight(astryx-tag) { color: var(--astryx-code-syntax-tag, #6d211c); }
90
+ .astryx-code-editor__input::highlight(astryx-attribute) { color: var(--astryx-code-syntax-attribute, #413e00); }
91
+ .astryx-code-editor__input::highlight(astryx-property) { color: var(--astryx-code-syntax-property, #203a6c); }
92
+ .astryx-code-editor__input::highlight(astryx-operator) { color: var(--astryx-code-syntax-operator, #171717); }
93
+ .astryx-code-editor__input::highlight(astryx-constant) { color: var(--astryx-code-syntax-constant, #622e00); }
94
+ .astryx-code-editor__input::highlight(astryx-punctuation) { color: var(--astryx-code-syntax-punctuation, #525252); }
95
+ .astryx-code-editor__input::highlight(astryx-variable) { color: var(--astryx-code-syntax-variable, #203a6c); }