mdk-skills 2.3.19 → 2.3.21

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.
@@ -1,16 +1,5 @@
1
1
  <template>
2
2
  <div class="dashboard">
3
- <!-- 仓库 README -->
4
- <div class="readme-fold" v-if="readmeContent">
5
- <div class="fold-header" @click="readmeOpen = !readmeOpen">
6
- <span class="fold-arrow" :class="{ open: readmeOpen }">▶</span>
7
- 仓库说明
8
- </div>
9
- <div class="fold-body" v-show="readmeOpen">
10
- <div class="markdown-content" v-html="renderedReadme" />
11
- </div>
12
- </div>
13
-
14
3
  <div class="page-header">
15
4
  <h2>技能列表</h2>
16
5
  <div class="header-actions">
@@ -55,6 +44,19 @@
55
44
  </n-tag>
56
45
  </div>
57
46
 
47
+ <div class="search-bar" v-if="!needsSetup && skills.length > 0">
48
+ <n-input
49
+ v-model:value="searchQuery"
50
+ placeholder="搜索技能名称或描述..."
51
+ size="small"
52
+ clearable
53
+ >
54
+ <template #prefix>
55
+ <n-icon><SearchOutline /></n-icon>
56
+ </template>
57
+ </n-input>
58
+ </div>
59
+
58
60
  <n-spin :show="loading">
59
61
  <!-- 引导卡片:未设置技能目录时显示 -->
60
62
  <n-card v-if="needsSetup" class="setup-card" size="small">
@@ -77,16 +79,28 @@
77
79
  <div class="group-header" @click="toggleGroup(group.key)">
78
80
  <span class="fold-arrow" :class="{ open: groupOpen[group.key] }">▶</span>
79
81
  <span class="group-indicator" :class="'indicator-' + group.type"></span>
80
- <span class="group-label">{{ group.type === 'remote' ? group.url : '本地技能' }}</span>
82
+ <span class="group-label">
83
+ <template v-if="group.type === 'remote'">
84
+ <span v-if="editingAlias === group.key" class="alias-edit-inline" @click.stop>
85
+ <n-input v-model:value="editAliasValue" size="tiny" style="width:200px" autofocus @blur="saveAlias(group)" @keyup.enter="saveAlias(group)" @keyup.escape="editingAlias = null" />
86
+ </span>
87
+ <span v-else>
88
+ {{ sourceNames[group.url] || group.url }}
89
+ </span>
90
+ <span class="alias-edit-trigger" @click.stop="startEditAlias(group)">
91
+ <n-icon size="13" class="alias-edit-icon"><PencilOutline /></n-icon>
92
+ </span>
93
+ </template>
94
+ <template v-else>本地技能</template>
95
+ </span>
81
96
  <span class="group-count">{{ group.skills.length }} 个</span>
82
- <n-button
83
- v-if="group.type === 'remote'"
84
- size="tiny"
85
- @click.stop="batchUpdateGroup(group)"
86
- class="group-update-btn"
87
- >
88
- 全部更新
89
- </n-button>
97
+ <span v-if="group.type === 'remote'" @click.stop>
98
+ <n-dropdown trigger="click" :options="getBatchMenuOptions(group)" @select="(key) => onBatchMenuSelect(key, group)">
99
+ <n-button size="tiny" class="group-menu-btn">
100
+ <template #icon><n-icon><EllipsisHorizontalOutline /></n-icon></template>
101
+ </n-button>
102
+ </n-dropdown>
103
+ </span>
90
104
  </div>
91
105
  <div class="group-body" v-show="groupOpen[group.key]">
92
106
  <SkillCard
@@ -103,19 +117,34 @@
103
117
 
104
118
  <n-empty v-if="!loading && skills.length === 0 && !needsSetup" description="暂无技能数据">
105
119
  <template #extra>
106
- <n-button size="small" @click="$router.push({ name: 'Scenes' })">
107
- 前往场景切换
108
- </n-button>
120
+ <n-button size="small" @click="openPullModal">去拉取技能</n-button>
121
+ </template>
122
+ </n-empty>
123
+
124
+ <n-empty v-if="!loading && skills.length > 0 && displaySkills.length === 0" description="没有匹配的技能">
125
+ <template #extra>
126
+ <n-button size="small" @click="selectedTags = []; searchQuery = ''">清除筛选</n-button>
109
127
  </template>
110
128
  </n-empty>
111
129
 
130
+ <!-- 仓库 README(页面底部) -->
131
+ <div class="readme-fold" v-if="readmeContent">
132
+ <div class="fold-header" @click="readmeOpen = !readmeOpen">
133
+ <span class="fold-arrow" :class="{ open: readmeOpen }">▶</span>
134
+ 仓库说明
135
+ </div>
136
+ <div class="fold-body" v-show="readmeOpen">
137
+ <div class="markdown-content" v-html="renderedReadme" />
138
+ </div>
139
+ </div>
140
+
112
141
  <!-- 技能详情弹窗 -->
113
142
  <ModalComp
114
143
  :show="detailVisible"
115
144
  :title="detailSkill?.name || '技能详情'"
116
145
  width="720px"
117
146
  :mask-closable="true"
118
- @update:show="(v) => { if (!v) { detailVisible = false; updatingSkill = false; detailLoading = false; cancelTask(); } }"
147
+ @update:show="handleDetailClose"
119
148
  >
120
149
  <div class="detail-body">
121
150
  <!-- 编辑面板 -->
@@ -142,7 +171,7 @@
142
171
  <template v-if="skillSource.type === 'remote'">
143
172
  <div class="source-row">
144
173
  <span class="source-label">来源</span>
145
- <span class="source-value">{{ skillSource.url }}</span>
174
+ <span class="source-value">{{ sourceNames[skillSource.url] || skillSource.url }}</span>
146
175
  </div>
147
176
  <div class="source-row">
148
177
  <span class="source-label">拉取时间</span>
@@ -197,6 +226,25 @@
197
226
  <div class="pull-input-row">
198
227
  <n-input v-model:value="pullUrl" placeholder="输入 GitHub 仓库地址..." size="small" @keyup.enter="handlePreview" />
199
228
  <n-button size="small" type="primary" @click="handlePreview" :loading="previewing">预览</n-button>
