vue-agent-start 0.1.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.
Files changed (169) hide show
  1. package/package.json +30 -0
  2. package/pnpm-workspace.yaml +3 -0
  3. package/src/agent-flow/adapter/backend.ts +99 -0
  4. package/src/agent-flow/components/ChatIframePanel.vue +773 -0
  5. package/src/agent-flow/components/DatasetItemCard.vue +313 -0
  6. package/src/agent-flow/components/MemoryWindow.vue +104 -0
  7. package/src/agent-flow/components/PromptEditor.vue +804 -0
  8. package/src/agent-flow/components/PromptEditorTagPanel.vue +460 -0
  9. package/src/agent-flow/components/SvgIcon.vue +32 -0
  10. package/src/agent-flow/components/ToolItemCard.vue +169 -0
  11. package/src/agent-flow/components/VariableCard.vue +161 -0
  12. package/src/agent-flow/components/VariableSelector.vue +162 -0
  13. package/src/agent-flow/components/chat-iframe-types.ts +35 -0
  14. package/src/agent-flow/components/stubs/DatasetChooser.vue +70 -0
  15. package/src/agent-flow/components/stubs/Embedding.vue +22 -0
  16. package/src/agent-flow/components/stubs/ModelSelect.vue +24 -0
  17. package/src/agent-flow/components/stubs/RestfulSelectCard.vue +24 -0
  18. package/src/agent-flow/components/stubs/ToolChooser.vue +96 -0
  19. package/src/agent-flow/components/stubs/TriggersItemCard.vue +126 -0
  20. package/src/agent-flow/components/stubs/VariableModal.vue +115 -0
  21. package/src/agent-flow/index.ts +73 -0
  22. package/src/agent-flow/stores/workflow.ts +72 -0
  23. package/src/agent-flow/utils/langUtils.ts +173 -0
  24. package/src/agent-flow/workflow/Ability.vue +65 -0
  25. package/src/agent-flow/workflow/AuthorizationItem.vue +101 -0
  26. package/src/agent-flow/workflow/ConversationVar.vue +42 -0
  27. package/src/agent-flow/workflow/CustomEdge.vue +94 -0
  28. package/src/agent-flow/workflow/EnvironmentVar.vue +40 -0
  29. package/src/agent-flow/workflow/FlowDesigner.vue +2802 -0
  30. package/src/agent-flow/workflow/Icon.vue +179 -0
  31. package/src/agent-flow/workflow/NodeConfig.vue +509 -0
  32. package/src/agent-flow/workflow/NodeConfigCard.vue +460 -0
  33. package/src/agent-flow/workflow/NodeHandle.vue +147 -0
  34. package/src/agent-flow/workflow/NodeHeaderToolbar.vue +77 -0
  35. package/src/agent-flow/workflow/NodeHoverToolbar.vue +176 -0
  36. package/src/agent-flow/workflow/OutputItemCard.vue +668 -0
  37. package/src/agent-flow/workflow/ParamTableItem.vue +200 -0
  38. package/src/agent-flow/workflow/StructTable.vue +202 -0
  39. package/src/agent-flow/workflow/VarInsertField.vue +517 -0
  40. package/src/agent-flow/workflow/VarSelectField.vue +223 -0
  41. package/src/agent-flow/workflow/VariableSelectorItem.vue +48 -0
  42. package/src/agent-flow/workflow/WfField.vue +206 -0
  43. package/src/agent-flow/workflow/_config-base.css +470 -0
  44. package/src/agent-flow/workflow/composables/useNode.js +55 -0
  45. package/src/agent-flow/workflow/index.vue +53 -0
  46. package/src/agent-flow/workflow/nodeCard/AgentNodeCard.vue +180 -0
  47. package/src/agent-flow/workflow/nodeCard/AnswerNodeCard.vue +65 -0
  48. package/src/agent-flow/workflow/nodeCard/ClassifierCard.vue +152 -0
  49. package/src/agent-flow/workflow/nodeCard/CodeNodeCard.vue +139 -0
  50. package/src/agent-flow/workflow/nodeCard/ConditionNodeCard.vue +323 -0
  51. package/src/agent-flow/workflow/nodeCard/DocumentExtractorCard.vue +83 -0
  52. package/src/agent-flow/workflow/nodeCard/EndNodeCard.vue +123 -0
  53. package/src/agent-flow/workflow/nodeCard/HttpNodeCard.vue +451 -0
  54. package/src/agent-flow/workflow/nodeCard/HumanInputCard.vue +152 -0
  55. package/src/agent-flow/workflow/nodeCard/IterationCard.vue +66 -0
  56. package/src/agent-flow/workflow/nodeCard/KnowledgeRetrievalCard.vue +48 -0
  57. package/src/agent-flow/workflow/nodeCard/LLMNodeCard.vue +206 -0
  58. package/src/agent-flow/workflow/nodeCard/ListOperatorCard.vue +188 -0
  59. package/src/agent-flow/workflow/nodeCard/ParameterExtractorCard.vue +352 -0
  60. package/src/agent-flow/workflow/nodeCard/ServiceApiNodeCard.vue +188 -0
  61. package/src/agent-flow/workflow/nodeCard/StartNodeCard.vue +216 -0
  62. package/src/agent-flow/workflow/nodeCard/VariableAssignerCard.vue +125 -0
  63. package/src/agent-flow/workflow/nodeCard/VariableNodeCard.vue +121 -0
  64. package/src/agent-flow/workflow/nodes/AgentNode.vue +122 -0
  65. package/src/agent-flow/workflow/nodes/AnswerNode.vue +99 -0
  66. package/src/agent-flow/workflow/nodes/ClassifierNode.vue +199 -0
  67. package/src/agent-flow/workflow/nodes/CodeNode.vue +152 -0
  68. package/src/agent-flow/workflow/nodes/ConditionNode.vue +232 -0
  69. package/src/agent-flow/workflow/nodes/DocumentExtractorNode.vue +96 -0
  70. package/src/agent-flow/workflow/nodes/EndNode.vue +119 -0
  71. package/src/agent-flow/workflow/nodes/FileUploadNode.vue +97 -0
  72. package/src/agent-flow/workflow/nodes/HttpNode.vue +204 -0
  73. package/src/agent-flow/workflow/nodes/HumanInputNode.vue +110 -0
  74. package/src/agent-flow/workflow/nodes/IterationNode.vue +98 -0
  75. package/src/agent-flow/workflow/nodes/KnowledgeRetrievalNode.vue +253 -0
  76. package/src/agent-flow/workflow/nodes/LLMNode.vue +134 -0
  77. package/src/agent-flow/workflow/nodes/ListOperatorNode.vue +111 -0
  78. package/src/agent-flow/workflow/nodes/LoopNode.vue +94 -0
  79. package/src/agent-flow/workflow/nodes/ParameterExtractorNode.vue +147 -0
  80. package/src/agent-flow/workflow/nodes/ServiceApiNode.vue +161 -0
  81. package/src/agent-flow/workflow/nodes/StartNode.vue +199 -0
  82. package/src/agent-flow/workflow/nodes/TemplateNode.vue +103 -0
  83. package/src/agent-flow/workflow/nodes/UserInputNode.vue +92 -0
  84. package/src/agent-flow/workflow/nodes/VariableAssignerNode.vue +145 -0
  85. package/src/agent-flow/workflow/nodes/VariableNode.vue +130 -0
  86. package/src/agent-flow/workflow/nodes/_node-base.css +338 -0
  87. package/src/agent-flow/workflow/utils/node_card_form.ts +335 -0
  88. package/src/agent-flow/workflow/utils/node_config.ts +63 -0
  89. package/src/agent-flow/workflow/utils/workflow_utils.ts +151 -0
  90. package/src/agent-studio/adapters/springAgentStart.ts +390 -0
  91. package/src/agent-studio/api/index.ts +1 -0
  92. package/src/agent-studio/api/types.ts +173 -0
  93. package/src/agent-studio/components/AgentApiDocs.vue +500 -0
  94. package/src/agent-studio/components/AgentAppsPage.vue +1601 -0
  95. package/src/agent-studio/components/AgentCardGrid.vue +259 -0
  96. package/src/agent-studio/components/AgentChatPage.vue +553 -0
  97. package/src/agent-studio/components/AgentDebugPanel.vue +350 -0
  98. package/src/agent-studio/components/AgentLogsPanel.vue +391 -0
  99. package/src/agent-studio/components/AgentMonitorPanel.vue +204 -0
  100. package/src/agent-studio/components/AgentOrchestrate.vue +505 -0
  101. package/src/agent-studio/components/AgentPromptEditor.vue +237 -0
  102. package/src/agent-studio/components/AgentStudioShell.vue +227 -0
  103. package/src/agent-studio/components/AgentToolsPanel.vue +394 -0
  104. package/src/agent-studio/components/AgentVariablesPanel.vue +205 -0
  105. package/src/agent-studio/components/ApiKeyManager.vue +251 -0
  106. package/src/agent-studio/components/AppDesignDrawer.vue +3391 -0
  107. package/src/agent-studio/components/CreateAppModal.vue +737 -0
  108. package/src/agent-studio/components/SparkChart.vue +200 -0
  109. package/src/agent-studio/components/VariableEditorModal.vue +499 -0
  110. package/src/agent-studio/composables/useAgentStudio.ts +347 -0
  111. package/src/agent-studio/index.ts +60 -0
  112. package/src/agent-studio/panels/DrawerFlowDesigner.vue +203 -0
  113. package/src/agent-studio/panels/LogAnnotationPanel.vue +490 -0
  114. package/src/agent-studio/panels/MonitorPanel.vue +535 -0
  115. package/src/agent-studio/types.ts +242 -0
  116. package/src/index.ts +155 -0
  117. package/src/knowledge-hub/adapters/springAgentStart.ts +472 -0
  118. package/src/knowledge-hub/components/AddDocumentsWizard.vue +1452 -0
  119. package/src/knowledge-hub/components/CreateDatasetWizard.vue +1723 -0
  120. package/src/knowledge-hub/components/DatasetCardGrid.vue +467 -0
  121. package/src/knowledge-hub/components/DatasetDetailDrawer.vue +888 -0
  122. package/src/knowledge-hub/components/DatasetPicker.vue +65 -0
  123. package/src/knowledge-hub/components/DatasetPickerModal.vue +526 -0
  124. package/src/knowledge-hub/components/DatasetSettingsPanel.vue +908 -0
  125. package/src/knowledge-hub/components/DatasetSidebar.vue +781 -0
  126. package/src/knowledge-hub/components/DocumentChunksView.vue +802 -0
  127. package/src/knowledge-hub/components/DocumentTable.vue +552 -0
  128. package/src/knowledge-hub/components/HitTestingPanel.vue +74 -0
  129. package/src/knowledge-hub/components/KnowledgeApp.vue +66 -0
  130. package/src/knowledge-hub/components/KnowledgeHubApp.vue +504 -0
  131. package/src/knowledge-hub/components/RecallTestingPanelV2.vue +515 -0
  132. package/src/knowledge-hub/components/RetrievalConfigPopover.vue +270 -0
  133. package/src/knowledge-hub/components/RetrievalMethodPicker.vue +547 -0
  134. package/src/knowledge-hub/components/RetrievedList.vue +24 -0
  135. package/src/knowledge-hub/components/internal/KhDialog.vue +195 -0
  136. package/src/knowledge-hub/composables/useKnowledge.ts +89 -0
  137. package/src/knowledge-hub/i18n/index.ts +2 -0
  138. package/src/knowledge-hub/i18n/messages.ts +422 -0
  139. package/src/knowledge-hub/i18n/useKhI18n.ts +107 -0
  140. package/src/knowledge-hub/index.ts +46 -0
  141. package/src/knowledge-hub/styles/index.css +4 -0
  142. package/src/knowledge-hub/styles/tokens.css +138 -0
  143. package/src/knowledge-hub/types/api.ts +213 -0
  144. package/src/knowledge-hub/types/dataset.ts +88 -0
  145. package/src/knowledge-hub/types/document.ts +45 -0
  146. package/src/knowledge-hub/types/index.ts +9 -0
  147. package/src/knowledge-hub/types/retrieval.ts +46 -0
  148. package/src/provider-hub/components/CredentialForm.vue +103 -0
  149. package/src/provider-hub/components/DefaultModelsPanel.vue +270 -0
  150. package/src/provider-hub/components/GroupedModelSelect.vue +445 -0
  151. package/src/provider-hub/components/ModelCardGrid.vue +332 -0
  152. package/src/provider-hub/components/ModelParameterDrawer.vue +739 -0
  153. package/src/provider-hub/components/ModelPickerPopover.vue +1282 -0
  154. package/src/provider-hub/components/ProviderApp.vue +159 -0
  155. package/src/provider-hub/components/ProviderCredentialModal.vue +321 -0
  156. package/src/provider-hub/components/ProviderGallery.vue +131 -0
  157. package/src/provider-hub/components/ProviderHubShell.vue +1992 -0
  158. package/src/provider-hub/components/ProviderIcon.vue +130 -0
  159. package/src/provider-hub/composables/useProviderHub.ts +358 -0
  160. package/src/provider-hub/index.ts +43 -0
  161. package/src/provider-hub/svg/MiniMax.svg +1 -0
  162. package/src/provider-hub/svg/Volcengine.svg +1 -0
  163. package/src/provider-hub/svg/Zhipu.svg +1 -0
  164. package/src/provider-hub/svg/deepseek.svg +1 -0
  165. package/src/provider-hub/svg/moonshot.svg +1 -0
  166. package/src/provider-hub/svg/openai.svg +1 -0
  167. package/src/provider-hub/svg/qwen.svg +1 -0
  168. package/src/provider-hub/svg/siliconflow.svg +1 -0
  169. package/src/provider-hub/types.ts +256 -0
