@shwfed/nuxt 0.10.6 → 0.10.8
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/module.json +1 -1
- package/dist/runtime/components/ui/button-configurator/ButtonConfiguratorDialog.vue +282 -5
- package/dist/runtime/components/ui/markdown-configurator/MarkdownConfiguratorDialog.vue +271 -7
- package/dist/runtime/components/ui/table/Table.vue +1 -1
- package/package.json +1 -1
package/dist/module.json
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
+
import { useNuxtApp } from "#app";
|
|
2
3
|
import { useSortable } from "@vueuse/integrations/useSortable";
|
|
3
4
|
import { Icon } from "@iconify/vue";
|
|
5
|
+
import z from "zod";
|
|
4
6
|
import { computed, nextTick, ref, watch } from "vue";
|
|
5
7
|
import { useI18n } from "vue-i18n";
|
|
6
8
|
import { cn } from "../../../utils/cn";
|
|
@@ -38,6 +40,7 @@ const emit = defineEmits(["confirm"]);
|
|
|
38
40
|
const open = defineModel("open", { type: Boolean, ...{
|
|
39
41
|
default: false
|
|
40
42
|
} });
|
|
43
|
+
const { $toast } = useNuxtApp();
|
|
41
44
|
const { t } = useI18n();
|
|
42
45
|
const search = ref("");
|
|
43
46
|
const draftGap = ref(props.config.gap ?? 12);
|
|
@@ -450,7 +453,21 @@ async function copySelectedId() {
|
|
|
450
453
|
return;
|
|
451
454
|
}
|
|
452
455
|
}
|
|
453
|
-
function
|
|
456
|
+
function showImportError(message) {
|
|
457
|
+
$toast.error(message);
|
|
458
|
+
}
|
|
459
|
+
function showCopyError(message) {
|
|
460
|
+
$toast.error(message);
|
|
461
|
+
}
|
|
462
|
+
function showImportErrorWithCopyAction(message, onClick) {
|
|
463
|
+
$toast.error(message, {
|
|
464
|
+
action: {
|
|
465
|
+
label: t("copy-paste-error"),
|
|
466
|
+
onClick
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
function buildDraftConfig() {
|
|
454
471
|
const generalStyleResult = ButtonsStyleC.safeParse(draftStyle.value);
|
|
455
472
|
if (!generalStyleResult.success) {
|
|
456
473
|
validationErrors.value = {
|
|
@@ -468,7 +485,196 @@ function confirmChanges() {
|
|
|
468
485
|
if (!result.success) {
|
|
469
486
|
return;
|
|
470
487
|
}
|
|
471
|
-
|
|
488
|
+
return result.data;
|
|
489
|
+
}
|
|
490
|
+
function getValidDraftConfig(errorMessage) {
|
|
491
|
+
const config = buildDraftConfig();
|
|
492
|
+
if (!config) {
|
|
493
|
+
showCopyError(errorMessage);
|
|
494
|
+
return void 0;
|
|
495
|
+
}
|
|
496
|
+
return config;
|
|
497
|
+
}
|
|
498
|
+
function buildButtonConfigJsonSchema() {
|
|
499
|
+
return z.toJSONSchema(ButtonConfigC, {
|
|
500
|
+
io: "input",
|
|
501
|
+
unrepresentable: "any"
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
function buildAiPromptHeaderMarkdown() {
|
|
505
|
+
return [
|
|
506
|
+
"# \u6309\u94AE\u914D\u7F6E AI \u4E0A\u4E0B\u6587",
|
|
507
|
+
"\u4F60\u662F\u4E00\u4E2A\u5E2E\u52A9\u7528\u6237\u7F16\u8F91\u6309\u94AE\u7EC4\u4EF6\u914D\u7F6E\u7684 AI \u52A9\u624B\u3002",
|
|
508
|
+
"\u5982\u679C\u4F60\u4E0D\u786E\u5B9A\u6309\u94AE\u7ED3\u6784\u3001\u6587\u6848\u542B\u4E49\u3001\u6837\u5F0F\u8868\u8FBE\u5F0F\u6216\u4E1A\u52A1\u4E0A\u4E0B\u6587\uFF0C\u5FC5\u987B\u660E\u786E\u8BF4\u660E\u4E0D\u786E\u5B9A\u70B9\uFF0C\u4E0D\u8981\u731C\u6D4B\u3002",
|
|
509
|
+
"\u53EA\u6709\u5F53\u7528\u6237\u660E\u786E\u8981\u6C42\u8F93\u51FA\u914D\u7F6E\u65F6\uFF0C\u624D\u8FD4\u56DE\u5B8C\u6574\u914D\u7F6E\uFF1B\u5982\u679C\u8FD4\u56DE\u914D\u7F6E\uFF0C\u5FC5\u987B\u653E\u5728 Markdown code block \u4E2D\u3002"
|
|
510
|
+
].join("\n");
|
|
511
|
+
}
|
|
512
|
+
function buildMarkdownNotes() {
|
|
513
|
+
return [
|
|
514
|
+
"## \u6CE8\u610F\u4E8B\u9879",
|
|
515
|
+
"- \u6240\u6709 `group.id`\u3001\u6309\u94AE `id`\u3001\u4E0B\u62C9\u6309\u94AE `id` \u90FD\u5FC5\u987B\u4FDD\u7559\u73B0\u6709 UUID\uFF0C\u4E0D\u8981\u751F\u6210\u65B0\u7684 ID \u66FF\u6362\u5DF2\u6709\u9879\u3002",
|
|
516
|
+
"- `groups` \u662F\u6839\u7EA7\u6309\u94AE\u7EC4\u5217\u8868\uFF1B\u4E0B\u62C9\u6309\u94AE\u53EA\u80FD\u5B58\u5728\u4E8E\u6309\u94AE\u7EC4\u5185\u3002",
|
|
517
|
+
"- \u4E0B\u62C9\u6309\u94AE\u7684 `items` \u53EA\u80FD\u662F\u6309\u94AE\u5217\u8868\uFF0C\u4E0D\u80FD\u518D\u5D4C\u5957\u4E0B\u62C9\u6309\u94AE\u6216\u6309\u94AE\u7EC4\u3002",
|
|
518
|
+
"- `style` \u5FC5\u987B\u662F\u8FD4\u56DE\u6837\u5F0F\u5BF9\u8C61\u7684 CEL \u8868\u8FBE\u5F0F\u5B57\u7B26\u4E32\uFF1B\u5982\u679C\u4E0D\u80FD\u786E\u5B9A\u4E0A\u4E0B\u6587\u53D8\u91CF\uFF0C\u8BF7\u5148\u8BF4\u660E\u9650\u5236\u3002"
|
|
519
|
+
].join("\n");
|
|
520
|
+
}
|
|
521
|
+
function buildMarkdownCopyContent(config) {
|
|
522
|
+
return [
|
|
523
|
+
buildAiPromptHeaderMarkdown(),
|
|
524
|
+
"",
|
|
525
|
+
"## \u5F53\u524D\u914D\u7F6E",
|
|
526
|
+
"```json",
|
|
527
|
+
JSON.stringify(config, null, 2),
|
|
528
|
+
"```",
|
|
529
|
+
"",
|
|
530
|
+
buildMarkdownNotes(),
|
|
531
|
+
"",
|
|
532
|
+
"## ButtonConfig JSON Schema",
|
|
533
|
+
"```json",
|
|
534
|
+
JSON.stringify(buildButtonConfigJsonSchema(), null, 2),
|
|
535
|
+
"```"
|
|
536
|
+
].join("\n");
|
|
537
|
+
}
|
|
538
|
+
function formatIssuePath(path) {
|
|
539
|
+
if (path.length === 0) {
|
|
540
|
+
return "(root)";
|
|
541
|
+
}
|
|
542
|
+
return path.map((segment) => {
|
|
543
|
+
if (typeof segment === "number") {
|
|
544
|
+
return `${segment}`;
|
|
545
|
+
}
|
|
546
|
+
if (typeof segment === "string") {
|
|
547
|
+
return segment;
|
|
548
|
+
}
|
|
549
|
+
return String(segment);
|
|
550
|
+
}).join(".");
|
|
551
|
+
}
|
|
552
|
+
function formatIssueExtraFields(issue) {
|
|
553
|
+
const lines = [];
|
|
554
|
+
for (const [key, value] of Object.entries(issue)) {
|
|
555
|
+
if (key === "code" || key === "message" || key === "path") {
|
|
556
|
+
continue;
|
|
557
|
+
}
|
|
558
|
+
lines.push(` - ${key}: ${JSON.stringify(value)}`);
|
|
559
|
+
}
|
|
560
|
+
return lines;
|
|
561
|
+
}
|
|
562
|
+
function buildPasteConfigErrorDetails(source, error) {
|
|
563
|
+
if (error instanceof SyntaxError) {
|
|
564
|
+
return [
|
|
565
|
+
"## \u7C98\u8D34\u5931\u8D25\u539F\u56E0",
|
|
566
|
+
"- \u7C7B\u578B\uFF1AJSON \u89E3\u6790\u5931\u8D25",
|
|
567
|
+
`- message: ${error.message}`,
|
|
568
|
+
"",
|
|
569
|
+
"## \u539F\u59CB\u7C98\u8D34\u5185\u5BB9",
|
|
570
|
+
"```text",
|
|
571
|
+
source,
|
|
572
|
+
"```"
|
|
573
|
+
].join("\n");
|
|
574
|
+
}
|
|
575
|
+
const issueLines = error.issues.flatMap((issue, index) => {
|
|
576
|
+
return [
|
|
577
|
+
`### Issue ${index + 1}`,
|
|
578
|
+
`- path: ${formatIssuePath(issue.path)}`,
|
|
579
|
+
`- code: ${issue.code}`,
|
|
580
|
+
`- message: ${issue.message}`,
|
|
581
|
+
...formatIssueExtraFields(issue)
|
|
582
|
+
];
|
|
583
|
+
});
|
|
584
|
+
return [
|
|
585
|
+
"## \u7C98\u8D34\u5931\u8D25\u539F\u56E0",
|
|
586
|
+
"- \u7C7B\u578B\uFF1ASchema \u6821\u9A8C\u5931\u8D25",
|
|
587
|
+
"",
|
|
588
|
+
"## \u539F\u59CB\u7C98\u8D34\u5185\u5BB9",
|
|
589
|
+
"```json",
|
|
590
|
+
source,
|
|
591
|
+
"```",
|
|
592
|
+
"",
|
|
593
|
+
"## Schema \u62A5\u9519",
|
|
594
|
+
...issueLines
|
|
595
|
+
].join("\n");
|
|
596
|
+
}
|
|
597
|
+
function buildPasteConfigErrorMarkdown(source, error) {
|
|
598
|
+
return [
|
|
599
|
+
buildAiPromptHeaderMarkdown(),
|
|
600
|
+
"",
|
|
601
|
+
"## \u5F53\u524D\u4EFB\u52A1",
|
|
602
|
+
"\u7528\u6237\u628A\u4E00\u6BB5\u914D\u7F6E\u7C98\u8D34\u56DE\u6309\u94AE\u914D\u7F6E\u5668\u65F6\u5931\u8D25\u4E86\u3002\u8BF7\u57FA\u4E8E\u4E0B\u9762\u7684\u539F\u59CB\u5185\u5BB9\u548C\u62A5\u9519\u4FEE\u590D\u5F53\u524D\u914D\u7F6E\u3002",
|
|
603
|
+
"\u8BF7\u4F18\u5148\u4FEE\u590D\u6700\u5C0F\u5FC5\u8981\u8303\u56F4\uFF0C\u4E0D\u8981\u91CD\u5EFA\u73B0\u6709\u9879\uFF0C\u4E5F\u4E0D\u8981\u66FF\u73B0\u6709\u9879\u751F\u6210\u65B0\u7684 UUID\u3002",
|
|
604
|
+
"\u53EA\u6709\u5F53\u7528\u6237\u660E\u786E\u8981\u6C42\u8F93\u51FA\u914D\u7F6E\u65F6\uFF0C\u624D\u8FD4\u56DE\u5B8C\u6574\u914D\u7F6E\uFF1B\u5982\u679C\u8FD4\u56DE\u914D\u7F6E\uFF0C\u5FC5\u987B\u653E\u5728 Markdown code block \u4E2D\u3002",
|
|
605
|
+
"",
|
|
606
|
+
buildPasteConfigErrorDetails(source, error),
|
|
607
|
+
"",
|
|
608
|
+
buildMarkdownNotes()
|
|
609
|
+
].join("\n");
|
|
610
|
+
}
|
|
611
|
+
async function writeClipboardText(value, errorMessage) {
|
|
612
|
+
try {
|
|
613
|
+
await navigator.clipboard.writeText(value);
|
|
614
|
+
} catch {
|
|
615
|
+
showCopyError(errorMessage);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
async function copyPasteConfigError(source, error) {
|
|
619
|
+
await writeClipboardText(
|
|
620
|
+
buildPasteConfigErrorMarkdown(source, error),
|
|
621
|
+
t("copy-paste-error-failed")
|
|
622
|
+
);
|
|
623
|
+
}
|
|
624
|
+
async function pasteConfigFromClipboard() {
|
|
625
|
+
let source = "";
|
|
626
|
+
try {
|
|
627
|
+
source = await navigator.clipboard.readText();
|
|
628
|
+
} catch {
|
|
629
|
+
showImportError(t("paste-config-read-failed"));
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
let parsedValue;
|
|
633
|
+
try {
|
|
634
|
+
parsedValue = JSON.parse(source);
|
|
635
|
+
} catch (error) {
|
|
636
|
+
if (error instanceof SyntaxError) {
|
|
637
|
+
showImportErrorWithCopyAction(
|
|
638
|
+
t("paste-config-invalid-json"),
|
|
639
|
+
async () => copyPasteConfigError(source, error)
|
|
640
|
+
);
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
showImportError(t("paste-config-invalid-json"));
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
const result = ButtonConfigC.safeParse(parsedValue);
|
|
647
|
+
if (!result.success) {
|
|
648
|
+
showImportErrorWithCopyAction(
|
|
649
|
+
t("paste-config-invalid-schema"),
|
|
650
|
+
async () => copyPasteConfigError(source, result.error)
|
|
651
|
+
);
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
applyDraftConfig(result.data);
|
|
655
|
+
await nextTick();
|
|
656
|
+
await refreshSortable();
|
|
657
|
+
}
|
|
658
|
+
async function copyConfig() {
|
|
659
|
+
const config = getValidDraftConfig(t("copy-config-failed"));
|
|
660
|
+
if (!config) {
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
await writeClipboardText(JSON.stringify(config, null, 2), t("copy-config-failed"));
|
|
664
|
+
}
|
|
665
|
+
async function copyMarkdown() {
|
|
666
|
+
const config = getValidDraftConfig(t("copy-markdown-failed"));
|
|
667
|
+
if (!config) {
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
await writeClipboardText(buildMarkdownCopyContent(config), t("copy-markdown-failed"));
|
|
671
|
+
}
|
|
672
|
+
function confirmChanges() {
|
|
673
|
+
const config = buildDraftConfig();
|
|
674
|
+
if (!config) {
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
emit("confirm", config);
|
|
472
678
|
open.value = false;
|
|
473
679
|
}
|
|
474
680
|
</script>
|
|
@@ -480,9 +686,22 @@ function confirmChanges() {
|
|
|
480
686
|
>
|
|
481
687
|
<DialogContent class="flex h-[min(40rem,calc(100vh-4rem))] w-[calc(100%-2rem)] max-w-[calc(100%-2rem)] flex-col overflow-hidden p-0 sm:w-[72rem] sm:max-w-[72rem]">
|
|
482
688
|
<DialogHeader class="gap-1 border-b border-zinc-200 px-6 py-5">
|
|
483
|
-
<
|
|
484
|
-
|
|
485
|
-
|
|
689
|
+
<div class="flex items-center gap-3">
|
|
690
|
+
<DialogTitle class="text-xl font-semibold text-zinc-800">
|
|
691
|
+
{{ t("configure-buttons") }}
|
|
692
|
+
</DialogTitle>
|
|
693
|
+
<Button
|
|
694
|
+
type="button"
|
|
695
|
+
variant="ghost"
|
|
696
|
+
size="sm"
|
|
697
|
+
data-slot="button-configurator-paste"
|
|
698
|
+
class="shrink-0"
|
|
699
|
+
@click="void pasteConfigFromClipboard()"
|
|
700
|
+
>
|
|
701
|
+
<Icon icon="fluent:clipboard-paste-20-regular" />
|
|
702
|
+
{{ t("paste-config") }}
|
|
703
|
+
</Button>
|
|
704
|
+
</div>
|
|
486
705
|
<DialogDescription class="text-sm text-zinc-500">
|
|
487
706
|
{{ t("configure-buttons-description") }}
|
|
488
707
|
</DialogDescription>
|
|
@@ -881,6 +1100,24 @@ function confirmChanges() {
|
|
|
881
1100
|
</div>
|
|
882
1101
|
|
|
883
1102
|
<DialogFooter class="border-t border-zinc-200 px-6 py-4">
|
|
1103
|
+
<Button
|
|
1104
|
+
type="button"
|
|
1105
|
+
variant="ghost"
|
|
1106
|
+
data-slot="button-configurator-copy-markdown"
|
|
1107
|
+
@click="void copyMarkdown()"
|
|
1108
|
+
>
|
|
1109
|
+
<Icon icon="fluent:document-one-page-20-regular" />
|
|
1110
|
+
{{ t("copy-markdown") }}
|
|
1111
|
+
</Button>
|
|
1112
|
+
<Button
|
|
1113
|
+
type="button"
|
|
1114
|
+
variant="ghost"
|
|
1115
|
+
data-slot="button-configurator-copy-config"
|
|
1116
|
+
@click="void copyConfig()"
|
|
1117
|
+
>
|
|
1118
|
+
<Icon icon="fluent:code-text-20-regular" />
|
|
1119
|
+
{{ t("copy-config") }}
|
|
1120
|
+
</Button>
|
|
884
1121
|
<Button
|
|
885
1122
|
type="button"
|
|
886
1123
|
variant="default"
|
|
@@ -909,6 +1146,16 @@ function confirmChanges() {
|
|
|
909
1146
|
"zh": {
|
|
910
1147
|
"configure-buttons": "配置按钮",
|
|
911
1148
|
"configure-buttons-description": "管理多个按钮组、组间距和下拉按钮结构。",
|
|
1149
|
+
"paste-config": "粘贴配置",
|
|
1150
|
+
"paste-config-invalid-json": "粘贴失败,剪贴板不是有效的 JSON。",
|
|
1151
|
+
"paste-config-invalid-schema": "粘贴失败,配置格式无效。",
|
|
1152
|
+
"paste-config-read-failed": "读取剪贴板失败,请检查剪贴板权限。",
|
|
1153
|
+
"copy-paste-error": "复制错误详情",
|
|
1154
|
+
"copy-paste-error-failed": "复制错误详情失败,请检查剪贴板权限。",
|
|
1155
|
+
"copy-markdown": "复制为 Markdown",
|
|
1156
|
+
"copy-markdown-failed": "复制 Markdown 失败,请先修正当前配置中的错误。",
|
|
1157
|
+
"copy-config": "仅复制配置",
|
|
1158
|
+
"copy-config-failed": "复制配置失败,请先修正当前配置中的错误。",
|
|
912
1159
|
"general": "总览",
|
|
913
1160
|
"general-description": "在这里管理按钮组和按钮组之间的间距。",
|
|
914
1161
|
"detail-description": "编辑当前选中节点,或调整它的子项顺序。",
|
|
@@ -951,6 +1198,16 @@ function confirmChanges() {
|
|
|
951
1198
|
"en": {
|
|
952
1199
|
"configure-buttons": "Configure buttons",
|
|
953
1200
|
"configure-buttons-description": "Manage button groups, group gaps, and dropdown structure.",
|
|
1201
|
+
"paste-config": "Paste Config",
|
|
1202
|
+
"paste-config-invalid-json": "Paste failed because the clipboard does not contain valid JSON.",
|
|
1203
|
+
"paste-config-invalid-schema": "Paste failed because the config shape is invalid.",
|
|
1204
|
+
"paste-config-read-failed": "Failed to read from the clipboard. Check clipboard permissions.",
|
|
1205
|
+
"copy-paste-error": "Copy Error Details",
|
|
1206
|
+
"copy-paste-error-failed": "Failed to copy the error details. Check clipboard permissions.",
|
|
1207
|
+
"copy-markdown": "Copy as Markdown",
|
|
1208
|
+
"copy-markdown-failed": "Failed to copy Markdown. Fix the current config errors first.",
|
|
1209
|
+
"copy-config": "Copy Config Only",
|
|
1210
|
+
"copy-config-failed": "Failed to copy the config. Fix the current config errors first.",
|
|
954
1211
|
"general": "General",
|
|
955
1212
|
"general-description": "Manage button groups and the gap between them.",
|
|
956
1213
|
"detail-description": "Edit the selected node and reorder its children.",
|
|
@@ -993,6 +1250,16 @@ function confirmChanges() {
|
|
|
993
1250
|
"ja": {
|
|
994
1251
|
"configure-buttons": "ボタン設定",
|
|
995
1252
|
"configure-buttons-description": "複数のボタングループとドロップダウン構造を管理します。",
|
|
1253
|
+
"paste-config": "設定を貼り付け",
|
|
1254
|
+
"paste-config-invalid-json": "貼り付けに失敗しました。クリップボードの内容が有効な JSON ではありません。",
|
|
1255
|
+
"paste-config-invalid-schema": "貼り付けに失敗しました。設定形式が無効です。",
|
|
1256
|
+
"paste-config-read-failed": "クリップボードの読み取りに失敗しました。権限を確認してください。",
|
|
1257
|
+
"copy-paste-error": "エラー詳細をコピー",
|
|
1258
|
+
"copy-paste-error-failed": "エラー詳細のコピーに失敗しました。クリップボード権限を確認してください。",
|
|
1259
|
+
"copy-markdown": "Markdown としてコピー",
|
|
1260
|
+
"copy-markdown-failed": "Markdown のコピーに失敗しました。現在の設定エラーを先に修正してください。",
|
|
1261
|
+
"copy-config": "設定のみコピー",
|
|
1262
|
+
"copy-config-failed": "設定のコピーに失敗しました。現在の設定エラーを先に修正してください。",
|
|
996
1263
|
"general": "全体",
|
|
997
1264
|
"general-description": "ボタングループとその間隔を管理します。",
|
|
998
1265
|
"detail-description": "選択中ノードを編集し、子項目を並び替えます。",
|
|
@@ -1035,6 +1302,16 @@ function confirmChanges() {
|
|
|
1035
1302
|
"ko": {
|
|
1036
1303
|
"configure-buttons": "버튼 설정",
|
|
1037
1304
|
"configure-buttons-description": "여러 버튼 그룹과 드롭다운 구조를 관리합니다.",
|
|
1305
|
+
"paste-config": "설정 붙여넣기",
|
|
1306
|
+
"paste-config-invalid-json": "붙여넣기에 실패했습니다. 클립보드 내용이 올바른 JSON이 아닙니다.",
|
|
1307
|
+
"paste-config-invalid-schema": "붙여넣기에 실패했습니다. 설정 형식이 올바르지 않습니다.",
|
|
1308
|
+
"paste-config-read-failed": "클립보드를 읽지 못했습니다. 권한을 확인해 주세요.",
|
|
1309
|
+
"copy-paste-error": "오류 세부 정보 복사",
|
|
1310
|
+
"copy-paste-error-failed": "오류 세부 정보를 복사하지 못했습니다. 클립보드 권한을 확인해 주세요.",
|
|
1311
|
+
"copy-markdown": "Markdown으로 복사",
|
|
1312
|
+
"copy-markdown-failed": "Markdown 복사에 실패했습니다. 먼저 현재 설정 오류를 수정해 주세요.",
|
|
1313
|
+
"copy-config": "설정만 복사",
|
|
1314
|
+
"copy-config-failed": "설정 복사에 실패했습니다. 먼저 현재 설정 오류를 수정해 주세요.",
|
|
1038
1315
|
"general": "전체",
|
|
1039
1316
|
"general-description": "버튼 그룹과 그룹 간 간격을 관리합니다.",
|
|
1040
1317
|
"detail-description": "선택한 노드를 편집하고 하위 항목 순서를 조정합니다.",
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { useNuxtApp } from "#app";
|
|
3
|
+
import { Icon } from "@iconify/vue";
|
|
4
|
+
import z from "zod";
|
|
3
5
|
import { ref, watch } from "vue";
|
|
4
6
|
import { useI18n } from "vue-i18n";
|
|
5
7
|
import { hasVisibleLocaleValue } from "../../../utils/coders";
|
|
@@ -26,7 +28,7 @@ const emit = defineEmits(["confirm"]);
|
|
|
26
28
|
const open = defineModel("open", { type: Boolean, ...{
|
|
27
29
|
default: false
|
|
28
30
|
} });
|
|
29
|
-
const { $dsl } = useNuxtApp();
|
|
31
|
+
const { $dsl, $toast } = useNuxtApp();
|
|
30
32
|
const { t } = useI18n();
|
|
31
33
|
const draftLocale = ref();
|
|
32
34
|
const draftInline = ref(false);
|
|
@@ -82,10 +84,24 @@ function validateStyle(style) {
|
|
|
82
84
|
styleError.value = t("style-error");
|
|
83
85
|
return false;
|
|
84
86
|
}
|
|
85
|
-
function
|
|
87
|
+
function showImportError(message) {
|
|
88
|
+
$toast.error(message);
|
|
89
|
+
}
|
|
90
|
+
function showCopyError(message) {
|
|
91
|
+
$toast.error(message);
|
|
92
|
+
}
|
|
93
|
+
function showImportErrorWithCopyAction(message, onClick) {
|
|
94
|
+
$toast.error(message, {
|
|
95
|
+
action: {
|
|
96
|
+
label: t("copy-paste-error"),
|
|
97
|
+
onClick
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
function buildDraftConfig() {
|
|
86
102
|
const normalizedStyle = normalizeStyle(draftStyle.value);
|
|
87
103
|
if (!validateStyle(normalizedStyle)) {
|
|
88
|
-
return;
|
|
104
|
+
return void 0;
|
|
89
105
|
}
|
|
90
106
|
const nextConfig = {};
|
|
91
107
|
const localeValue = normalizeLocale(draftLocale.value);
|
|
@@ -98,7 +114,194 @@ function confirmChanges() {
|
|
|
98
114
|
if (normalizedStyle) {
|
|
99
115
|
nextConfig.style = normalizedStyle;
|
|
100
116
|
}
|
|
101
|
-
|
|
117
|
+
return MarkdownConfigC.parse(nextConfig);
|
|
118
|
+
}
|
|
119
|
+
function getValidDraftConfig(errorMessage) {
|
|
120
|
+
const config = buildDraftConfig();
|
|
121
|
+
if (!config) {
|
|
122
|
+
showCopyError(errorMessage);
|
|
123
|
+
return void 0;
|
|
124
|
+
}
|
|
125
|
+
return config;
|
|
126
|
+
}
|
|
127
|
+
function buildMarkdownConfigJsonSchema() {
|
|
128
|
+
return z.toJSONSchema(MarkdownConfigC, {
|
|
129
|
+
io: "input",
|
|
130
|
+
unrepresentable: "any"
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
function buildAiPromptHeaderMarkdown() {
|
|
134
|
+
return [
|
|
135
|
+
"# Markdown \u914D\u7F6E AI \u4E0A\u4E0B\u6587",
|
|
136
|
+
"\u4F60\u662F\u4E00\u4E2A\u5E2E\u52A9\u7528\u6237\u7F16\u8F91 Markdown \u7EC4\u4EF6\u914D\u7F6E\u7684 AI \u52A9\u624B\u3002",
|
|
137
|
+
"\u5982\u679C\u4F60\u4E0D\u786E\u5B9A\u6E32\u67D3\u4E0A\u4E0B\u6587\u3001\u8868\u8FBE\u5F0F\u542B\u4E49\u6216\u914D\u7F6E\u7EA6\u675F\uFF0C\u5FC5\u987B\u660E\u786E\u8BF4\u660E\u4E0D\u786E\u5B9A\u70B9\uFF0C\u4E0D\u8981\u731C\u6D4B\u3002",
|
|
138
|
+
"\u53EA\u6709\u5F53\u7528\u6237\u660E\u786E\u8981\u6C42\u8F93\u51FA\u914D\u7F6E\u65F6\uFF0C\u624D\u8FD4\u56DE\u5B8C\u6574\u914D\u7F6E\uFF1B\u5982\u679C\u8FD4\u56DE\u914D\u7F6E\uFF0C\u5FC5\u987B\u653E\u5728 Markdown code block \u4E2D\u3002"
|
|
139
|
+
].join("\n");
|
|
140
|
+
}
|
|
141
|
+
function buildMarkdownNotes() {
|
|
142
|
+
return [
|
|
143
|
+
"## \u6CE8\u610F\u4E8B\u9879",
|
|
144
|
+
"- `locale` \u652F\u6301 Markdown \u5185\u5BB9\u548C `{{ expression }}` \u6A21\u677F\u8BED\u6CD5\u3002",
|
|
145
|
+
"- `inline` \u53EA\u80FD\u662F `true` \u6216\u7701\u7565\uFF1B\u7701\u7565\u65F6\u6309 block \u6E32\u67D3\u3002",
|
|
146
|
+
"- `style` \u5FC5\u987B\u662F CEL \u8868\u8FBE\u5F0F\u5B57\u7B26\u4E32\uFF0C\u5E76\u4E14\u8FD0\u884C\u540E\u8FD4\u56DE\u5BF9\u8C61\u3002",
|
|
147
|
+
"- \u5982\u679C\u5F53\u524D\u4FE1\u606F\u4E0D\u8DB3\u4EE5\u786E\u5B9A\u8868\u8FBE\u5F0F\u6216\u6587\u6848\uFF0C\u8BF7\u5148\u6307\u51FA\u7F3A\u5931\u4FE1\u606F\uFF0C\u4E0D\u8981\u81C6\u9020\u4E0A\u4E0B\u6587\u53D8\u91CF\u3002"
|
|
148
|
+
].join("\n");
|
|
149
|
+
}
|
|
150
|
+
function buildMarkdownCopyContent(config) {
|
|
151
|
+
return [
|
|
152
|
+
buildAiPromptHeaderMarkdown(),
|
|
153
|
+
"",
|
|
154
|
+
"## \u5F53\u524D\u914D\u7F6E",
|
|
155
|
+
"```json",
|
|
156
|
+
JSON.stringify(config, null, 2),
|
|
157
|
+
"```",
|
|
158
|
+
"",
|
|
159
|
+
buildMarkdownNotes(),
|
|
160
|
+
"",
|
|
161
|
+
"## MarkdownConfig JSON Schema",
|
|
162
|
+
"```json",
|
|
163
|
+
JSON.stringify(buildMarkdownConfigJsonSchema(), null, 2),
|
|
164
|
+
"```"
|
|
165
|
+
].join("\n");
|
|
166
|
+
}
|
|
167
|
+
function formatIssuePath(path) {
|
|
168
|
+
if (path.length === 0) {
|
|
169
|
+
return "(root)";
|
|
170
|
+
}
|
|
171
|
+
return path.map((segment) => {
|
|
172
|
+
if (typeof segment === "number") {
|
|
173
|
+
return `${segment}`;
|
|
174
|
+
}
|
|
175
|
+
if (typeof segment === "string") {
|
|
176
|
+
return segment;
|
|
177
|
+
}
|
|
178
|
+
return String(segment);
|
|
179
|
+
}).join(".");
|
|
180
|
+
}
|
|
181
|
+
function formatIssueExtraFields(issue) {
|
|
182
|
+
const lines = [];
|
|
183
|
+
for (const [key, value] of Object.entries(issue)) {
|
|
184
|
+
if (key === "code" || key === "message" || key === "path") {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
lines.push(` - ${key}: ${JSON.stringify(value)}`);
|
|
188
|
+
}
|
|
189
|
+
return lines;
|
|
190
|
+
}
|
|
191
|
+
function buildPasteConfigErrorDetails(source, error) {
|
|
192
|
+
if (error instanceof SyntaxError) {
|
|
193
|
+
return [
|
|
194
|
+
"## \u7C98\u8D34\u5931\u8D25\u539F\u56E0",
|
|
195
|
+
"- \u7C7B\u578B\uFF1AJSON \u89E3\u6790\u5931\u8D25",
|
|
196
|
+
`- message: ${error.message}`,
|
|
197
|
+
"",
|
|
198
|
+
"## \u539F\u59CB\u7C98\u8D34\u5185\u5BB9",
|
|
199
|
+
"```text",
|
|
200
|
+
source,
|
|
201
|
+
"```"
|
|
202
|
+
].join("\n");
|
|
203
|
+
}
|
|
204
|
+
const issueLines = error.issues.flatMap((issue, index) => {
|
|
205
|
+
return [
|
|
206
|
+
`### Issue ${index + 1}`,
|
|
207
|
+
`- path: ${formatIssuePath(issue.path)}`,
|
|
208
|
+
`- code: ${issue.code}`,
|
|
209
|
+
`- message: ${issue.message}`,
|
|
210
|
+
...formatIssueExtraFields(issue)
|
|
211
|
+
];
|
|
212
|
+
});
|
|
213
|
+
return [
|
|
214
|
+
"## \u7C98\u8D34\u5931\u8D25\u539F\u56E0",
|
|
215
|
+
"- \u7C7B\u578B\uFF1ASchema \u6821\u9A8C\u5931\u8D25",
|
|
216
|
+
"",
|
|
217
|
+
"## \u539F\u59CB\u7C98\u8D34\u5185\u5BB9",
|
|
218
|
+
"```json",
|
|
219
|
+
source,
|
|
220
|
+
"```",
|
|
221
|
+
"",
|
|
222
|
+
"## Schema \u62A5\u9519",
|
|
223
|
+
...issueLines
|
|
224
|
+
].join("\n");
|
|
225
|
+
}
|
|
226
|
+
function buildPasteConfigErrorMarkdown(source, error) {
|
|
227
|
+
return [
|
|
228
|
+
buildAiPromptHeaderMarkdown(),
|
|
229
|
+
"",
|
|
230
|
+
"## \u5F53\u524D\u4EFB\u52A1",
|
|
231
|
+
"\u7528\u6237\u628A\u4E00\u6BB5\u914D\u7F6E\u7C98\u8D34\u56DE Markdown \u914D\u7F6E\u5668\u65F6\u5931\u8D25\u4E86\u3002\u8BF7\u57FA\u4E8E\u4E0B\u9762\u7684\u539F\u59CB\u5185\u5BB9\u548C\u62A5\u9519\u4FEE\u590D\u5F53\u524D\u914D\u7F6E\u3002",
|
|
232
|
+
"\u8BF7\u4F18\u5148\u4FEE\u590D\u6700\u5C0F\u5FC5\u8981\u8303\u56F4\uFF0C\u4E0D\u8981\u53D1\u660E schema \u4E2D\u4E0D\u5B58\u5728\u7684\u5B57\u6BB5\u3002",
|
|
233
|
+
"\u53EA\u6709\u5F53\u7528\u6237\u660E\u786E\u8981\u6C42\u8F93\u51FA\u914D\u7F6E\u65F6\uFF0C\u624D\u8FD4\u56DE\u5B8C\u6574\u914D\u7F6E\uFF1B\u5982\u679C\u8FD4\u56DE\u914D\u7F6E\uFF0C\u5FC5\u987B\u653E\u5728 Markdown code block \u4E2D\u3002",
|
|
234
|
+
"",
|
|
235
|
+
buildPasteConfigErrorDetails(source, error),
|
|
236
|
+
"",
|
|
237
|
+
buildMarkdownNotes()
|
|
238
|
+
].join("\n");
|
|
239
|
+
}
|
|
240
|
+
async function writeClipboardText(value, errorMessage) {
|
|
241
|
+
try {
|
|
242
|
+
await navigator.clipboard.writeText(value);
|
|
243
|
+
} catch {
|
|
244
|
+
showCopyError(errorMessage);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
async function copyPasteConfigError(source, error) {
|
|
248
|
+
await writeClipboardText(
|
|
249
|
+
buildPasteConfigErrorMarkdown(source, error),
|
|
250
|
+
t("copy-paste-error-failed")
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
async function pasteConfigFromClipboard() {
|
|
254
|
+
let source = "";
|
|
255
|
+
try {
|
|
256
|
+
source = await navigator.clipboard.readText();
|
|
257
|
+
} catch {
|
|
258
|
+
showImportError(t("paste-config-read-failed"));
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
let parsedValue;
|
|
262
|
+
try {
|
|
263
|
+
parsedValue = JSON.parse(source);
|
|
264
|
+
} catch (error) {
|
|
265
|
+
if (error instanceof SyntaxError) {
|
|
266
|
+
showImportErrorWithCopyAction(
|
|
267
|
+
t("paste-config-invalid-json"),
|
|
268
|
+
async () => copyPasteConfigError(source, error)
|
|
269
|
+
);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
showImportError(t("paste-config-invalid-json"));
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
const result = MarkdownConfigC.safeParse(parsedValue);
|
|
276
|
+
if (!result.success) {
|
|
277
|
+
showImportErrorWithCopyAction(
|
|
278
|
+
t("paste-config-invalid-schema"),
|
|
279
|
+
async () => copyPasteConfigError(source, result.error)
|
|
280
|
+
);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
resetDraftState(result.data);
|
|
284
|
+
}
|
|
285
|
+
async function copyConfig() {
|
|
286
|
+
const config = getValidDraftConfig(t("copy-config-failed"));
|
|
287
|
+
if (!config) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
await writeClipboardText(JSON.stringify(config, null, 2), t("copy-config-failed"));
|
|
291
|
+
}
|
|
292
|
+
async function copyMarkdown() {
|
|
293
|
+
const config = getValidDraftConfig(t("copy-markdown-failed"));
|
|
294
|
+
if (!config) {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
await writeClipboardText(buildMarkdownCopyContent(config), t("copy-markdown-failed"));
|
|
298
|
+
}
|
|
299
|
+
function confirmChanges() {
|
|
300
|
+
const config = buildDraftConfig();
|
|
301
|
+
if (!config) {
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
emit("confirm", config);
|
|
102
305
|
open.value = false;
|
|
103
306
|
}
|
|
104
307
|
watch(() => props.config, (value) => {
|
|
@@ -116,9 +319,22 @@ watch(() => props.config, (value) => {
|
|
|
116
319
|
:show-close-button="true"
|
|
117
320
|
>
|
|
118
321
|
<DialogHeader class="gap-1 border-b border-zinc-200 px-6 py-5">
|
|
119
|
-
<
|
|
120
|
-
|
|
121
|
-
|
|
322
|
+
<div class="flex items-center gap-3">
|
|
323
|
+
<DialogTitle class="text-xl font-semibold text-zinc-800">
|
|
324
|
+
{{ t("configure-markdown") }}
|
|
325
|
+
</DialogTitle>
|
|
326
|
+
<Button
|
|
327
|
+
type="button"
|
|
328
|
+
variant="ghost"
|
|
329
|
+
size="sm"
|
|
330
|
+
data-slot="markdown-configurator-paste"
|
|
331
|
+
class="shrink-0"
|
|
332
|
+
@click="void pasteConfigFromClipboard()"
|
|
333
|
+
>
|
|
334
|
+
<Icon icon="fluent:clipboard-paste-20-regular" />
|
|
335
|
+
{{ t("paste-config") }}
|
|
336
|
+
</Button>
|
|
337
|
+
</div>
|
|
122
338
|
<DialogDescription class="text-sm text-zinc-500">
|
|
123
339
|
{{ t("configure-markdown-description") }}
|
|
124
340
|
</DialogDescription>
|
|
@@ -213,6 +429,24 @@ watch(() => props.config, (value) => {
|
|
|
213
429
|
</div>
|
|
214
430
|
|
|
215
431
|
<DialogFooter class="border-t border-zinc-200 px-6 py-4">
|
|
432
|
+
<Button
|
|
433
|
+
data-slot="markdown-configurator-copy-markdown"
|
|
434
|
+
type="button"
|
|
435
|
+
variant="ghost"
|
|
436
|
+
@click="void copyMarkdown()"
|
|
437
|
+
>
|
|
438
|
+
<Icon icon="fluent:document-one-page-20-regular" />
|
|
439
|
+
{{ t("copy-markdown") }}
|
|
440
|
+
</Button>
|
|
441
|
+
<Button
|
|
442
|
+
data-slot="markdown-configurator-copy-config"
|
|
443
|
+
type="button"
|
|
444
|
+
variant="ghost"
|
|
445
|
+
@click="void copyConfig()"
|
|
446
|
+
>
|
|
447
|
+
<Icon icon="fluent:code-text-20-regular" />
|
|
448
|
+
{{ t("copy-config") }}
|
|
449
|
+
</Button>
|
|
216
450
|
<Button
|
|
217
451
|
data-slot="markdown-configurator-cancel"
|
|
218
452
|
type="button"
|
|
@@ -239,6 +473,16 @@ watch(() => props.config, (value) => {
|
|
|
239
473
|
"zh": {
|
|
240
474
|
"configure-markdown": "配置 Markdown",
|
|
241
475
|
"configure-markdown-description": "编辑 Markdown 文案、渲染模式和容器样式。",
|
|
476
|
+
"paste-config": "粘贴配置",
|
|
477
|
+
"paste-config-invalid-json": "粘贴失败,剪贴板不是有效的 JSON。",
|
|
478
|
+
"paste-config-invalid-schema": "粘贴失败,配置格式无效。",
|
|
479
|
+
"paste-config-read-failed": "读取剪贴板失败,请检查剪贴板权限。",
|
|
480
|
+
"copy-paste-error": "复制错误详情",
|
|
481
|
+
"copy-paste-error-failed": "复制错误详情失败,请检查剪贴板权限。",
|
|
482
|
+
"copy-markdown": "复制为 Markdown",
|
|
483
|
+
"copy-markdown-failed": "复制 Markdown 失败,请先修正当前配置中的错误。",
|
|
484
|
+
"copy-config": "仅复制配置",
|
|
485
|
+
"copy-config-failed": "复制配置失败,请先修正当前配置中的错误。",
|
|
242
486
|
"locale": "文案",
|
|
243
487
|
"locale-description": "支持 Markdown 编辑和 {expression} 语法。",
|
|
244
488
|
"render-mode": "渲染模式",
|
|
@@ -255,6 +499,16 @@ watch(() => props.config, (value) => {
|
|
|
255
499
|
"ja": {
|
|
256
500
|
"configure-markdown": "Markdown を設定",
|
|
257
501
|
"configure-markdown-description": "Markdown テキスト、描画モード、コンテナスタイルを編集します。",
|
|
502
|
+
"paste-config": "設定を貼り付け",
|
|
503
|
+
"paste-config-invalid-json": "貼り付けに失敗しました。クリップボードの内容が有効な JSON ではありません。",
|
|
504
|
+
"paste-config-invalid-schema": "貼り付けに失敗しました。設定形式が無効です。",
|
|
505
|
+
"paste-config-read-failed": "クリップボードの読み取りに失敗しました。権限を確認してください。",
|
|
506
|
+
"copy-paste-error": "エラー詳細をコピー",
|
|
507
|
+
"copy-paste-error-failed": "エラー詳細のコピーに失敗しました。クリップボード権限を確認してください。",
|
|
508
|
+
"copy-markdown": "Markdown としてコピー",
|
|
509
|
+
"copy-markdown-failed": "Markdown のコピーに失敗しました。現在の設定エラーを先に修正してください。",
|
|
510
|
+
"copy-config": "設定のみコピー",
|
|
511
|
+
"copy-config-failed": "設定のコピーに失敗しました。現在の設定エラーを先に修正してください。",
|
|
258
512
|
"locale": "テキスト",
|
|
259
513
|
"locale-description": "Markdown と {expression} 構文をサポートします。",
|
|
260
514
|
"render-mode": "描画モード",
|
|
@@ -271,6 +525,16 @@ watch(() => props.config, (value) => {
|
|
|
271
525
|
"en": {
|
|
272
526
|
"configure-markdown": "Configure markdown",
|
|
273
527
|
"configure-markdown-description": "Edit markdown content, render mode, and container style.",
|
|
528
|
+
"paste-config": "Paste Config",
|
|
529
|
+
"paste-config-invalid-json": "Paste failed because the clipboard does not contain valid JSON.",
|
|
530
|
+
"paste-config-invalid-schema": "Paste failed because the config shape is invalid.",
|
|
531
|
+
"paste-config-read-failed": "Failed to read from the clipboard. Check clipboard permissions.",
|
|
532
|
+
"copy-paste-error": "Copy Error Details",
|
|
533
|
+
"copy-paste-error-failed": "Failed to copy the error details. Check clipboard permissions.",
|
|
534
|
+
"copy-markdown": "Copy as Markdown",
|
|
535
|
+
"copy-markdown-failed": "Failed to copy Markdown. Fix the current config errors first.",
|
|
536
|
+
"copy-config": "Copy Config Only",
|
|
537
|
+
"copy-config-failed": "Failed to copy the config. Fix the current config errors first.",
|
|
274
538
|
"locale": "Content",
|
|
275
539
|
"locale-description": "Supports markdown editing and {expression} syntax.",
|
|
276
540
|
"render-mode": "Render mode",
|
|
@@ -701,7 +701,7 @@ export { AccessorC, ColumnC, RenderC, TableConfigC } from "./schema";
|
|
|
701
701
|
v-for="r in rowWindow"
|
|
702
702
|
:key="rows[r.index]?.id ?? r.index"
|
|
703
703
|
:ref="measureRow"
|
|
704
|
-
class="flex absolute w-full
|
|
704
|
+
class="flex absolute w-full border-b border-zinc-300"
|
|
705
705
|
:data-index="rows[r.index]?.index"
|
|
706
706
|
:style="{ transform: `translate3d(0, ${r.start}px, 0)` }"
|
|
707
707
|
>
|