229
+ <n-button size="small" @click="toggleSourceManager">管理源</n-button>
230
+ </div>
231
+
232
+ <!-- 别名管理 -->
233
+ <div v-if="showSourceManager" class="source-manager">
234
+ <div class="source-manager-header">
235
+ <span class="source-manager-title">远程源别名</span>
236
+ <n-button size="tiny" text @click="showSourceManager = false">关闭</n-button>
237
+ </div>
238
+ <div class="source-manager-list">
239
+ <div v-for="url in knownUrlsList" :key="url" class="source-manager-row">
240
+ <span class="source-manager-url" :title="url">{{ url }}</span>
241
+ <n-input v-model:value="editSourceNames[url]" placeholder="输入别名..." size="tiny" style="width:180px" clearable />
242
+ </div>
243
+ <div v-if="knownUrlsList.length === 0" class="source-manager-empty">暂无已拉取的远程源</div>
244
+ </div>
245
+ <div class="source-manager-actions">
246
+ <n-button size="small" type="primary" @click="doSaveSourceNames" :loading="savingSourceNames">保存</n-button>
247
+ </div>
200
248
  </div>
201
249
 
202
250
  <!-- 错误提示 -->
@@ -228,8 +276,10 @@
228
276
  v-for="name in filteredPullSkills"
229
277
  :key="name"
230
278
  v-model:checked="pullCheck[name]"
231
- :label="name"
232
- />
279
+ >
280
+ {{ name }}
281
+ <n-tag v-if="existingSkillNames.has(name)" size="tiny" type="success" :bordered="false" class="installed-tag">已安装</n-tag>
282
+ </n-checkbox>
233
283
  </div>
234
284
  <div class="preview-actions">
235
285
  <n-button size="small" @click="showPullModal = false">取消</n-button>
@@ -347,13 +397,13 @@
347
397
 
348
398
  <script setup>
349
399
  import { ref, computed, onMounted, onActivated, onUnmounted, nextTick } from "vue";
350
- import { NIcon, useMessage } from "naive-ui";
351
- import { RefreshOutline, FolderOpenOutline } from "@vicons/ionicons5";
400
+ import { NIcon, useMessage, createDiscreteApi, darkTheme } from "naive-ui";
401
+ import { RefreshOutline, FolderOpenOutline, SearchOutline, PencilOutline, EllipsisHorizontalOutline } from "@vicons/ionicons5";
352
402
  import { marked } from "marked";
353
403
  import hljs from "highlight.js";
354
404
  import SkillCard from "../components/SkillCard.vue";
355
405
  import ModalComp from "../components/ModalComp.vue";
356
- import { getSkills, getReadme, getSkillReadme, updateSkillMeta, deleteSkill, pullSkills, cancelPull, installSkills, getSkillSource, updateSkill, batchUpdateSkills, openSkillDir, cancelTask } from "../api/skills";
406
+ import { getSkills, getReadme, getSkillReadme, updateSkillMeta, deleteSkill, pullSkills, cancelPull, installSkills, getSkillSource, updateSkill, batchUpdateSkills, openSkillDir, cancelTask, getSourceNames, saveSourceNames, bulkToggleSkills, bulkDeleteSkills } from "../api/skills";
357
407
  import { sortSkills, getUsageMap, recordUsage } from "../utils/usage";
358
408
 
359
409
  // marked 配置:代码高亮 + 外链安全
@@ -382,14 +432,22 @@ marked.use({
382
432
 
383
433
  const emit = defineEmits(["refresh"]);
384
434
  const message = useMessage();
435
+
436
+ function getDialog() {
437
+ const isDark = document.documentElement.getAttribute("data-theme") === "dark";
438
+ return createDiscreteApi(["dialog"], {
439
+ configProviderProps: { theme: isDark ? darkTheme : undefined },
440
+ }).dialog;
441
+ }
385
442
  const skills = ref([]);
386
443
  const loading = ref(false);
387
444
  const needsSetup = ref(false);
445
+ const sourceNames = ref({});
388
446
 
389
447
  // README
390
448
  const readmeContent = ref(null);
391
449
  const renderedReadme = ref("");
392
- const readmeOpen = ref(true);
450
+ const readmeOpen = ref(false);
393
451
 
394
452
  // 详情弹窗
395
453
  const detailVisible = ref(false);
@@ -403,6 +461,13 @@ const editVersion = ref("");
403
461
  const editDescription = ref("");
404
462
  const editTags = ref([]);
405
463
  const savingMeta = ref(false);
464
+ const originalMeta = ref(null);
465
+ const isMetaDirty = computed(() => {
466
+ if (!originalMeta.value) return false;
467
+ return editVersion.value !== originalMeta.value.version
468
+ || editDescription.value !== originalMeta.value.description
469
+ || JSON.stringify([...editTags.value].sort()) !== JSON.stringify([...originalMeta.value.tags].sort());
470
+ });
406
471
 
407
472
  // 分组折叠
408
473
  const groupOpen = ref({});
@@ -448,6 +513,19 @@ async function handleSiblingUpdate() {
448
513
  }
449
514
  }
450
515
 
516
+ // 别名编辑(分组 header 原地编辑)
517
+ const editingAlias = ref(null);
518
+ const editAliasValue = ref("");
519
+
520
+ // 源管理(拉取弹窗内)
521
+ const showSourceManager = ref(false);
522
+ const knownUrlsList = ref([]);
523
+ const editSourceNames = ref({});
524
+ const savingSourceNames = ref(false);
525
+
526
+ // 搜索
527
+ const searchQuery = ref("");
528
+
451
529
  // 标签筛选 & 排序
452
530
  const selectedTags = ref([]);
453
531
  const sortBy = ref("name");
@@ -490,6 +568,8 @@ function openPullModal() {
490
568
  pullCheck.value = {};
491
569
  pullResult.value = null;
492
570
  pullError.value = "";
571
+ showSourceManager.value = false;
572
+ loadSourceNames();
493
573
  nextTick(() => window.scrollTo(0, savedY));
494
574
  }
495
575
 
@@ -558,6 +638,8 @@ async function handlePull() {
558
638
  pulling.value = false;
559
639
  }
560
640
  }
