mdk-skills 2.4.0 → 2.4.2

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,99 +0,0 @@
1
- <template>
2
- <n-card class="skill-card" :title="skill.name" size="small" @click="emit('click')">
3
- <template #header-extra>
4
- <span @click.stop>
5
- <n-switch
6
- :value="skill.enabled"
7
- :loading="loading"
8
- @update:value="onToggle"
9
- />
10
- </span>
11
- </template>
12
- <div class="skill-meta">
13
- <span class="skill-version">{{ skill._updateCount > 0 ? 'r' + skill._updateCount : 'v' + skill.version }}</span>
14
- <n-tag v-for="tag in skill.tags" :key="tag" size="tiny" :bordered="false">
15
- {{ tag }}
16
- </n-tag>
17
- <n-tag v-if="usages > 0" size="tiny" type="warning" :bordered="false">
18
- 使用 {{ usages }} 次
19
- </n-tag>
20
- </div>
21
- <p class="skill-desc">{{ skill.description || "暂无描述" }}</p>
22
- </n-card>
23
- </template>
24
-
25
- <script setup>
26
- import { ref } from "vue";
27
- import { useMessage } from "naive-ui";
28
- import { toggleSkill } from "../api/skills";
29
- import { recordUsage } from "../utils/usage";
30
-
31
- const props = defineProps({
32
- skill: { type: Object, required: true },
33
- usages: { type: Number, default: 0 },
34
- });
35
-
36
- const emit = defineEmits(["refresh", "click"]);
37
- const message = useMessage();
38
- const loading = ref(false);
39
-
40
- async function onToggle(value) {
41
- loading.value = true;
42
- try {
43
- const res = await toggleSkill(props.skill.name, value);
44
- if (res.ok) {
45
- if (value) recordUsage(props.skill.name);
46
- message.success(`技能 "${props.skill.name}" 已${value ? "启用" : "停用"}`);
47
- emit("refresh");
48
- } else if (res.locked) {
49
- message.warning(`技能 "${props.skill.name}" 被其他程序占用,无法${value ? "启用" : "停用"}`);
50
- }
51
- } catch {
52
- message.error("操作失败");
53
- } finally {
54
- loading.value = false;
55
- }
56
- }
57
- </script>
58
-
59
- <style scoped>
60
- .skill-card {
61
- cursor: pointer;
62
- transition: box-shadow 0.2s;
63
- }
64
-
65
- .skill-card:hover {
66
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
67
- }
68
-
69
- .skill-meta {
70
- display: flex;
71
- align-items: center;
72
- gap: 8px;
73
- margin-bottom: 8px;
74
- }
75
-
76
- .skill-version {
77
- font-size: 12px;
78
- color: #888;
79
- font-family: monospace;
80
- }
81
-
82
- .skill-desc {
83
- font-size: 13px;
84
- color: #666;
85
- margin: 0;
86
- overflow: hidden;
87
- text-overflow: ellipsis;
88
- white-space: nowrap;
89
- }
90
- </style>
91
-
92
- <style>
93
- [data-theme="dark"] .skill-version {
94
- color: #9399b2;
95
- }
96
- [data-theme="dark"] .skill-desc {
97
- color: #a6adc8;
98
- }
99
- </style>
@@ -1,87 +0,0 @@
1
- <template>
2
- <div class="status-bar">
3
- <div class="status-item">
4
- <n-icon size="18" color="#18a058">
5
- <CheckmarkCircleOutline />
6
- </n-icon>
7
- <span>已启用 <strong>{{ data.enabledCount }}</strong> / {{ data.totalCount }} 个技能</span>
8
- </div>
9
- <div class="status-divider" />
10
- <div class="status-item">
11
- <n-icon size="18" color="#2080f0">
12
- <SwapHorizontalOutline />
13
- </n-icon>
14
- <span>
15
- 当前场景:
16
- <n-tag size="small" type="info">
17
- {{ data.activeProfile?.name || "自定义" }}
18
- </n-tag>
19
- </span>
20
- </div>
21
- <div class="status-divider" />
22
- <div class="status-item clickable" @click="$router.push({ name: 'ClaudeMd' })">
23
- <n-icon size="18" :color="data.hasClaudeMd ? '#18a058' : '#d03050'">
24
- <DocumentTextOutline />
25
- </n-icon>
26
- <span>CLAUDE.md {{ data.hasClaudeMd ? "已就绪" : "未创建" }}</span>
27
- </div>
28
- </div>
29
- </template>
30
-
31
- <script setup>
32
- import { CheckmarkCircleOutline, SwapHorizontalOutline, DocumentTextOutline } from "@vicons/ionicons5";
33
-
34
- defineProps({
35
- data: {
36
- type: Object,
37
- required: true,
38
- },
39
- });
40
- </script>
41
-
42
- <style scoped>
43
- .status-bar {
44
- display: flex;
45
- align-items: center;
46
- gap: 12px;
47
- padding: 10px 24px;
48
- background: #fff;
49
- border-bottom: 1px solid #e5e7eb;
50
- flex-wrap: wrap;
51
- }
52
-
53
- .status-item {
54
- display: flex;
55
- align-items: center;
56
- gap: 6px;
57
- font-size: 13px;
58
- color: #555;
59
- }
60
-
61
- .status-item.clickable {
62
- cursor: pointer;
63
- }
64
-
65
- .status-item.clickable:hover {
66
- color: #2080f0;
67
- }
68
-
69
- .status-divider {
70
- width: 1px;
71
- height: 20px;
72
- background: #e0e0e0;
73
- }
74
- </style>
75
-
76
- <style>
77
- [data-theme="dark"] .status-bar {
78
- background: #1e1e2e;
79
- border-bottom-color: #363650;
80
- }
81
- [data-theme="dark"] .status-item {
82
- color: #a6adc8;
83
- }
84
- [data-theme="dark"] .status-divider {
85
- background: #363650;
86
- }
87
- </style>
@@ -1,10 +0,0 @@
1
- import { createApp } from "vue";
2
- import naive from "naive-ui";
3
- import App from "./App.vue";
4
- import router from "./router";
5
- import "highlight.js/styles/github.css";
6
-
7
- const app = createApp(App);
8
- app.use(naive);
9
- app.use(router);
10
- app.mount("#app");
@@ -1,17 +0,0 @@
1
- import { createRouter, createWebHistory } from "vue-router";
2
- import Dashboard from "../views/Dashboard.vue";
3
- import SceneSwitch from "../views/SceneSwitch.vue";
4
- import Settings from "../views/Settings.vue";
5
- import ReadmeView from "../views/ReadmeView.vue";
6
-
7
- const routes = [
8
- { path: "/", name: "Dashboard", component: Dashboard, meta: { title: "仪表盘" } },
9
- { path: "/scenes", name: "Scenes", component: SceneSwitch, meta: { title: "场景切换" } },
10
- { path: "/settings", name: "Settings", component: Settings, meta: { title: "设置" } },
11
- { path: "/readme", name: "ClaudeMd", component: ReadmeView, meta: { title: "CLAUDE.md" } },
12
- ];
13
-
14
- export default createRouter({
15
- history: createWebHistory(),
16
- routes,
17
- });
@@ -1,352 +0,0 @@
1
- * {
2
- margin: 0;
3
- padding: 0;
4
- box-sizing: border-box;
5
- }
6
-
7
- html, body, #app {
8
- height: 100%;
9
- background: #f5f7fa;
10
- }
11
-
12
- body {
13
- overflow-y: auto;
14
- }
15
-
16
- /* 全局隐藏所有滚动条,能滚但不显示 */
17
- * {
18
- scrollbar-width: none;
19
- -ms-overflow-style: none;
20
- }
21
- *::-webkit-scrollbar {
22
- display: none;
23
- }
24
-
25
- .app-layout {
26
- min-height: 100vh;
27
- display: flex;
28
- flex-direction: column;
29
- padding-top: 56px;
30
- }
31
-
32
- .app-header {
33
- position: fixed;
34
- top: 0;
35
- left: 0;
36
- right: 0;
37
- height: 56px;
38
- display: flex;
39
- align-items: center;
40
- background: #fff;
41
- padding: 0 24px;
42
- z-index: 100;
43
- border-bottom: 1px solid #e5e7eb;
44
- }
45
-
46
- .header-inner {
47
- width: 100%;
48
- display: flex;
49
- align-items: center;
50
- justify-content: space-between;
51
- }
52
-
53
- .header-left {
54
- display: flex;
55
- align-items: center;
56
- gap: 32px;
57
- }
58
-
59
- .logo {
60
- font-size: 18px;
61
- font-weight: 700;
62
- color: #1a1a1a;
63
- white-space: nowrap;
64
- }
65
-
66
- .app-content {
67
- flex: 1;
68
- padding: 24px;
69
- max-width: 1000px;
70
- width: 100%;
71
- margin: 0 auto;
72
- }
73
-
74
- /* ---------- markdown 渲染样式 ---------- */
75
-
76
- .markdown-content {
77
- line-height: 1.7;
78
- font-size: 14px;
79
- color: #333;
80
- word-wrap: break-word;
81
- overflow: auto;
82
- }
83
-
84
- .markdown-content h1 { font-size: 22px; font-weight: 700; margin: 20px 0 12px; padding-bottom: 8px; border-bottom: 1px solid #eee; }
85
- .markdown-content h2 { font-size: 18px; font-weight: 700; margin: 18px 0 10px; padding-bottom: 6px; border-bottom: 1px solid #eee; }
86
- .markdown-content h3 { font-size: 16px; font-weight: 600; margin: 16px 0 8px; }
87
- .markdown-content h4 { font-size: 14px; font-weight: 600; margin: 14px 0 6px; }
88
- .markdown-content h5, .markdown-content h6 { font-size: 13px; font-weight: 600; margin: 12px 0 6px; }
89
-
90
- .markdown-content p { margin: 8px 0; }
91
- .markdown-content ul, .markdown-content ol { margin: 8px 0; padding-left: 24px; }
92
- .markdown-content li { margin: 4px 0; }
93
- .markdown-content blockquote {
94
- margin: 10px 0;
95
- padding: 8px 16px;
96
- border-left: 4px solid #2080f0;
97
- background: #f0f7ff;
98
- color: #555;
99
- }
100
- .markdown-content blockquote p { margin: 4px 0; }
101
-
102
- .markdown-content a { color: #2080f0; text-decoration: none; }
103
- .markdown-content a:hover { text-decoration: underline; }
104
-
105
- .markdown-content hr { margin: 16px 0; border: none; border-top: 1px solid #e0e0e0; }
106
-
107
- .markdown-content table { width: 100%; border-collapse: collapse; margin: 12px 0; font-size: 13px; }
108
- .markdown-content th, .markdown-content td {
109
- padding: 8px 12px;
110
- border: 1px solid #e0e0e0;
111
- text-align: left;
112
- }
113
- .markdown-content th { background: #f5f7fa; font-weight: 600; }
114
- .markdown-content tr:nth-child(even) td { background: #fafafa; }
115
-
116
- .markdown-content img { max-width: 100%; border-radius: 4px; margin: 8px 0; }
117
-
118
- .markdown-content code {
119
- font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
120
- font-size: 13px;
121
- padding: 2px 6px;
122
- background: #f0f0f0;
123
- border-radius: 3px;
124
- color: #d63384;
125
- }
126
-
127
- .markdown-content pre {
128
- margin: 12px 0;
129
- padding: 0;
130
- border-radius: 6px;
131
- overflow: hidden;
132
- border: 1px solid #e8e8e8;
133
- position: relative;
134
- }
135
-
136
- .markdown-content pre code {
137
- display: block;
138
- padding: 14px 16px;
139
- background: #f8f9fa;
140
- color: #333;
141
- font-size: 13px;
142
- line-height: 1.5;
143
- overflow-x: auto;
144
- tab-size: 2;
145
- }
146
-
147
- .markdown-content pre .copy-btn {
148
- position: absolute;
149
- top: 6px;
150
- right: 6px;
151
- padding: 3px 8px;
152
- font-size: 11px;
153
- color: #999;
154
- background: #fff;
155
- border: 1px solid #e0e0e0;
156
- border-radius: 4px;
157
- cursor: pointer;
158
- opacity: 0;
159
- transition: opacity 0.2s;
160
- }
161
-
162
- /* ---------- 手搓折叠 ---------- */
163
- .readme-fold {
164
- margin-bottom: 20px;
165
- border: 1px solid #e5e7eb;
166
- border-radius: 6px;
167
- overflow: hidden;
168
- }
169
- .fold-header {
170
- display: flex;
171
- align-items: center;
172
- gap: 6px;
173
- padding: 10px 16px;
174
- font-size: 14px;
175
- font-weight: 600;
176
- background: #fafafa;
177
- cursor: pointer;
178
- user-select: none;
179
- }
180
- .fold-header:hover {
181
- background: #f0f0f0;
182
- }
183
- .fold-arrow {
184
- font-size: 11px;
185
- transition: transform 0.2s;
186
- color: #999;
187
- }
188
- .fold-arrow.open {
189
- transform: rotate(90deg);
190
- }
191
- .fold-body {
192
- padding: 0 16px 16px;
193
- }
194
-
195
- .markdown-content pre:hover .copy-btn {
196
- opacity: 1;
197
- }
198
-
199
- .markdown-content pre .copy-btn:hover {
200
- color: #2080f0;
201
- border-color: #2080f0;
202
- }
203
-
204
- /* ---------- 导航栏右侧 ---------- */
205
- .header-right {
206
- display: flex;
207
- align-items: center;
208
- gap: 8px;
209
- }
210
-
211
- /* 暗黑模式过渡 */
212
- html, body, #app, .app-header, .app-content,
213
- .logo, .readme-fold, .fold-header, .fold-body,
214
- .markdown-content,
215
- [class*="n-"] {
216
- transition: background-color 0.3s, color 0.3s, border-color 0.3s, box-shadow 0.3s;
217
- }
218
-
219
- /* ---------- 暗黑主题(One Dark Pro 风格) ---------- */
220
- [data-theme="dark"] html,
221
- [data-theme="dark"] body,
222
- [data-theme="dark"] #app {
223
- background: #1e1e2e;
224
- }
225
-
226
- [data-theme="dark"] .app-header {
227
- background: #1e1e2e;
228
- border-bottom-color: #363650;
229
- }
230
-
231
- [data-theme="dark"] .app-content {
232
- background: transparent;
233
- }
234
-
235
- [data-theme="dark"] .logo {
236
- color: #e4e4ef;
237
- }
238
-
239
- [data-theme="dark"] .header-status-item {
240
- color: #a6adc8;
241
- }
242
- [data-theme="dark"] .header-status-item.clickable:hover {
243
- color: #6a8cff;
244
- }
245
- [data-theme="dark"] .header-status-divider {
246
- background: #363650;
247
- }
248
-
249
- [data-theme="dark"] .readme-fold {
250
- border-color: #363650;
251
- }
252
- [data-theme="dark"] .fold-header {
253
- background: #282840;
254
- color: #e4e4ef;
255
- }
256
- [data-theme="dark"] .fold-header:hover {
257
- background: #323250;
258
- }
259
- [data-theme="dark"] .fold-body {
260
- background: #282840;
261
- }
262
- [data-theme="dark"] .fold-arrow {
263
- color: #6c7086;
264
- }
265
-
266
- [data-theme="dark"] .markdown-content {
267
- color: #cdd6f4;
268
- }
269
- [data-theme="dark"] .markdown-content h1,
270
- [data-theme="dark"] .markdown-content h2 {
271
- border-bottom-color: #363650;
272
- color: #e4e4ef;
273
- }
274
- [data-theme="dark"] .markdown-content h3,
275
- [data-theme="dark"] .markdown-content h4 {
276
- color: #e4e4ef;
277
- }
278
- [data-theme="dark"] .markdown-content code {
279
- background: #2a2a42;
280
- color: #e06c75;
281
- }
282
- [data-theme="dark"] .markdown-content pre {
283
- border-color: #363650;
284
- }
285
- [data-theme="dark"] .markdown-content pre code {
286
- background: #1a1a2a;
287
- color: #cdd6f4;
288
- }
289
- [data-theme="dark"] .markdown-content blockquote {
290
- background: #282840;
291
- border-left-color: #6a8cff;
292
- color: #a6adc8;
293
- }
294
- [data-theme="dark"] .markdown-content a {
295
- color: #6a8cff;
296
- }
297
- [data-theme="dark"] .markdown-content th {
298
- background: #282840;
299
- }
300
- [data-theme="dark"] .markdown-content td {
301
- border-color: #363650;
302
- }
303
- [data-theme="dark"] .markdown-content tr:nth-child(even) td {
304
- background: #24243a;
305
- }
306
- [data-theme="dark"] .markdown-content hr {
307
- border-top-color: #363650;
308
- }
309
- [data-theme="dark"] .markdown-content img {
310
- filter: brightness(0.8);
311
- }
312
- [data-theme="dark"] .markdown-content pre .copy-btn {
313
- color: #6c7086;
314
- background: #2a2a42;
315
- border-color: #363650;
316
- }
317
- [data-theme="dark"] .markdown-content pre .copy-btn:hover {
318
- color: #6a8cff;
319
- border-color: #6a8cff;
320
- }
321
-
322
- /* 暗色模式下的 hljs 代码高亮 token(One Dark Pro 配色) */
323
- [data-theme="dark"] .hljs-keyword,
324
- [data-theme="dark"] .hljs-selector-tag,
325
- [data-theme="dark"] .hljs-built_in { color: #c678dd; }
326
- [data-theme="dark"] .hljs-string,
327
- [data-theme="dark"] .hljs-addition { color: #98c379; }
328
- [data-theme="dark"] .hljs-number,
329
- [data-theme="dark"] .hljs-literal { color: #d19a66; }
330
- [data-theme="dark"] .hljs-comment { color: #5c6370; }
331
- [data-theme="dark"] .hljs-title,
332
- [data-theme="dark"] .hljs-section { color: #61afef; }
333
- [data-theme="dark"] .hljs-attr,
334
- [data-theme="dark"] .hljs-attribute,
335
- [data-theme="dark"] .hljs-selector-class,
336
- [data-theme="dark"] .hljs-variable { color: #e06c75; }
337
- [data-theme="dark"] .hljs-type,
338
- [data-theme="dark"] .hljs-meta { color: #e5c07b; }
339
- [data-theme="dark"] .hljs-deletion { color: #e06c75; }
340
- [data-theme="dark"] .hljs-bullet,
341
- [data-theme="dark"] .hljs-link { color: #6a8cff; }
342
- [data-theme="dark"] .hljs-emphasis { font-style: italic; }
343
- [data-theme="dark"] .hljs-strong { font-weight: bold; }
344
-
345
- /* 全局页面标题 */
346
- [data-theme="dark"] .page-header h2 {
347
- color: #e4e4ef;
348
- }
349
-
350
- [data-theme="dark"] .scene-desc {
351
- color: #a6adc8;
352
- }
@@ -1,46 +0,0 @@
1
- const STORAGE_KEY = "mdk-usage";
2
-
3
- function getRaw() {
4
- try {
5
- return JSON.parse(localStorage.getItem(STORAGE_KEY) || "{}");
6
- } catch {
7
- return {};
8
- }
9
- }
10
-
11
- function saveRaw(data) {
12
- localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
13
- }
14
-
15
- /** 记录某个技能被使用一次 */
16
- export function recordUsage(name) {
17
- const data = getRaw();
18
- const entry = data[name] || { count: 0 };
19
- entry.count += 1;
20
- entry.lastUsed = Date.now();
21
- data[name] = entry;
22
- saveRaw(data);
23
- }
24
-
25
- /** 获取完整使用统计数据 */
26
- export function getUsageMap() {
27
- return getRaw();
28
- }
29
-
30
- /**
31
- * 对技能列表排序
32
- * @param {Array} skills - 技能数组
33
- * @param {'name'|'frequency'|'recent'} by - 排序方式
34
- */
35
- export function sortSkills(skills, by) {
36
- const usage = getRaw();
37
- const list = [...skills];
38
- if (by === "frequency") {
39
- list.sort((a, b) => (usage[b.name]?.count || 0) - (usage[a.name]?.count || 0));
40
- } else if (by === "recent") {
41
- list.sort((a, b) => (usage[b.name]?.lastUsed || 0) - (usage[a.name]?.lastUsed || 0));
42
- } else {
43
- list.sort((a, b) => a.name.localeCompare(b.name));
44
- }
45
- return list;
46
- }