@@ -0,0 +1,737 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * CreateAppModal — Dify-parity two-pane "create application" dialog.
4
+ *
5
+ * Layout:
6
+ *
7
+ * ┌──────────────────────────────────────────────────────────────┐
8
+ * │ 创建应用 从空白开始一个 AI 应用 × │
9
+ * ├────────────────────────────────┬─────────────────────────────┤
10
+ * │ 选择应用类型 │ ┌─────────────────────┐ │
11
+ * │ ┌─────────┐ ┌─────────┐ │ │ │ │
12
+ * │ │ chatflow│ │workflow │ │ │ 应用类型缩略图 │ │
13
+ * │ └─────────┘ └─────────┘ │ │ (右侧随选中变化) │ │
14
+ * │ 初学者场景 ▽ │ │ │ │
15
+ * │ ┌────┐┌────┐┌────┐ │ └─────────────────────┘ │
16
+ * │ │chat││agent││textg│ │ │
17
+ * │ └────┘└────┘└────┘ │ 标题 │
18
+ * │ ── 分割线 ── │ 用户视角的一句话说明 │
19
+ * │ 应用名称 │ │
20
+ * │ [ ][🤖] │ │
21
+ * │ 描述 │ │
22
+ * │ [ ] │ │
23
+ * │ │ │
24
+ * │ 取消 创建 ⌘↵ │ │
25
+ * └────────────────────────────────┴─────────────────────────────┘
26
+ *
27
+ * Emits `create({appType, name, description, icon})` — parent decides where
28
+ * to route: workflow → visual designer; others → open the model-picking form
29
+ * with those fields preseeded.
30
+ */
31
+ import { computed, ref, watch } from 'vue';
32
+
33
+ import { APP_TYPES } from '../composables/useAgentStudio';
34
+ import type { AppType, AppTypeDescriptor } from '../types';
35
+
36
+ interface AppIcon {
37
+ emoji: string;
38
+ background: string;
39
+ }
40
+
41
+ interface Props {
42
+ open: boolean;
43
+ /** Whitelist a subset of app types to show. */
44
+ allow?: AppType[];
45
+ }
46
+
47
+ const props = withDefaults(defineProps<Props>(), {
48
+ allow: () => ['chatbot', 'agent', 'workflow', 'chatflow', 'text-generator'],
49
+ });
50
+
51
+ const emit = defineEmits<{
52
+ (e: 'update:open', v: boolean): void;
53
+ (
54
+ e: 'create',
55
+ payload: {
56
+ appType: AppType;
57
+ name: string;
58
+ description: string;
59
+ icon: AppIcon;
60
+ },
61
+ ): void;
62
+ }>();
63
+
64
+ // -------- state --------
65
+ // Dify shows the "primary" pair (chatflow / workflow) as always-visible, and the
66
+ // beginner set (chatbot / agent / text-gen) behind a collapsible section.
67
+ const PRIMARY_TYPES: AppType[] = ['workflow', 'chatflow'];
68
+ const BEGINNER_TYPES: AppType[] = ['chatbot', 'agent', 'text-generator'];
69
+
70
+ const primaryCards = computed<AppTypeDescriptor[]>(() =>
71
+ APP_TYPES.filter(
72
+ (a) => PRIMARY_TYPES.includes(a.id) && props.allow.includes(a.id),
73
+ ),
74
+ );
75
+ const beginnerCards = computed<AppTypeDescriptor[]>(() =>
76
+ APP_TYPES.filter(
77
+ (a) => BEGINNER_TYPES.includes(a.id) && props.allow.includes(a.id),
78
+ ),
79
+ );
80
+
81
+ const selectedType = ref<AppType>('chatbot');
82
+ const isBeginnerExpanded = ref(true);
83
+
84
+ const currentDesc = computed<AppTypeDescriptor | undefined>(() =>
85
+ APP_TYPES.find((a) => a.id === selectedType.value),
86
+ );
87
+
88
+ const name = ref('');
89
+ const description = ref('');
90
+ const icon = ref<AppIcon>({ emoji: '🤖', background: '#FFEAD5' });
91
+
92
+ // Small emoji + swatch palette; matches Dify's "app icon picker" light version.
93
+ const EMOJIS = ['🤖', '💬', '🧠', '📝', '🎯', '⚡', '🔧', '📊', '💡', '🚀', '📚', '🌐'];
94
+ const SWATCHES = [
95
+ '#FFEAD5',
96
+ '#EEF4FF',
97
+ '#DCFCE7',
98
+ '#FCE7F3',
99
+ '#FEF3C7',
100
+ '#EDE9FE',
101
+ '#DBEAFE',
102
+ '#FFE4E6',
103
+ ];
104
+ const showIconPicker = ref(false);
105
+
106
+ // -------- lifecycle --------
107
+ watch(
108
+ () => props.open,
109
+ (v) => {
110
+ if (v) {
111
+ // Reset form each time the modal opens so a cancelled draft doesn't come back.
112
+ name.value = '';
113
+ description.value = '';
114
+ icon.value = { emoji: '🤖', background: '#FFEAD5' };
115
+ // Pre-select the first allowed type — beginners land on chatbot by default.
116
+ if (props.allow.includes('chatbot')) selectedType.value = 'chatbot';
117
+ else if (props.allow[0]) selectedType.value = props.allow[0];
118
+ showIconPicker.value = false;
119
+ }
120
+ },
121
+ );
122
+
123
+ function close() {
124
+ emit('update:open', false);
125
+ }
126
+
127
+ function submit() {
128
+ if (!name.value.trim()) return;
129
+ emit('create', {
130
+ appType: selectedType.value,
131
+ name: name.value.trim(),
132
+ description: description.value.trim(),
133
+ icon: { ...icon.value },
134
+ });
135
+ close();
136
+ }
137
+
138
+ function onKeydown(e: KeyboardEvent) {
139
+ if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
140
+ e.preventDefault();
141
+ submit();
142
+ } else if (e.key === 'Escape') {
143
+ close();
144
+ }
145
+ }
146
+ </script>
147
+
148
+ <template>
149
+ <!-- disabled="!open": 关闭时 Teleport 跳过挂载,body 里不残留 fixed 遮罩;
150
+ 避免 <KeepAlive> deactivate 后遮罩漏在页面上盖住后续路由。-->
151
+ <Teleport to="body" :disabled="!open">
152
+ <div v-if="open" class="as-modal-mask" @click.self="close" @keydown="onKeydown">
153
+ <div class="as-modal-panel" tabindex="-1" @keydown="onKeydown">
154
+ <!-- Header -->
155
+ <div class="as-modal-header">
156
+ <div>
157
+ <div class="as-modal-title">创建应用</div>
158
+ <div class="as-modal-subtitle">从空白开始配置一个 AI 应用</div>
159
+ </div>
160
+ <button class="as-close" @click="close" aria-label="关闭">×</button>
161
+ </div>
162
+
163
+ <!-- Two-pane body -->
164
+ <div class="as-body">
165
+ <!-- LEFT: type tiles + form -->
166
+ <div class="as-left">
167
+ <div class="as-section-label">选择应用类型</div>
168
+
169
+ <!-- Primary tiles (workflow, potentially chatflow if we add it) -->
170
+ <div v-if="primaryCards.length > 0" class="as-tile-row">
171
+ <button
172
+ v-for="a in primaryCards"
173
+ :key="a.id"
174
+ class="as-tile"
175
+ :class="{ 'as-tile-active': selectedType === a.id }"
176
+ @click="selectedType = a.id"
177
+ >
178
+ <div class="as-tile-icon" :style="{ background: a.iconBg }">
179
+ {{ a.icon }}
180
+ </div>
181
+ <div class="as-tile-title">{{ a.title }}</div>
182
+ <div class="as-tile-desc">{{ a.description }}</div>
183
+ </button>
184
+ </div>
185
+
186
+ <!-- Beginner tiles: collapsible group -->
187
+ <button
188
+ v-if="beginnerCards.length > 0"
189
+ type="button"
190
+ class="as-expand-btn"
191
+ @click="isBeginnerExpanded = !isBeginnerExpanded"
192
+ >
193
+ <span>初学者常用</span>
194
+ <span class="as-caret" :class="{ 'as-caret-open': isBeginnerExpanded }">
195
+
196
+ </span>
197
+ </button>
198
+ <div
199
+ v-if="isBeginnerExpanded && beginnerCards.length > 0"
200
+ class="as-tile-row as-tile-row-3"
201
+ >
202
+ <button
203
+ v-for="a in beginnerCards"
204
+ :key="a.id"
205
+ class="as-tile"
206
+ :class="{ 'as-tile-active': selectedType === a.id }"
207
+ @click="selectedType = a.id"
208
+ >
209
+ <div class="as-tile-icon" :style="{ background: a.iconBg }">
210
+ {{ a.icon }}
211
+ </div>
212
+ <div class="as-tile-title">{{ a.title }}</div>
213
+ <div class="as-tile-desc">{{ a.description }}</div>
214
+ </button>
215
+ </div>
216
+
217
+ <div class="as-divider" />
218
+
219
+ <!-- Form: name + icon (side by side) -->
220
+ <div class="as-field">
221
+ <label class="as-label">
222
+ 应用名称 <span class="as-req">*</span>
223
+ </label>
224
+ <div class="as-name-row">
225
+ <input
226
+ v-model="name"
227
+ class="as-input"
228
+ placeholder="给你的应用起个名字"
229
+ maxlength="60"
230
+ />
231
+ <button
232
+ type="button"
233
+ class="as-icon-btn"
234
+ :style="{ background: icon.background }"
235
+ title="点击选择应用图标"
236
+ @click="showIconPicker = !showIconPicker"
237
+ >
238
+ {{ icon.emoji }}
239
+ </button>
240
+ </div>
241
+ <!-- Icon picker: emoji grid + swatch strip -->
242
+ <div v-if="showIconPicker" class="as-icon-picker">
243
+ <div class="as-emoji-grid">
244
+ <button
245
+ v-for="e in EMOJIS"
246
+ :key="e"
247
+ type="button"
248
+ class="as-emoji-btn"
249
+ :class="{ 'as-emoji-active': icon.emoji === e }"
250
+ @click="icon.emoji = e"
251
+ >
252
+ {{ e }}
253
+ </button>
254
+ </div>
255
+ <div class="as-swatch-row">
256
+ <button
257
+ v-for="s in SWATCHES"
258
+ :key="s"
259
+ type="button"
260
+ class="as-swatch"
261
+ :class="{ 'as-swatch-active': icon.background === s }"
262
+ :style="{ background: s }"
263
+ @click="icon.background = s"
264
+ />
265
+ </div>
266
+ </div>
267
+ </div>
268
+
269
+ <div class="as-field">
270
+ <label class="as-label">
271
+ 描述 <span class="as-optional">(可选)</span>
272
+ </label>
273
+ <textarea
274
+ v-model="description"
275
+ class="as-textarea"
276
+ placeholder="用一两句话说清这个应用要做什么"
277
+ maxlength="255"
278
+ rows="2"
279
+ />
280
+ </div>
281
+
282
+ <!-- Footer -->
283
+ <div class="as-actions">
284
+ <button type="button" class="as-btn as-btn-secondary" @click="close">
285
+ 取消
286
+ </button>
287
+ <button
288
+ type="button"
289
+ class="as-btn as-btn-primary"
290
+ :disabled="!name.trim()"
291
+ @click="submit"
292
+ >
293
+ <span>创建</span>
294
+ <kbd class="as-kbd">⌘</kbd>
295
+ <kbd class="as-kbd">↵</kbd>
296
+ </button>
297
+ </div>
298
+ </div>
299
+
300
+ <!-- RIGHT: preview pane -->
301
+ <div class="as-right">
302
+ <div class="as-preview-title">
303
+ <div class="as-preview-eyebrow">应用预览</div>
304
+ <div class="as-preview-heading">{{ currentDesc?.title }}</div>
305
+ <div class="as-preview-desc">{{ currentDesc?.description }}</div>
306
+ </div>
307
+ <div class="as-preview-canvas" v-html="currentDesc?.previewSvg ?? ''" />
308
+ <div class="as-preview-hint">{{ currentDesc?.hint }}</div>
309
+ </div>
310
+ </div>
311
+ </div>
312
+ </div>
313
+ </Teleport>
314
+ </template>
315
+
316
+ <style scoped>
317
+ .as-modal-mask {
318
+ position: fixed;
319
+ inset: 0;
320
+ z-index: 1050;
321
+ display: flex;
322
+ align-items: center;
323
+ justify-content: center;
324
+ background: rgba(15, 23, 42, 0.55);
325
+ backdrop-filter: blur(2px);
326
+ }
327
+ .as-modal-panel {
328
+ width: min(1080px, 96vw);
329
+ max-height: 92vh;
330
+ overflow: hidden;
331
+ display: flex;
332
+ flex-direction: column;
333
+ background: #fff;
334
+ border-radius: 16px;
335
+ box-shadow: 0 25px 70px rgba(15, 23, 42, 0.25);
336
+ outline: none;
337
+ }
338
+
339
+ /* Header */
340
+ .as-modal-header {
341
+ display: flex;
342
+ align-items: flex-start;
343
+ justify-content: space-between;
344
+ padding: 20px 24px 12px;
345
+ border-bottom: 1px solid #f1f5f9;
346
+ }
347
+ .as-modal-title {
348
+ font-size: 18px;
349
+ font-weight: 600;
350
+ color: #0f172a;
351
+ letter-spacing: 0;
352
+ }
353
+ .as-modal-subtitle {
354
+ margin-top: 4px;
355
+ font-size: 12px;
356
+ color: #94a3b8;
357
+ }
358
+ .as-close {
359
+ width: 32px;
360
+ height: 32px;
361
+ padding: 0;
362
+ border: none;
363
+ border-radius: 8px;
364
+ background: transparent;
365
+ color: #94a3b8;
366
+ font-size: 20px;
367
+ line-height: 1;
368
+ cursor: pointer;
369
+ transition: background 0.15s;
370
+ }
371
+ .as-close:hover {
372
+ background: #f1f5f9;
373
+ color: #475569;
374
+ }
375
+
376
+ /* Two-pane body */
377
+ .as-body {
378
+ display: flex;
379
+ min-height: 560px;
380
+ max-height: calc(92vh - 65px);
381
+ overflow: hidden;
382
+ }
383
+ .as-left {
384
+ flex: 1 1 520px;
385
+ min-width: 460px;
386
+ padding: 20px 24px 22px;
387
+ overflow-y: auto;
388
+ border-right: 1px solid #f1f5f9;
389
+ display: flex;
390
+ flex-direction: column;
391
+ gap: 14px;
392
+ }
393
+ .as-right {
394
+ flex: 1 1 480px;
395
+ min-width: 420px;
396
+ padding: 24px 28px;
397
+ overflow-y: auto;
398
+ background: linear-gradient(180deg, #fafbff 0%, #f4f6fd 100%);
399
+ display: flex;
400
+ flex-direction: column;
401
+ gap: 14px;
402
+ }
403
+
404
+ /* Type-tile section */
405
+ .as-section-label {
406
+ font-size: 13px;
407
+ font-weight: 600;
408
+ color: #334155;
409
+ }
410
+ .as-tile-row {
411
+ display: grid;
412
+ grid-template-columns: repeat(2, minmax(0, 1fr));
413
+ gap: 10px;
414
+ }
415
+ .as-tile-row-3 {
416
+ grid-template-columns: repeat(3, minmax(0, 1fr));
417
+ }
418
+ .as-tile {
419
+ position: relative;
420
+ padding: 10px 12px 12px;
421
+ background: #fff;
422
+ border: 1px solid #e2e8f0;
423
+ border-radius: 12px;
424
+ cursor: pointer;
425
+ text-align: left;
426
+ transition:
427
+ border-color 0.15s ease,
428
+ box-shadow 0.15s ease,
429
+ transform 0.15s ease;
430
+ }
431
+ .as-tile:hover {
432
+ border-color: #cbd5e1;
433
+ box-shadow: 0 2px 6px rgba(15, 23, 42, 0.06);
434
+ transform: translateY(-1px);
435
+ }
436
+ .as-tile-active {
437
+ border-color: transparent;
438
+ outline: 1.5px solid #6366f1;
439
+ box-shadow: 0 4px 10px rgba(99, 102, 241, 0.15);
440
+ }
441
+ .as-tile-icon {
442
+ width: 28px;
443
+ height: 28px;
444
+ display: flex;
445
+ align-items: center;
446
+ justify-content: center;
447
+ border-radius: 6px;
448
+ font-size: 15px;
449
+ }
450
+ .as-tile-title {
451
+ margin-top: 8px;
452
+ font-size: 13px;
453
+ font-weight: 600;
454
+ color: #0f172a;
455
+ }
456
+ .as-tile-desc {
457
+ margin-top: 3px;
458
+ font-size: 11px;
459
+ color: #64748b;
460
+ line-height: 1.45;
461
+ display: -webkit-box;
462
+ -webkit-line-clamp: 2;
463
+ -webkit-box-orient: vertical;
464
+ overflow: hidden;
465
+ }
466
+
467
+ /* Beginner expand toggle */
468
+ .as-expand-btn {
469
+ align-self: flex-start;
470
+ padding: 0;
471
+ border: none;
472
+ background: transparent;
473
+ font-size: 11px;
474
+ font-weight: 500;
475
+ color: #64748b;
476
+ cursor: pointer;
477
+ display: inline-flex;
478
+ align-items: center;
479
+ gap: 4px;
480
+ letter-spacing: 0.02em;
481
+ text-transform: uppercase;
482
+ }
483
+ .as-caret {
484
+ font-size: 14px;
485
+ transition: transform 0.15s;
486
+ }
487
+ .as-caret-open {
488
+ transform: rotate(90deg);
489
+ }
490
+
491
+ /* Divider */
492
+ .as-divider {
493
+ height: 1px;
494
+ margin: 2px 0;
495
+ background: linear-gradient(90deg, transparent, #e2e8f0, transparent);
496
+ }
497
+
498
+ /* Form fields */
499
+ .as-field {
500
+ display: flex;
501
+ flex-direction: column;
502
+ gap: 6px;
503
+ }
504
+ .as-label {
505
+ font-size: 13px;
506
+ font-weight: 600;
507
+ color: #334155;
508
+ }
509
+ .as-req {
510
+ color: #dc2626;
511
+ }
512
+ .as-optional {
513
+ margin-left: 4px;
514
+ font-weight: 400;
515
+ font-size: 11px;
516
+ color: #94a3b8;
517
+ }
518
+ .as-name-row {
519
+ display: flex;
520
+ gap: 10px;
521
+ align-items: center;
522
+ }
523
+ .as-input {
524
+ flex: 1;
525
+ padding: 8px 12px;
526
+ font-size: 13px;
527
+ border: 1px solid #d1d5db;
528
+ border-radius: 8px;
529
+ background: #fff;
530
+ color: #0f172a;
531
+ transition: border-color 0.15s ease;
532
+ }
533
+ .as-input:focus {
534
+ outline: none;
535
+ border-color: #6366f1;
536
+ box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.15);
537
+ }
538
+ .as-textarea {
539
+ padding: 8px 12px;
540
+ font-size: 13px;
541
+ border: 1px solid #d1d5db;
542
+ border-radius: 8px;
543
+ background: #fff;
544
+ color: #0f172a;
545
+ font-family: inherit;
546
+ resize: vertical;
547
+ min-height: 60px;
548
+ transition: border-color 0.15s ease;
549
+ }
550
+ .as-textarea:focus {
551
+ outline: none;
552
+ border-color: #6366f1;
553
+ box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.15);
554
+ }
555
+ .as-icon-btn {
556
+ width: 44px;
557
+ height: 44px;
558
+ display: flex;
559
+ align-items: center;
560
+ justify-content: center;
561
+ padding: 0;
562
+ border: 1px solid #e2e8f0;
563
+ border-radius: 10px;
564
+ font-size: 22px;
565
+ cursor: pointer;
566
+ transition: transform 0.15s ease;
567
+ }
568
+ .as-icon-btn:hover {
569
+ transform: scale(1.05);
570
+ }
571
+
572
+ /* Icon picker inline panel */
573
+ .as-icon-picker {
574
+ margin-top: 8px;
575
+ padding: 10px;
576
+ border: 1px solid #e2e8f0;
577
+ border-radius: 10px;
578
+ background: #f8fafc;
579
+ }
580
+ .as-emoji-grid {
581
+ display: grid;
582
+ grid-template-columns: repeat(6, minmax(0, 1fr));
583
+ gap: 4px;
584
+ }
585
+ .as-emoji-btn {
586
+ padding: 4px;
587
+ font-size: 18px;
588
+ border: 1px solid transparent;
589
+ border-radius: 6px;
590
+ background: transparent;
591
+ cursor: pointer;
592
+ transition: background 0.15s ease;
593
+ }
594
+ .as-emoji-btn:hover {
595
+ background: #fff;
596
+ }
597
+ .as-emoji-active {
598
+ background: #fff;
599
+ border-color: #6366f1;
600
+ }
601
+ .as-swatch-row {
602
+ margin-top: 8px;
603
+ display: flex;
604
+ gap: 6px;
605
+ }
606
+ .as-swatch {
607
+ width: 24px;
608
+ height: 24px;
609
+ padding: 0;
610
+ border-radius: 50%;
611
+ border: 1.5px solid #fff;
612
+ outline: 1px solid #e2e8f0;
613
+ cursor: pointer;
614
+ transition: transform 0.15s ease;
615
+ }
616
+ .as-swatch:hover {
617
+ transform: scale(1.15);
618
+ }
619
+ .as-swatch-active {
620
+ outline: 2px solid #6366f1;
621
+ }
622
+
623
+ /* Footer actions */
624
+ .as-actions {
625
+ display: flex;
626
+ justify-content: flex-end;
627
+ gap: 10px;
628
+ margin-top: auto;
629
+ padding-top: 8px;
630
+ }
631
+ .as-btn {
632
+ display: inline-flex;
633
+ align-items: center;
634
+ gap: 6px;
635
+ padding: 8px 16px;
636
+ border: none;
637
+ border-radius: 8px;
638
+ font-size: 13px;
639
+ font-weight: 500;
640
+ cursor: pointer;
641
+ transition:
642
+ background 0.15s ease,
643
+ transform 0.05s ease;
644
+ }
645
+ .as-btn-secondary {
646
+ background: #f1f5f9;
647
+ color: #475569;
648
+ }
649
+ .as-btn-secondary:hover {
650
+ background: #e2e8f0;
651
+ }
652
+ .as-btn-primary {
653
+ background: linear-gradient(135deg, #6366f1 0%, #4f46e5 100%);
654
+ color: #fff;
655
+ box-shadow: 0 2px 6px rgba(79, 70, 229, 0.3);
656
+ }
657
+ .as-btn-primary:hover {
658
+ transform: translateY(-1px);
659
+ box-shadow: 0 4px 12px rgba(79, 70, 229, 0.4);
660
+ }
661
+ .as-btn-primary:disabled {
662
+ background: #cbd5e1;
663
+ box-shadow: none;
664
+ cursor: not-allowed;
665
+ transform: none;
666
+ }
667
+ .as-kbd {
668
+ display: inline-flex;
669
+ align-items: center;
670
+ justify-content: center;
671
+ padding: 0 5px;
672
+ min-width: 18px;
673
+ height: 18px;
674
+ background: rgba(255, 255, 255, 0.2);
675
+ border-radius: 4px;
676
+ font-size: 10px;
677
+ font-family: inherit;
678
+ }
679
+
680
+ /* Right pane preview */
681
+ .as-preview-eyebrow {
682
+ font-size: 10px;
683
+ font-weight: 600;
684
+ color: #94a3b8;
685
+ letter-spacing: 0.08em;
686
+ text-transform: uppercase;
687
+ }
688
+ .as-preview-heading {
689
+ margin-top: 2px;
690
+ font-size: 15px;
691
+ font-weight: 600;
692
+ color: #0f172a;
693
+ }
694
+ .as-preview-desc {
695
+ margin-top: 4px;
696
+ font-size: 12px;
697
+ color: #64748b;
698
+ line-height: 1.5;
699
+ }
700
+ .as-preview-canvas {
701
+ margin-top: 4px;
702
+ padding: 8px;
703
+ background:
704
+ repeating-linear-gradient(
705
+ 135deg,
706
+ transparent,
707
+ transparent 4px,
708
+ rgba(15, 23, 42, 0.04) 4px,
709
+ rgba(15, 23, 42, 0.04) 5px
710
+ ),
711
+ #fff;
712
+ border: 1px solid #e2e8f0;
713
+ border-radius: 12px;
714
+ overflow: hidden;
715
+ flex: 1;
716
+ min-height: 300px;
717
+ display: flex;
718
+ align-items: center;
719
+ justify-content: center;
720
+ }
721
+ .as-preview-canvas :deep(svg) {
722
+ width: 100%;
723
+ height: auto;
724
+ max-height: 340px;
725
+ display: block;
726
+ border-radius: 8px;
727
+ box-shadow: 0 6px 16px rgba(15, 23, 42, 0.1);
728
+ }
729
+ .as-preview-hint {
730
+ padding: 8px 12px;
731
+ font-size: 11px;
732
+ color: #64748b;
733
+ background: rgba(255, 255, 255, 0.6);
734
+ border: 1px dashed #e2e8f0;
735
+ border-radius: 8px;
736
+ }
737
+ </style>