641
+ const existingSkillNames = computed(() => new Set(skills.value.map(s => s.name)));
642
+
561
643
  const allTags = computed(() => {
562
644
  const set = new Set();
563
645
  skills.value.forEach((s) => (s.tags || []).forEach((t) => set.add(t)));
@@ -571,6 +653,13 @@ const displaySkills = computed(() => {
571
653
  selectedTags.value.every((t) => (s.tags || []).includes(t))
572
654
  );
573
655
  }
656
+ if (searchQuery.value) {
657
+ const q = searchQuery.value.toLowerCase();
658
+ filtered = filtered.filter((s) =>
659
+ s.name.toLowerCase().includes(q)
660
+ || (s.description || "").toLowerCase().includes(q)
661
+ );
662
+ }
574
663
  return sortSkills(filtered, sortBy.value);
575
664
  });
576
665
 
@@ -672,6 +761,11 @@ async function showSkillDetail(skill) {
672
761
  editVersion.value = skill.version || "1.0.0";
673
762
  editDescription.value = skill.description || "";
674
763
  editTags.value = [...(skill.tags || [])];
764
+ originalMeta.value = {
765
+ version: editVersion.value,
766
+ description: editDescription.value,
767
+ tags: [...editTags.value],
768
+ };
675
769
  nextTick(() => window.scrollTo(0, savedY));
676
770
  // 查来源
677
771
  try {
@@ -692,6 +786,31 @@ async function showSkillDetail(skill) {
692
786
  }
693
787
  }
694
788
 
789
+ function handleDetailClose(v) {
790
+ if (!v) {
791
+ if (isMetaDirty.value) {
792
+ getDialog().warning({
793
+ title: "未保存的修改",
794
+ content: "编辑面板中有未保存的修改,确定关闭吗?",
795
+ positiveText: "确定关闭",
796
+ negativeText: "取消",
797
+ onPositiveClick: () => {
798
+ closeDetailModal();
799
+ },
800
+ });
801
+ return;
802
+ }
803
+ closeDetailModal();
804
+ }
805
+ }
806
+
807
+ function closeDetailModal() {
808
+ detailVisible.value = false;
809
+ updatingSkill.value = false;
810
+ detailLoading.value = false;
811
+ cancelTask();
812
+ }
813
+
695
814
  async function handleUpdate() {
696
815
  if (!detailSkill.value) return;
697
816
  updatingSkill.value = true;
@@ -778,6 +897,11 @@ async function saveMeta() {
778
897
  detailSkill.value.version = editVersion.value;
779
898
  detailSkill.value.description = editDescription.value;
780
899
  detailSkill.value.tags = [...editTags.value];
900
+ originalMeta.value = {
901
+ version: editVersion.value,
902
+ description: editDescription.value,
903
+ tags: [...editTags.value],
904
+ };
781
905
  await loadSkills();
782
906
  }
783
907
  } catch {
@@ -804,10 +928,130 @@ async function handleDelete() {
804
928
  }
805
929
  }
806
930
 
931
+ // ---------- P1 新功能 ----------
932
+
933
+ // 加载源别名
934
+ async function loadSourceNames() {
935
+ try {
936
+ const res = await getSourceNames();
937
+ sourceNames.value = res.names || {};
938
+ knownUrlsList.value = res.knownUrls || [];
939
+ } catch { /* 静默 */ }
940
+ }
941
+
942
+ // 切换别名管理面板
943
+ function toggleSourceManager() {
944
+ showSourceManager.value = !showSourceManager.value;
945
+ if (showSourceManager.value) {
946
+ editSourceNames.value = { ...sourceNames.value };
947
+ loadSourceNames();
948
+ }
949
+ }
950
+
951
+ // 保存别名
952
+ async function doSaveSourceNames() {
953
+ savingSourceNames.value = true;
954
+ try {
955
+ const names = {};
956
+ for (const [url, alias] of Object.entries(editSourceNames.value)) {
957
+ if (alias && alias.trim()) names[url] = alias.trim();
958
+ }
959
+ const res = await saveSourceNames(names);
960
+ if (res.ok) {
961
+ sourceNames.value = names;
962
+ message.success("别名已保存");
963
+ showSourceManager.value = false;
964
+ }
965
+ } catch {
966
+ message.error("保存失败");
967
+ } finally {
968
+ savingSourceNames.value = false;
969
+ }
970
+ }
971
+
972
+ // 分组 header 别名原地编辑
973
+ function startEditAlias(group) {
974
+ editingAlias.value = group.key;
975
+ editAliasValue.value = sourceNames.value[group.url] || "";
976
+ }
977
+
978
+ async function saveAlias(group) {
979
+ if (!editingAlias.value) return;
980
+ const url = group.url;
981
+ const alias = editAliasValue.value.trim();
982
+ const names = { ...sourceNames.value };
983
+ if (alias) {
984
+ names[url] = alias;
985
+ } else {
986
+ delete names[url];
987
+ }
988
+ editingAlias.value = null;
989
+ try {
990
+ const res = await saveSourceNames(names);
991
+ if (res.ok) sourceNames.value = names;
992
+ } catch { /* 静默 */ }
993
+ }
994
+
995
+ // 分组批量操作菜单
996
+ function getBatchMenuOptions(group) {
997
+ return [
998
+ { label: "全部启用", key: "enable" },
999
+ { label: "全部禁用", key: "disable" },
1000
+ { label: "全部更新", key: "update" },
1001
+ { type: "divider" },
1002
+ { label: "删除该源所有技能", key: "delete" },
1003
+ ];
1004
+ }
1005
+
1006
+ async function onBatchMenuSelect(key, group) {
1007
+ const names = group.skills.map((s) => s.name);
1008
+ switch (key) {
1009
+ case "enable":
1010
+ try {
1011
+ await bulkToggleSkills(names, true);
1012
+ message.success(`${names.length} 个技能已启用`);
1013
+ await loadSkills();
1014
+ } catch {
1015
+ message.error("操作失败");
1016
+ }
1017
+ break;
1018
+ case "disable":
1019
+ try {
1020
+ await bulkToggleSkills(names, false);
1021
+ message.success(`${names.length} 个技能已停用`);
1022
+ await loadSkills();
1023
+ } catch {
1024
+ message.error("操作失败");
1025
+ }
1026
+ break;
1027
+ case "update":
1028
+ await batchUpdateGroup(group);
1029
+ break;
1030
+ case "delete":
1031
+ getDialog().warning({
1032
+ title: "删除确认",
1033
+ content: `确定要删除该源下的 ${names.length} 个技能吗?将同时删除源目录和项目目录中的文件。`,
1034
+ positiveText: "确定删除",
1035
+ negativeText: "取消",
1036
+ onPositiveClick: async () => {
1037
+ try {
1038
+ await bulkDeleteSkills(names);
1039
+ message.success(`${names.length} 个技能已删除`);
1040
+ await loadSkills();
1041
+ } catch {
1042
+ message.error("删除失败");
1043
+ }
1044
+ },
1045
+ });
1046
+ break;
1047
+ }
1048
+ }
1049
+
807
1050
  // 首次加载
808
1051
  onMounted(() => {
809
1052
  loadSkills();
810
1053
  loadReadme();
1054
+ loadSourceNames();
811
1055
  });
812
1056
 
813
1057
  // keep-alive 切回来时自动刷新
@@ -857,6 +1101,10 @@ onUnmounted(() => document.removeEventListener("visibilitychange", onFocus));
857
1101
  flex-wrap: wrap;
858
1102
  }
859
1103
 
1104
+ .search-bar {
1105
+ margin-bottom: 12px;
1106
+ }
1107
+
860
1108
  .detail-status {
861
1109
  display: flex;
862
1110
  justify-content: center;
@@ -1011,6 +1259,93 @@ onUnmounted(() => document.removeEventListener("visibilitychange", onFocus));
1011
1259
  gap: 8px;
1012
1260
  }
1013
1261
 
1262
+ .alias-edit-trigger {
1263
+ display: inline-flex;
1264
+ align-items: center;
1265
+ cursor: pointer;
1266
+ opacity: 0;
1267
+ transition: opacity 0.15s;
1268
+ vertical-align: middle;
1269
+ }
1270
+
1271
+ .group-header:hover .alias-edit-trigger {
1272
+ opacity: 1;
1273
+ }
1274
+
1275
+ .alias-edit-icon {
1276
+ color: #999;
1277
+ }
1278
+
1279
+ .alias-edit-inline {
1280
+ display: inline-flex;
1281
+ align-items: center;
1282
+ }
1283
+
1284
+ .group-menu-btn {
1285
+ flex-shrink: 0;
1286
+ }
1287
+
1288
+ /* 源别名管理 */
1289
+ .source-manager {
1290
+ margin-top: 8px;
1291
+ padding: 10px;
1292
+ background: rgba(0, 0, 0, 0.02);
1293
+ border-radius: 6px;
1294
+ }
1295
+
1296
+ .source-manager-header {
1297
+ display: flex;
1298
+ align-items: center;
1299
+ justify-content: space-between;
1300
+ margin-bottom: 8px;
1301
+ }
1302
+
1303
+ .source-manager-title {
1304
+ font-size: 13px;
1305
+ font-weight: 600;
1306
+ }
1307
+
1308
+ .source-manager-list {
1309
+ max-height: 240px;
1310
+ overflow-y: auto;
1311
+ display: flex;
1312
+ flex-direction: column;
1313
+ gap: 6px;
1314
+ }
1315
+
1316
+ .source-manager-row {
1317
+ display: flex;
1318
+ align-items: center;
1319
+ gap: 8px;
1320
+ }
1321
+
1322
+ .source-manager-url {
1323
+ font-size: 11px;
1324
+ font-family: monospace;
1325
+ color: #888;
1326
+ flex: 1;
1327
+ overflow: hidden;
1328
+ text-overflow: ellipsis;
1329
+ white-space: nowrap;
1330
+ }
1331
+
1332
+ .source-manager-empty {
1333
+ font-size: 12px;
1334
+ color: #999;
1335
+ padding: 12px 0;
1336
+ text-align: center;
1337
+ }
1338
+
1339
+ .source-manager-actions {
1340
+ display: flex;
1341
+ justify-content: flex-end;
1342
+ margin-top: 8px;
1343
+ }
1344
+
1345
+ .installed-tag {
1346
+ margin-left: 4px;
1347
+ }
1348
+
1014
1349
  /* 分组折叠 */
1015
1350
  .skill-group {
1016
1351
  margin-bottom: 4px;
@@ -1068,7 +1403,12 @@ onUnmounted(() => document.removeEventListener("visibilitychange", onFocus));
1068
1403
  gap: 8px;
1069
1404
  }
1070
1405
 
1071
- /* 引导卡片 */
1406
+ /* 底部 README */
1407
+ .readme-fold {
1408
+ margin-top: 24px;
1409
+ border-top: 1px solid rgba(128, 128, 128, 0.15);
1410
+ padding-top: 12px;
1411
+ }
1072
1412
  .setup-card {
1073
1413
  margin-bottom: 20px;
1074
1414
  }
@@ -1316,4 +1656,28 @@ onUnmounted(() => document.removeEventListener("visibilitychange", onFocus));
1316
1656
  [data-theme="dark"] .setup-desc code {
1317
1657
  background: rgba(255, 255, 255, 0.08);
1318
1658
  }
1659
+ [data-theme="dark"] .readme-fold {
1660
+ border-top-color: rgba(255, 255, 255, 0.08);
1661
+ }
1662
+ [data-theme="dark"] .source-manager {
1663
+ background: rgba(255, 255, 255, 0.03);
1664
+ }
1665
+ [data-theme="dark"] .source-manager-url {
1666
+ color: #6c7086;
1667
+ }
1668
+ [data-theme="dark"] .alias-edit-icon {
1669
+ color: #6c7086;
1670
+ }
1671
+ [data-theme="dark"] .source-value {
1672
+ color: #cdd6f4;
1673
+ }
1674
+ [data-theme="dark"] .siblings-desc {
1675
+ color: #cdd6f4;
1676
+ }
1677
+ [data-theme="dark"] .batch-desc {
1678
+ color: #cdd6f4;
1679
+ }
1680
+ [data-theme="dark"] .source-manager-title {
1681
+ color: #e4e4ef;
1682
+ }
1319
1683
  </style>
@@ -640,4 +640,7 @@ onMounted(() => {
640
640
  [data-theme="dark"] .fold-arrow {
641
641
  color: #6c7086;
642
642
  }
643
+ [data-theme="dark"] .skill-group-label {
644
+ color: #cdd6f4;
645
+ }
643
646
  </style>
@@ -1 +0,0 @@
1
- *{box-sizing:border-box;margin:0;padding:0}html,body,#app{background:#f5f7fa;height:100%}body{overflow-y:auto}*{scrollbar-width:none;-ms-overflow-style:none}::-webkit-scrollbar{display:none}.app-layout{flex-direction:column;min-height:100vh;padding-top:56px;display:flex}.app-header{z-index:100;background:#fff;border-bottom:1px solid #e5e7eb;align-items:center;height:56px;padding:0 24px;display:flex;position:fixed;top:0;left:0;right:0}.header-inner{justify-content:space-between;align-items:center;width:100%;display:flex}.header-left{align-items:center;gap:32px;display:flex}.logo{color:#1a1a1a;white-space:nowrap;font-size:18px;font-weight:700}.app-content{flex:1;width:100%;max-width:1000px;margin:0 auto;padding:24px}.markdown-content{color:#333;word-wrap:break-word;font-size:14px;line-height:1.7;overflow:auto}.markdown-content h1{border-bottom:1px solid #eee;margin:20px 0 12px;padding-bottom:8px;font-size:22px;font-weight:700}.markdown-content h2{border-bottom:1px solid #eee;margin:18px 0 10px;padding-bottom:6px;font-size:18px;font-weight:700}.markdown-content h3{margin:16px 0 8px;font-size:16px;font-weight:600}.markdown-content h4{margin:14px 0 6px;font-size:14px;font-weight:600}.markdown-content h5,.markdown-content h6{margin:12px 0 6px;font-size:13px;font-weight:600}.markdown-content p{margin:8px 0}.markdown-content ul,.markdown-content ol{margin:8px 0;padding-left:24px}.markdown-content li{margin:4px 0}.markdown-content blockquote{color:#555;background:#f0f7ff;border-left:4px solid #2080f0;margin:10px 0;padding:8px 16px}.markdown-content blockquote p{margin:4px 0}.markdown-content a{color:#2080f0;text-decoration:none}.markdown-content a:hover{text-decoration:underline}.markdown-content hr{border:none;border-top:1px solid #e0e0e0;margin:16px 0}.markdown-content table{border-collapse:collapse;width:100%;margin:12px 0;font-size:13px}.markdown-content th,.markdown-content td{text-align:left;border:1px solid #e0e0e0;padding:8px 12px}.markdown-content th{background:#f5f7fa;font-weight:600}.markdown-content tr:nth-child(2n) td{background:#fafafa}.markdown-content img{border-radius:4px;max-width:100%;margin:8px 0}.markdown-content code{color:#d63384;background:#f0f0f0;border-radius:3px;padding:2px 6px;font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:13px}.markdown-content pre{border:1px solid #e8e8e8;border-radius:6px;margin:12px 0;padding:0;position:relative;overflow:hidden}.markdown-content pre code{color:#333;tab-size:2;background:#f8f9fa;padding:14px 16px;font-size:13px;line-height:1.5;display:block;overflow-x:auto}.markdown-content pre .copy-btn{color:#999;cursor:pointer;opacity:0;background:#fff;border:1px solid #e0e0e0;border-radius:4px;padding:3px 8px;font-size:11px;transition:opacity .2s;position:absolute;top:6px;right:6px}.readme-fold{border:1px solid #e5e7eb;border-radius:6px;margin-bottom:20px;overflow:hidden}.fold-header{cursor:pointer;-webkit-user-select:none;user-select:none;background:#fafafa;align-items:center;gap:6px;padding:10px 16px;font-size:14px;font-weight:600;display:flex}.fold-header:hover{background:#f0f0f0}.fold-arrow{color:#999;font-size:11px;transition:transform .2s}.fold-arrow.open{transform:rotate(90deg)}.fold-body{padding:0 16px 16px}.markdown-content pre:hover .copy-btn{opacity:1}.markdown-content pre .copy-btn:hover{color:#2080f0;border-color:#2080f0}.header-right{align-items:center;gap:8px;display:flex}html,body,#app,.app-header,.app-content,.logo,.readme-fold,.fold-header,.fold-body,.markdown-content,[class*=n-]{transition:background-color .3s,color .3s,border-color .3s,box-shadow .3s}[data-theme=dark] html,[data-theme=dark] body,[data-theme=dark] #app{background:#1e1e2e}[data-theme=dark] .app-header{background:#1e1e2e;border-bottom-color:#363650}[data-theme=dark] .app-content{background:0 0}[data-theme=dark] .logo{color:#e4e4ef}[data-theme=dark] .header-status-item{color:#a6adc8}[data-theme=dark] .header-status-item.clickable:hover{color:#6a8cff}[data-theme=dark] .header-status-divider{background:#363650}[data-theme=dark] .readme-fold{border-color:#363650}[data-theme=dark] .fold-header{color:#e4e4ef;background:#282840}[data-theme=dark] .fold-header:hover{background:#323250}[data-theme=dark] .fold-body{background:#282840}[data-theme=dark] .markdown-content{color:#cdd6f4}[data-theme=dark] .markdown-content h1,[data-theme=dark] .markdown-content h2{color:#e4e4ef;border-bottom-color:#363650}[data-theme=dark] .markdown-content h3,[data-theme=dark] .markdown-content h4{color:#e4e4ef}[data-theme=dark] .markdown-content code{color:#e06c75;background:#2a2a42}[data-theme=dark] .markdown-content pre{border-color:#363650}[data-theme=dark] .markdown-content pre code{color:#cdd6f4;background:#1a1a2a}[data-theme=dark] .markdown-content blockquote{color:#a6adc8;background:#282840;border-left-color:#6a8cff}[data-theme=dark] .markdown-content a{color:#6a8cff}[data-theme=dark] .markdown-content th{background:#282840}[data-theme=dark] .markdown-content td{border-color:#363650}[data-theme=dark] .markdown-content tr:nth-child(2n) td{background:#24243a}[data-theme=dark] .markdown-content hr{border-top-color:#363650}[data-theme=dark] .markdown-content img{filter:brightness(.8)}[data-theme=dark] .markdown-content pre .copy-btn{color:#6c7086;background:#2a2a42;border-color:#363650}[data-theme=dark] .markdown-content pre .copy-btn:hover{color:#6a8cff;border-color:#6a8cff}[data-theme=dark] .hljs-keyword,[data-theme=dark] .hljs-selector-tag,[data-theme=dark] .hljs-built_in{color:#c678dd}[data-theme=dark] .hljs-string,[data-theme=dark] .hljs-addition{color:#98c379}[data-theme=dark] .hljs-number,[data-theme=dark] .hljs-literal{color:#d19a66}[data-theme=dark] .hljs-comment{color:#5c6370}[data-theme=dark] .hljs-title,[data-theme=dark] .hljs-section{color:#61afef}[data-theme=dark] .hljs-attr,[data-theme=dark] .hljs-attribute,[data-theme=dark] .hljs-selector-class,[data-theme=dark] .hljs-variable{color:#e06c75}[data-theme=dark] .hljs-type,[data-theme=dark] .hljs-meta{color:#e5c07b}[data-theme=dark] .hljs-deletion{color:#e06c75}[data-theme=dark] .hljs-bullet,[data-theme=dark] .hljs-link{color:#6a8cff}[data-theme=dark] .hljs-emphasis{font-style:italic}[data-theme=dark] .hljs-strong{font-weight:700}[data-theme=dark] .page-header h2{color:#e4e4ef}[data-theme=dark] .scene-desc{color:#a6adc8}.fade-enter-active,.fade-leave-active{transition:opacity .2s,transform .2s}.fade-enter-from{opacity:0;transform:translateY(6px)}.fade-leave-to{opacity:0}.header-status{align-items:center;gap:8px;margin-right:8px;display:flex}.header-status-item{color:#666;white-space:nowrap;align-items:center;gap:4px;font-size:12px;display:flex}.header-status-item.clickable{cursor:pointer}.header-status-item.clickable:hover{color:#2080f0}.header-status-divider{background:#e0e0e0;width:1px;height:14px}.skill-card[data-v-5b75bf2b]{cursor:pointer;transition:box-shadow .2s}.skill-card[data-v-5b75bf2b]:hover{box-shadow:0 2px 8px #00000014}.skill-meta[data-v-5b75bf2b]{align-items:center;gap:8px;margin-bottom:8px;display:flex}.skill-version[data-v-5b75bf2b]{color:#888;font-family:monospace;font-size:12px}.skill-desc[data-v-5b75bf2b]{color:#666;text-overflow:ellipsis;white-space:nowrap;margin:0;font-size:13px;overflow:hidden}[data-theme=dark] .skill-version{color:#9399b2}[data-theme=dark] .skill-desc{color:#a6adc8}.modal-overlay[data-v-6c73fe2b]{z-index:2000;background:#00000073;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.modal-card[data-v-6c73fe2b]{background:#fff;border-radius:8px;flex-direction:column;width:90%;max-height:85vh;display:flex;box-shadow:0 8px 32px #0000001f}.modal-header[data-v-6c73fe2b]{justify-content:space-between;align-items:center;padding:14px 20px 0;display:flex}.modal-title[data-v-6c73fe2b]{color:#333;font-size:16px;font-weight:600}.modal-close[data-v-6c73fe2b]{color:#999;cursor:pointer;background:0 0;border:none;border-radius:4px;justify-content:center;align-items:center;width:28px;height:28px;font-size:20px;line-height:1;transition:background .15s,color .15s;display:flex}.modal-close[data-v-6c73fe2b]:hover{color:#333;background:#f0f0f0}.modal-body[data-v-6c73fe2b]{padding:16px 20px;overflow-y:auto}.modal-footer[data-v-6c73fe2b]{border-top:1px solid #f0f0f0;justify-content:flex-end;gap:8px;padding:12px 20px 16px;display:flex}.modal-fade-enter-active[data-v-6c73fe2b]{transition:opacity .2s}.modal-fade-leave-active[data-v-6c73fe2b]{transition:opacity .15s}.modal-fade-enter-from[data-v-6c73fe2b],.modal-fade-leave-to[data-v-6c73fe2b]{opacity:0}[data-theme=dark] .modal-card{background:#2a2a42;box-shadow:0 8px 32px #00000080}[data-theme=dark] .modal-title{color:#e4e4ef}[data-theme=dark] .modal-close{color:#6c7086}[data-theme=dark] .modal-close:hover{color:#cdd6f4;background:#323250}[data-theme=dark] .modal-footer{border-top-color:#363650}[data-theme=dark] .modal-overlay{background:#000000a6}.dashboard[data-v-8946d02e]{overflow-anchor:auto}.page-header[data-v-8946d02e]{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:8px;margin-bottom:4px;display:flex}.page-header h2[data-v-8946d02e]{font-size:20px;font-weight:600}.header-actions[data-v-8946d02e]{align-items:center;gap:8px;display:flex}.tag-filter[data-v-8946d02e]{flex-wrap:wrap;align-items:center;gap:6px;margin-bottom:16px;display:flex}.detail-status[data-v-8946d02e]{justify-content:center;padding:40px 0;display:flex}.detail-body[data-v-8946d02e]{max-height:65vh;overflow-y:auto}.edit-panel[data-v-8946d02e]{margin-bottom:4px}.edit-row[data-v-8946d02e]{align-items:flex-start;gap:10px;margin-bottom:10px;display:flex}.edit-label[data-v-8946d02e]{color:#888;flex-shrink:0;width:50px;font-size:13px;line-height:30px}.update-count-hint[data-v-8946d02e]{color:#e68a00;margin-left:8px;font-family:monospace;font-size:12px}.pull-modal-body[data-v-8946d02e]{min-height:100px}.pull-status[data-v-8946d02e]{color:#666;justify-content:center;align-items:center;gap:10px;padding:24px 0;font-size:13px;display:flex}.preview-result[data-v-8946d02e]{margin-top:8px}.pull-search-input[data-v-8946d02e]{margin-bottom:8px}.preview-info[data-v-8946d02e]{color:#555;align-items:center;gap:8px;margin-bottom:10px;font-size:13px;display:flex}.preview-list[data-v-8946d02e]{background:#00000005;border-radius:6px;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:6px;max-height:300px;margin-bottom:14px;padding:10px;display:grid;overflow-y:auto}.preview-actions[data-v-8946d02e]{justify-content:flex-end;gap:8px;display:flex}.pull-section[data-v-8946d02e]{margin-top:12px}.pull-input-row[data-v-8946d02e]{gap:8px;margin-bottom:8px;display:flex}.pull-input-row .n-input[data-v-8946d02e]{flex:1}.pull-error[data-v-8946d02e]{margin-bottom:8px}.pull-result[data-v-8946d02e]{margin-top:8px}.pull-result-line[data-v-8946d02e]{flex-wrap:wrap;align-items:center;gap:4px;margin-bottom:8px;font-size:13px;display:flex}.pull-result-label[data-v-8946d02e]{color:#888;flex-shrink:0}.pull-result-hint[data-v-8946d02e]{color:#999;font-size:12px}.install-panel[data-v-8946d02e]{background:#00000005;border-radius:6px;margin-top:12px;padding:12px}.install-panel-label[data-v-8946d02e]{color:#666;margin-bottom:8px;font-size:13px}.install-check-list[data-v-8946d02e]{margin-bottom:10px}.install-btn[data-v-8946d02e]{float:right}.edit-actions[data-v-8946d02e]{justify-content:flex-end;gap:8px;display:flex}.skill-group[data-v-8946d02e]{margin-bottom:4px}.group-header[data-v-8946d02e]{cursor:pointer;-webkit-user-select:none;user-select:none;border-radius:4px;align-items:center;gap:8px;padding:8px 4px;display:flex}.group-header[data-v-8946d02e]:hover{background:#00000008}.fold-arrow[data-v-8946d02e]{color:#999;flex-shrink:0;font-size:10px;transition:transform .2s}.fold-arrow.open[data-v-8946d02e]{transform:rotate(90deg)}.group-label[data-v-8946d02e]{color:inherit;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;font-size:13px;font-weight:600;overflow:hidden}.group-count[data-v-8946d02e]{color:#999;flex-shrink:0;font-size:12px}.group-update-btn[data-v-8946d02e]{flex-shrink:0}.group-body[data-v-8946d02e]{grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:8px;display:grid}.setup-card[data-v-8946d02e]{margin-bottom:20px}.setup-card-content[data-v-8946d02e]{text-align:center;flex-direction:column;align-items:center;gap:12px;padding:32px 16px;display:flex}.setup-icon[data-v-8946d02e]{margin-bottom:4px}.setup-title[data-v-8946d02e]{margin:0;font-size:18px;font-weight:600}.setup-desc[data-v-8946d02e]{color:#666;max-width:420px;margin:0;font-size:13px;line-height:1.6}.setup-desc code[data-v-8946d02e]{background:#0000000f;border-radius:3px;padding:1px 6px;font-size:12px}.skill-group.group-remote[data-v-8946d02e]{border-left:3px solid #2080f0;margin-bottom:12px;padding-left:8px}.skill-group.group-local[data-v-8946d02e]{border-left:3px solid #18a058;margin-bottom:12px;padding-left:8px}.group-indicator[data-v-8946d02e]{border-radius:50%;flex-shrink:0;width:6px;height:6px;display:inline-block}.indicator-remote[data-v-8946d02e]{background:#2080f0}.indicator-local[data-v-8946d02e]{background:#18a058}.source-info[data-v-8946d02e]{margin-bottom:4px}.source-row[data-v-8946d02e]{align-items:center;gap:10px;margin-bottom:6px;font-size:13px;display:flex}.source-label[data-v-8946d02e]{color:#888;flex-shrink:0;width:70px}.source-value[data-v-8946d02e]{word-break:break-all}.local-tag[data-v-8946d02e]{color:#2e7d32;background:#e8f5e9;border-radius:3px;padding:1px 8px;font-size:12px;display:inline-block}.path-text[data-v-8946d02e]{color:#666;font-family:monospace;font-size:12px}.source-actions[data-v-8946d02e]{gap:8px;margin-top:8px;display:flex}.local-hint[data-v-8946d02e]{color:#999;background:#00000005;border-radius:4px;margin-top:8px;padding:8px;font-size:12px;line-height:1.5}.siblings-body[data-v-8946d02e]{min-height:60px}.siblings-desc[data-v-8946d02e]{margin-bottom:12px;font-size:13px;line-height:1.6}.siblings-list[data-v-8946d02e]{flex-direction:column;gap:8px;margin-bottom:16px;display:flex}.siblings-actions[data-v-8946d02e]{justify-content:flex-end;gap:8px;display:flex}.batch-modal-body[data-v-8946d02e]{min-height:60px}.batch-desc[data-v-8946d02e]{margin-bottom:16px;font-size:13px;line-height:1.6}.batch-actions[data-v-8946d02e]{justify-content:flex-end;gap:8px;display:flex}.batch-executing[data-v-8946d02e]{color:#666;justify-content:center;align-items:center;gap:12px;padding:24px 0;font-size:13px;display:flex}.batch-result[data-v-8946d02e]{margin-bottom:16px}.batch-result-line[data-v-8946d02e]{flex-wrap:wrap;align-items:center;gap:6px;margin-bottom:10px;font-size:13px;display:flex}.batch-result-label[data-v-8946d02e]{color:#888;flex-shrink:0}[data-theme=dark] .edit-label,[data-theme=dark] .source-label,[data-theme=dark] .pull-result-label,[data-theme=dark] .batch-result-label{color:#9399b2!important}[data-theme=dark] .update-count-hint{color:#fbbf24}[data-theme=dark] .pull-status,[data-theme=dark] .batch-executing,[data-theme=dark] .install-panel-label,[data-theme=dark] .preview-info{color:#a6adc8}[data-theme=dark] .pull-result-hint,[data-theme=dark] .group-count,[data-theme=dark] .local-hint{color:#6c7086}[data-theme=dark] .path-text{color:#a6adc8}[data-theme=dark] .preview-list,[data-theme=dark] .install-panel,[data-theme=dark] .local-hint{background:#ffffff08}[data-theme=dark] .group-header:hover{background:#ffffff0a}[data-theme=dark] .local-tag{color:#4ade80;background:#4ade8026}[data-theme=dark] .skill-group.group-remote{border-left-color:#6a8cff}[data-theme=dark] .skill-group.group-local{border-left-color:#4ade80}[data-theme=dark] .indicator-remote{background:#6a8cff}[data-theme=dark] .indicator-local{background:#4ade80}[data-theme=dark] .setup-desc{color:#a6adc8}[data-theme=dark] .setup-desc code{background:#ffffff14}.page-header[data-v-1815b287]{justify-content:space-between;align-items:center;margin-bottom:20px;display:flex}.page-header h2[data-v-1815b287]{font-size:20px;font-weight:600}.scene-grid[data-v-1815b287]{grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:16px;margin:2px;display:grid}.scene-card[data-v-1815b287]{transition:box-shadow .2s}.scene-card.active[data-v-1815b287]{box-shadow:0 0 0 2px #2080f0}.scene-desc[data-v-1815b287]{color:#666;margin:0;font-size:13px}.scene-footer[data-v-1815b287]{justify-content:space-between;align-items:center;display:flex}.hint-text[data-v-1815b287]{color:#999;font-size:12px}.modal-skill-groups[data-v-1815b287]{max-height:50vh;overflow-y:auto}.skill-group[data-v-1815b287]{margin-bottom:2px}.skill-group-header[data-v-1815b287]{cursor:pointer;-webkit-user-select:none;user-select:none;border-radius:4px;align-items:center;gap:6px;padding:6px 4px;font-size:13px;display:flex}.skill-group-header[data-v-1815b287]:hover{background:#00000008}.fold-arrow[data-v-1815b287]{color:#999;flex-shrink:0;font-size:10px;transition:transform .2s}.fold-arrow.open[data-v-1815b287]{transform:rotate(90deg)}.skill-group-label[data-v-1815b287]{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;font-weight:600;overflow:hidden}.skill-group-count[data-v-1815b287]{color:#999;flex-shrink:0;font-size:12px}.skill-group-body[data-v-1815b287]{padding:4px 0 4px 16px}.search-input[data-v-1815b287]{margin-bottom:12px}[data-theme=dark] .scene-card.active{box-shadow:0 0 0 2px #6a8cff}[data-theme=dark] .hint-text,[data-theme=dark] .skill-group-count{color:#6c7086}[data-theme=dark] .skill-group-header:hover{background:#ffffff0a}[data-theme=dark] .fold-arrow{color:#6c7086}.page-header[data-v-5bb876ac]{margin-bottom:20px}.page-header h2[data-v-5bb876ac]{font-size:20px;font-weight:600}.section[data-v-5bb876ac]{margin-bottom:20px}.init-alert[data-v-5bb876ac]{margin-bottom:12px}.missing-list[data-v-5bb876ac]{flex-direction:column;gap:4px;display:flex}.missing-item[data-v-5bb876ac]{font-size:13px;line-height:1.6}.missing-item code[data-v-5bb876ac]{background:#0000000f;border-radius:3px;padding:1px 6px;font-size:12px}.skill-tag[data-v-5bb876ac]{margin:1px 2px;display:inline-block}.missing-hint[data-v-5bb876ac]{opacity:.7;margin-top:4px;font-size:12px}.source-status[data-v-5bb876ac]{margin-bottom:16px}.source-actions[data-v-5bb876ac]{flex-direction:column;gap:12px;display:flex}.action-buttons[data-v-5bb876ac]{gap:8px;display:flex}.healthy-text[data-v-5bb876ac]{color:#18a058;font-size:13px}.issue-text[data-v-5bb876ac]{color:#d03050;font-size:13px}.readme-dialog-desc[data-v-5bb876ac]{color:#555;margin-bottom:16px;font-size:14px}.readme-checkbox[data-v-5bb876ac]{align-items:flex-start;margin-bottom:12px;display:flex}.readme-checkbox-content[data-v-5bb876ac]{flex-direction:column;gap:2px;display:flex}.readme-checkbox-title[data-v-5bb876ac]{font-size:14px;font-weight:500}.readme-checkbox-desc[data-v-5bb876ac]{color:#999;font-size:12px}.diag-group[data-v-5bb876ac]{margin-bottom:16px}.diag-group[data-v-5bb876ac]:last-child{margin-bottom:0}.diag-group-header[data-v-5bb876ac]{align-items:center;gap:6px;padding:6px 0;font-size:13px;font-weight:600;display:flex}.diag-group-error[data-v-5bb876ac]{color:#d03050}.diag-group-ok[data-v-5bb876ac]{color:#18a058}[data-theme=dark] .missing-item code{background:#ffffff14}[data-theme=dark] .healthy-text{color:#4ade80}[data-theme=dark] .issue-text{color:#f87171}[data-theme=dark] .readme-dialog-desc{color:#a6adc8}[data-theme=dark] .readme-checkbox-desc{color:#6c7086}.page-header[data-v-97d38fe1]{justify-content:space-between;align-items:center;margin-bottom:20px;display:flex}.page-header h2[data-v-97d38fe1]{font-size:20px;font-weight:600}.fade-enter-active[data-v-97d38fe1],.fade-leave-active[data-v-97d38fe1]{transition:opacity .2s}.fade-enter-from[data-v-97d38fe1],.fade-leave-to[data-v-97d38fe1]{opacity:0}pre code.hljs{padding:1em;display:block;overflow-x:auto}code.hljs{padding:3px 5px}.hljs{color:#24292e;background:#fff}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#d73a49}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#6f42c1}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-variable,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id{color:#005cc5}.hljs-regexp,.hljs-string,.hljs-meta .hljs-string{color:#032f62}.hljs-built_in,.hljs-symbol{color:#e36209}.hljs-comment,.hljs-code,.hljs-formula{color:#6a737d}.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo{color:#22863a}.hljs-subst{color:#24292e}.hljs-section{color:#005cc5;font-weight:700}.hljs-bullet{color:#735c0f}.hljs-emphasis{color:#24292e;font-style:italic}.hljs-strong{color:#24292e;font-weight:700}.hljs-addition{color:#22863a;background-color:#f0fff4}.hljs-deletion{color:#b31d28;background-color:#ffeef0}