@valaxyjs/devtools 0.28.0-beta.7 → 0.28.1
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/client/assets/VDButton-BSVRcA6y.js +1 -0
- package/dist/client/assets/VDCheckbox-DIkwHeip.js +1 -0
- package/dist/client/assets/VDInput-6FQF1GB1.js +1 -0
- package/dist/client/assets/VDInput-D0VAYtEY.css +1 -0
- package/dist/client/assets/VDNumberField-DE-iSRFf.js +1 -0
- package/dist/client/assets/VDPostListItem-Ck7T2w63.js +1 -0
- package/dist/client/assets/VDPostPanel-BbehhYa0.css +1 -0
- package/dist/client/assets/VDPostPanel-CwawT8bS.js +1 -0
- package/dist/client/assets/VDSelect-UHvFCpyn.js +1 -0
- package/dist/client/assets/VDToggleGroup-BcQyHwGa.js +1 -0
- package/dist/client/assets/_plugin-vue_export-helper-PWPZAZD9.js +3 -0
- package/dist/client/assets/api-cJTeRRR-.js +1 -0
- package/dist/client/assets/app-Ciu-q_PC.js +1 -0
- package/dist/client/assets/archives-Cs5YBNbA.js +1 -0
- package/dist/client/assets/batch-edit-CPpsQiMq.js +1 -0
- package/dist/client/assets/batch-edit-CYvJIq6n.css +1 -0
- package/dist/client/assets/categories-Le9fv5rM.js +1 -0
- package/dist/client/assets/collections-DXhCCFt9.js +1 -0
- package/dist/client/assets/config-DuXyfRJ2.js +1 -0
- package/dist/client/assets/en-MQdkqnly.js +1 -0
- package/dist/client/assets/i18n-BF5OnflT.js +1 -0
- package/dist/client/assets/index-CaKwgroK.css +1 -0
- package/dist/client/assets/index-D82QJxg1.js +4 -0
- package/dist/client/assets/pages-xVEnBBX2.js +1 -0
- package/dist/client/assets/posts-C0Pk3Kwo.css +1 -0
- package/dist/client/assets/posts-CfAp665h.js +1 -0
- package/dist/client/assets/rpc-1TCVqa5N.js +1 -0
- package/dist/client/assets/settings-aU8HulMX.js +1 -0
- package/dist/client/assets/splitpanes.es-DbWSeMt3.js +1 -0
- package/dist/client/assets/tags-DLaHWCJB.js +1 -0
- package/dist/client/assets/useKbd-CutV7ZdL.js +1 -0
- package/dist/client/assets/vue.runtime.esm-bundler-DwHYEKV3.js +2 -0
- package/dist/client/assets/zh-CN-z7O7976b.js +1 -0
- package/dist/client/favicon.svg +33 -0
- package/dist/client/index.html +8 -4
- package/dist/index.mjs +404 -52
- package/package.json +7 -7
- package/rpc.d.ts +75 -0
- package/dist/client/assets/about-DgEDNyMK.js +0 -1
- package/dist/client/assets/app-D2GYSWUd.js +0 -1
- package/dist/client/assets/categories-DKPo6rvY.js +0 -1
- package/dist/client/assets/collections-rMAMCBCe.js +0 -1
- package/dist/client/assets/en-DHpmO8Nw.js +0 -1
- package/dist/client/assets/index-B12droL0.js +0 -148
- package/dist/client/assets/index-D8mzkHVf.css +0 -1
- package/dist/client/assets/migration-DsdxoodP.js +0 -788
- package/dist/client/assets/pages-DryDFQhn.js +0 -1
- package/dist/client/assets/tags-68AKj-NG.js +0 -1
- package/dist/client/assets/vue.runtime.esm-bundler-D01s-oMJ.js +0 -2
- package/dist/client/assets/zh-CN-vDynnsP5.js +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { colors } from 'consola/utils';
|
|
2
2
|
import sirv from 'sirv';
|
|
3
|
-
import { createRPCServer } from 'vite-dev-rpc';
|
|
4
3
|
import { dirname, resolve } from 'node:path';
|
|
5
4
|
import { fileURLToPath } from 'node:url';
|
|
6
5
|
import bodyParser from 'body-parser';
|
|
7
|
-
import fs from 'fs-extra';
|
|
8
|
-
import matter from 'gray-matter';
|
|
9
|
-
import { JSON_SCHEMA } from 'js-yaml';
|
|
10
6
|
import process from 'node:process';
|
|
11
7
|
import dayjs from 'dayjs';
|
|
12
8
|
import fg from 'fast-glob';
|
|
9
|
+
import fs from 'fs-extra';
|
|
10
|
+
import matter from 'gray-matter';
|
|
13
11
|
import pathe from 'pathe';
|
|
12
|
+
import { generateCode, parseModule } from 'magicast';
|
|
13
|
+
import { JSON_SCHEMA } from 'js-yaml';
|
|
14
14
|
|
|
15
15
|
const NAMESPACE = "valaxy:devtools";
|
|
16
16
|
|
|
@@ -18,6 +18,166 @@ const DIR_DIST = typeof __dirname !== "undefined" ? __dirname : dirname(fileURLT
|
|
|
18
18
|
const DEVTOOLS_CLIENT_FOLDER = resolve(DIR_DIST, "../dist/client");
|
|
19
19
|
const DIR_CLIENT = DEVTOOLS_CLIENT_FOLDER;
|
|
20
20
|
|
|
21
|
+
const DANGEROUS_FIELD_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
22
|
+
function validateFieldPath(fieldPath) {
|
|
23
|
+
const parts = fieldPath.split(".");
|
|
24
|
+
return parts.every((part) => part.length > 0 && !DANGEROUS_FIELD_KEYS.has(part));
|
|
25
|
+
}
|
|
26
|
+
function assertInsideRoot(root, targetFile) {
|
|
27
|
+
const relative = pathe.relative(root, targetFile);
|
|
28
|
+
if (relative.startsWith("..") || pathe.isAbsolute(relative))
|
|
29
|
+
throw new Error(`Config path is outside user root: ${targetFile}`);
|
|
30
|
+
}
|
|
31
|
+
function setNestedValue(obj, path, value) {
|
|
32
|
+
const parts = path.split(".");
|
|
33
|
+
let current = obj;
|
|
34
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
35
|
+
const part = parts[i];
|
|
36
|
+
if (current[part] == null || typeof current[part] !== "object") {
|
|
37
|
+
current[part] = {};
|
|
38
|
+
}
|
|
39
|
+
current = current[part];
|
|
40
|
+
}
|
|
41
|
+
current[parts.at(-1)] = value;
|
|
42
|
+
}
|
|
43
|
+
async function parseConfigFile(filePath) {
|
|
44
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
45
|
+
const mod = parseModule(content);
|
|
46
|
+
return mod;
|
|
47
|
+
}
|
|
48
|
+
function serializeProxy(value) {
|
|
49
|
+
try {
|
|
50
|
+
if (value == null)
|
|
51
|
+
return value;
|
|
52
|
+
if (typeof value !== "object")
|
|
53
|
+
return value;
|
|
54
|
+
if (value.$type === "function-call") {
|
|
55
|
+
const callee = value.$callee ?? "";
|
|
56
|
+
const args = value.$args ?? [];
|
|
57
|
+
const serializedArgs = args.map(
|
|
58
|
+
(a) => typeof a === "string" ? `'${a}'` : JSON.stringify(a)
|
|
59
|
+
).join(", ");
|
|
60
|
+
return `${callee}(${serializedArgs})`;
|
|
61
|
+
}
|
|
62
|
+
if (value.$type === "identifier") {
|
|
63
|
+
return String(value.$id ?? value);
|
|
64
|
+
}
|
|
65
|
+
if (value.$type && value.$type !== "object") {
|
|
66
|
+
return `<${value.$type}>`;
|
|
67
|
+
}
|
|
68
|
+
if (Array.isArray(value)) {
|
|
69
|
+
return value.map((item) => {
|
|
70
|
+
try {
|
|
71
|
+
return serializeProxy(item);
|
|
72
|
+
} catch {
|
|
73
|
+
return "<unsupported>";
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
const result = {};
|
|
78
|
+
for (const key of Object.keys(value)) {
|
|
79
|
+
if (key.startsWith("$"))
|
|
80
|
+
continue;
|
|
81
|
+
try {
|
|
82
|
+
result[key] = serializeProxy(value[key]);
|
|
83
|
+
} catch {
|
|
84
|
+
result[key] = "<unsupported expression>";
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return result;
|
|
88
|
+
} catch {
|
|
89
|
+
return "<unsupported expression>";
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async function readConfigs(userRoot) {
|
|
93
|
+
const siteConfigPath = pathe.resolve(userRoot, "site.config.ts");
|
|
94
|
+
const valaxyConfigPath = pathe.resolve(userRoot, "valaxy.config.ts");
|
|
95
|
+
let siteConfig = {};
|
|
96
|
+
let valaxyConfig = {};
|
|
97
|
+
let themeConfig = {};
|
|
98
|
+
const siteConfigExists = await fs.pathExists(siteConfigPath);
|
|
99
|
+
const valaxyConfigExists = await fs.pathExists(valaxyConfigPath);
|
|
100
|
+
if (siteConfigExists) {
|
|
101
|
+
try {
|
|
102
|
+
const mod = await parseConfigFile(siteConfigPath);
|
|
103
|
+
const defaultExport = mod.exports.default;
|
|
104
|
+
if (defaultExport && typeof defaultExport === "object") {
|
|
105
|
+
siteConfig = serializeProxy(defaultExport.$args?.[0] ?? defaultExport);
|
|
106
|
+
}
|
|
107
|
+
} catch (e) {
|
|
108
|
+
console.error("[devtools] Failed to parse site.config.ts:", e);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (valaxyConfigExists) {
|
|
112
|
+
try {
|
|
113
|
+
const mod = await parseConfigFile(valaxyConfigPath);
|
|
114
|
+
const defaultExport = mod.exports.default;
|
|
115
|
+
if (defaultExport && typeof defaultExport === "object") {
|
|
116
|
+
const configObj = defaultExport.$args?.[0] ?? defaultExport;
|
|
117
|
+
const raw = serializeProxy(configObj);
|
|
118
|
+
if (raw.themeConfig) {
|
|
119
|
+
themeConfig = raw.themeConfig;
|
|
120
|
+
delete raw.themeConfig;
|
|
121
|
+
}
|
|
122
|
+
if (raw.siteConfig) {
|
|
123
|
+
delete raw.siteConfig;
|
|
124
|
+
}
|
|
125
|
+
valaxyConfig = raw;
|
|
126
|
+
}
|
|
127
|
+
} catch (e) {
|
|
128
|
+
console.error("[devtools] Failed to parse valaxy.config.ts:", e);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
siteConfig,
|
|
133
|
+
valaxyConfig,
|
|
134
|
+
themeConfig,
|
|
135
|
+
siteConfigExists,
|
|
136
|
+
valaxyConfigExists,
|
|
137
|
+
siteConfigPath,
|
|
138
|
+
valaxyConfigPath
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
const SITE_CONFIG_TEMPLATE = `import { defineSiteConfig } from 'valaxy'
|
|
142
|
+
|
|
143
|
+
export default defineSiteConfig({
|
|
144
|
+
})
|
|
145
|
+
`;
|
|
146
|
+
const VALAXY_CONFIG_TEMPLATE = `import { defineValaxyConfig } from 'valaxy'
|
|
147
|
+
|
|
148
|
+
export default defineValaxyConfig({
|
|
149
|
+
})
|
|
150
|
+
`;
|
|
151
|
+
async function writeConfigField(userRoot, configType, fieldPath, value) {
|
|
152
|
+
if (!validateFieldPath(fieldPath)) {
|
|
153
|
+
throw new Error(`Invalid field path: ${fieldPath}`);
|
|
154
|
+
}
|
|
155
|
+
const targetFile = configType === "site" ? pathe.resolve(userRoot, "site.config.ts") : pathe.resolve(userRoot, "valaxy.config.ts");
|
|
156
|
+
const template = configType === "site" ? SITE_CONFIG_TEMPLATE : VALAXY_CONFIG_TEMPLATE;
|
|
157
|
+
assertInsideRoot(userRoot, targetFile);
|
|
158
|
+
if (!await fs.pathExists(targetFile)) {
|
|
159
|
+
await fs.writeFile(targetFile, template, "utf-8");
|
|
160
|
+
}
|
|
161
|
+
const mod = await parseConfigFile(targetFile);
|
|
162
|
+
const defaultExport = mod.exports.default;
|
|
163
|
+
if (!defaultExport) {
|
|
164
|
+
throw new Error(`No default export found in ${targetFile}`);
|
|
165
|
+
}
|
|
166
|
+
const configObj = defaultExport.$args?.[0] ?? defaultExport;
|
|
167
|
+
if (configType === "theme") {
|
|
168
|
+
if (!configObj.themeConfig || typeof configObj.themeConfig !== "object") {
|
|
169
|
+
configObj.themeConfig = {};
|
|
170
|
+
}
|
|
171
|
+
setNestedValue(configObj.themeConfig, fieldPath, value);
|
|
172
|
+
} else if (configType === "valaxy") {
|
|
173
|
+
setNestedValue(configObj, fieldPath, value);
|
|
174
|
+
} else {
|
|
175
|
+
setNestedValue(configObj, fieldPath, value);
|
|
176
|
+
}
|
|
177
|
+
const { code } = generateCode(mod);
|
|
178
|
+
await fs.writeFile(targetFile, code, "utf-8");
|
|
179
|
+
}
|
|
180
|
+
|
|
21
181
|
async function migration(path, frontmatter) {
|
|
22
182
|
if (fs.existsSync(path)) {
|
|
23
183
|
const rawMd = await fs.readFile(path, "utf-8");
|
|
@@ -37,51 +197,6 @@ async function migration(path, frontmatter) {
|
|
|
37
197
|
}
|
|
38
198
|
}
|
|
39
199
|
|
|
40
|
-
const prefix = "/valaxy-devtools-api";
|
|
41
|
-
const apis = [
|
|
42
|
-
{
|
|
43
|
-
route: "/frontmatter",
|
|
44
|
-
fn: async (req, res) => {
|
|
45
|
-
if (req.method === "POST") {
|
|
46
|
-
const { pageData, frontmatter: newFm } = await req.body;
|
|
47
|
-
const path = pageData.path;
|
|
48
|
-
if (fs.existsSync(path)) {
|
|
49
|
-
const rawMd = await fs.readFile(path, "utf-8");
|
|
50
|
-
const matterFile = matter(rawMd);
|
|
51
|
-
matterFile.data = newFm;
|
|
52
|
-
const newMd = matter.stringify(matterFile.content, matterFile.data);
|
|
53
|
-
await fs.writeFile(path, newMd);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
route: "/migration",
|
|
60
|
-
fn: async (req, res) => {
|
|
61
|
-
if (req.method === "POST") {
|
|
62
|
-
const { pageData, frontmatter } = await req.body;
|
|
63
|
-
const worker = [];
|
|
64
|
-
for (const item of pageData) {
|
|
65
|
-
const path = item;
|
|
66
|
-
worker.push(migration(path, frontmatter));
|
|
67
|
-
}
|
|
68
|
-
Promise.all(worker).then(() => {
|
|
69
|
-
res.end("ok");
|
|
70
|
-
}).catch((_) => {
|
|
71
|
-
res.end("migration error");
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
];
|
|
77
|
-
function registerApi(server, _viteConfig) {
|
|
78
|
-
const app = server.middlewares;
|
|
79
|
-
app.use(bodyParser.json());
|
|
80
|
-
apis.forEach(({ route, fn }) => {
|
|
81
|
-
app.use(prefix + route, fn);
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
200
|
function ensurePrefix(prefix, str) {
|
|
86
201
|
if (!str.startsWith(prefix))
|
|
87
202
|
return prefix + str;
|
|
@@ -164,6 +279,22 @@ function getFunctions(server, devtoolsOptions) {
|
|
|
164
279
|
frontmatter: data
|
|
165
280
|
};
|
|
166
281
|
},
|
|
282
|
+
async updateFrontmatter(req) {
|
|
283
|
+
const { filePath, frontmatter: newFm } = req;
|
|
284
|
+
const pagesDir = pathe.resolve(userRoot, "pages");
|
|
285
|
+
const resolved = pathe.resolve(filePath);
|
|
286
|
+
const rel = pathe.relative(pagesDir, resolved);
|
|
287
|
+
if (rel.startsWith("..") || pathe.isAbsolute(rel) || !resolved.endsWith(".md"))
|
|
288
|
+
throw new Error("Invalid file path: must be within pages directory and end with .md");
|
|
289
|
+
if (!fs.existsSync(resolved))
|
|
290
|
+
throw new Error(`File not found: ${resolved}`);
|
|
291
|
+
const rawMd = await fs.readFile(resolved, "utf-8");
|
|
292
|
+
const matterFile = matter(rawMd);
|
|
293
|
+
matterFile.data = newFm;
|
|
294
|
+
const newMd = matter.stringify(matterFile.content, matterFile.data);
|
|
295
|
+
await fs.writeFile(resolved, newMd);
|
|
296
|
+
return { success: true };
|
|
297
|
+
},
|
|
167
298
|
async getCollectionList() {
|
|
168
299
|
const collectionsRoot = pathe.resolve(userRoot, "pages", "collections");
|
|
169
300
|
if (!await fs.pathExists(collectionsRoot))
|
|
@@ -245,9 +376,232 @@ function getFunctions(server, devtoolsOptions) {
|
|
|
245
376
|
});
|
|
246
377
|
}
|
|
247
378
|
return result;
|
|
379
|
+
},
|
|
380
|
+
async batchUpdateFrontmatter(filePaths, operations) {
|
|
381
|
+
const pagesDir = pathe.resolve(userRoot, "pages");
|
|
382
|
+
const result = {
|
|
383
|
+
total: filePaths.length,
|
|
384
|
+
updated: 0,
|
|
385
|
+
errors: []
|
|
386
|
+
};
|
|
387
|
+
for (const filePath of filePaths) {
|
|
388
|
+
try {
|
|
389
|
+
const resolved = pathe.resolve(filePath);
|
|
390
|
+
const rel = pathe.relative(pagesDir, resolved);
|
|
391
|
+
if (rel.startsWith("..") || pathe.isAbsolute(rel) || !resolved.endsWith(".md")) {
|
|
392
|
+
result.errors.push({ filePath, error: "Invalid file path: must be within pages directory and end with .md" });
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
if (!await fs.pathExists(resolved)) {
|
|
396
|
+
result.errors.push({ filePath, error: "File not found" });
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
const rawMd = await fs.readFile(resolved, "utf-8");
|
|
400
|
+
const matterFile = matter(rawMd);
|
|
401
|
+
let modified = false;
|
|
402
|
+
for (const op of operations) {
|
|
403
|
+
if (DANGEROUS_FIELD_KEYS.has(op.key) || op.newKey && DANGEROUS_FIELD_KEYS.has(op.newKey))
|
|
404
|
+
continue;
|
|
405
|
+
switch (op.type) {
|
|
406
|
+
case "set":
|
|
407
|
+
matterFile.data[op.key] = op.value;
|
|
408
|
+
modified = true;
|
|
409
|
+
break;
|
|
410
|
+
case "delete":
|
|
411
|
+
if (op.key in matterFile.data) {
|
|
412
|
+
delete matterFile.data[op.key];
|
|
413
|
+
modified = true;
|
|
414
|
+
}
|
|
415
|
+
break;
|
|
416
|
+
case "rename":
|
|
417
|
+
if (op.key in matterFile.data && op.newKey) {
|
|
418
|
+
matterFile.data[op.newKey] = matterFile.data[op.key];
|
|
419
|
+
delete matterFile.data[op.key];
|
|
420
|
+
modified = true;
|
|
421
|
+
}
|
|
422
|
+
break;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
if (modified) {
|
|
426
|
+
const newMd = matter.stringify(matterFile.content, matterFile.data);
|
|
427
|
+
await fs.writeFile(resolved, newMd);
|
|
428
|
+
result.updated++;
|
|
429
|
+
}
|
|
430
|
+
} catch (e) {
|
|
431
|
+
result.errors.push({ filePath, error: e.message || String(e) });
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
return result;
|
|
435
|
+
},
|
|
436
|
+
async getConfig() {
|
|
437
|
+
return readConfigs(userRoot);
|
|
438
|
+
},
|
|
439
|
+
async updateConfigField(configType, fieldPath, value) {
|
|
440
|
+
try {
|
|
441
|
+
await writeConfigField(userRoot, configType, fieldPath, value);
|
|
442
|
+
return { success: true };
|
|
443
|
+
} catch (e) {
|
|
444
|
+
return { success: false, error: e.message || String(e) };
|
|
445
|
+
}
|
|
446
|
+
},
|
|
447
|
+
async runMigration(filePaths, frontmatter) {
|
|
448
|
+
const workers = filePaths.map((path) => migration(path, frontmatter));
|
|
449
|
+
await Promise.all(workers);
|
|
450
|
+
return { success: true };
|
|
451
|
+
},
|
|
452
|
+
async createPost(options) {
|
|
453
|
+
try {
|
|
454
|
+
const { title, path: customPath, tags, categories } = options;
|
|
455
|
+
const postsDir = pathe.resolve(userRoot, "pages", "posts");
|
|
456
|
+
let filePath;
|
|
457
|
+
if (customPath) {
|
|
458
|
+
const normalized = customPath.endsWith(".md") ? customPath : `${customPath}.md`;
|
|
459
|
+
filePath = pathe.resolve(postsDir, normalized);
|
|
460
|
+
const rel = pathe.relative(postsDir, filePath);
|
|
461
|
+
if (rel.startsWith("..") || pathe.isAbsolute(rel))
|
|
462
|
+
return { success: false, error: "Invalid path: must be within posts directory" };
|
|
463
|
+
} else {
|
|
464
|
+
const slug = title.toLowerCase().replace(/[\s_]+/g, "-").replace(/[^\w\u4E00-\u9FFF-]/g, "").replace(/-+/g, "-").replace(/^-|-$/g, "") || `post-${Date.now()}`;
|
|
465
|
+
filePath = pathe.resolve(postsDir, `${slug}.md`);
|
|
466
|
+
}
|
|
467
|
+
await fs.ensureDir(pathe.dirname(filePath));
|
|
468
|
+
if (await fs.pathExists(filePath)) {
|
|
469
|
+
const ext = pathe.extname(filePath);
|
|
470
|
+
const base = filePath.slice(0, -ext.length);
|
|
471
|
+
let counter = 1;
|
|
472
|
+
while (await fs.pathExists(filePath)) {
|
|
473
|
+
filePath = `${base}-${counter}${ext}`;
|
|
474
|
+
counter++;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
const now = dayjs().format("YYYY-MM-DD HH:mm:ss");
|
|
478
|
+
const frontmatter = {
|
|
479
|
+
title,
|
|
480
|
+
date: now,
|
|
481
|
+
updated: now,
|
|
482
|
+
draft: true
|
|
483
|
+
};
|
|
484
|
+
if (tags && tags.length > 0)
|
|
485
|
+
frontmatter.tags = tags;
|
|
486
|
+
if (categories && categories.length > 0)
|
|
487
|
+
frontmatter.categories = categories;
|
|
488
|
+
const content = matter.stringify("\n", frontmatter);
|
|
489
|
+
await fs.writeFile(filePath, content, "utf-8");
|
|
490
|
+
return { success: true, filePath };
|
|
491
|
+
} catch (e) {
|
|
492
|
+
return { success: false, error: e.message || String(e) };
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
const prefix = "/valaxy-devtools-api";
|
|
499
|
+
function isExactRouteRequest(req) {
|
|
500
|
+
const pathname = new URL(req.url || "/", "http://localhost").pathname;
|
|
501
|
+
return pathname === "/" || pathname === "";
|
|
502
|
+
}
|
|
503
|
+
function sendJson(res, data) {
|
|
504
|
+
res.setHeader("Content-Type", "application/json");
|
|
505
|
+
res.end(JSON.stringify(data));
|
|
506
|
+
}
|
|
507
|
+
function sendError(res, statusCode, error) {
|
|
508
|
+
res.statusCode = statusCode;
|
|
509
|
+
sendJson(res, { error });
|
|
510
|
+
}
|
|
511
|
+
function createRoute(handlers) {
|
|
512
|
+
return async (req, res, next) => {
|
|
513
|
+
if (!isExactRouteRequest(req)) {
|
|
514
|
+
next?.();
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
const method = req.method?.toUpperCase();
|
|
518
|
+
const handler = handlers[method];
|
|
519
|
+
if (!handler) {
|
|
520
|
+
next?.();
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
try {
|
|
524
|
+
const data = await handler(req, res);
|
|
525
|
+
if (!res.writableEnded)
|
|
526
|
+
sendJson(res, data);
|
|
527
|
+
} catch (e) {
|
|
528
|
+
sendError(res, 500, e?.message || String(e));
|
|
248
529
|
}
|
|
249
530
|
};
|
|
250
531
|
}
|
|
532
|
+
function registerApi(server, _viteConfig, options = {}) {
|
|
533
|
+
const app = server.middlewares;
|
|
534
|
+
app.use(bodyParser.json());
|
|
535
|
+
const functions = getFunctions(server, options);
|
|
536
|
+
app.use(`${prefix}/options`, createRoute({
|
|
537
|
+
GET: () => functions.getOptions()
|
|
538
|
+
}));
|
|
539
|
+
app.use(`${prefix}/posts`, createRoute({
|
|
540
|
+
GET: () => functions.getPostList(),
|
|
541
|
+
POST: async (req, res) => {
|
|
542
|
+
const body = req.body;
|
|
543
|
+
if (!body?.title) {
|
|
544
|
+
sendError(res, 400, "Missing title");
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
return functions.createPost(body);
|
|
548
|
+
}
|
|
549
|
+
}));
|
|
550
|
+
app.use(`${prefix}/pages`, createRoute({
|
|
551
|
+
GET: async (req, res) => {
|
|
552
|
+
const url = new URL(req.url || "", "http://localhost");
|
|
553
|
+
const pagePath = url.searchParams.get("path");
|
|
554
|
+
if (!pagePath) {
|
|
555
|
+
sendError(res, 400, 'Missing "path" query parameter');
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
return functions.getPageData(pagePath);
|
|
559
|
+
}
|
|
560
|
+
}));
|
|
561
|
+
app.use(`${prefix}/collections`, createRoute({
|
|
562
|
+
GET: () => functions.getCollectionList()
|
|
563
|
+
}));
|
|
564
|
+
app.use(`${prefix}/frontmatter/batch`, createRoute({
|
|
565
|
+
POST: async (req, res) => {
|
|
566
|
+
const body = req.body;
|
|
567
|
+
const { filePaths, operations } = body;
|
|
568
|
+
if (!Array.isArray(filePaths) || !Array.isArray(operations)) {
|
|
569
|
+
sendError(res, 400, "Invalid request: filePaths and operations must be arrays");
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
for (const op of operations) {
|
|
573
|
+
if (!op || typeof op.type !== "string" || typeof op.key !== "string") {
|
|
574
|
+
sendError(res, 400, "Invalid operation: each operation must have type and key");
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
return functions.batchUpdateFrontmatter(filePaths, operations);
|
|
579
|
+
}
|
|
580
|
+
}));
|
|
581
|
+
app.use(`${prefix}/frontmatter`, createRoute({
|
|
582
|
+
POST: async (req) => {
|
|
583
|
+
return functions.updateFrontmatter(req.body);
|
|
584
|
+
}
|
|
585
|
+
}));
|
|
586
|
+
app.use(`${prefix}/migration`, createRoute({
|
|
587
|
+
POST: async (req) => {
|
|
588
|
+
const { filePaths, frontmatter } = req.body;
|
|
589
|
+
return functions.runMigration(filePaths, frontmatter);
|
|
590
|
+
}
|
|
591
|
+
}));
|
|
592
|
+
app.use(`${prefix}/config`, createRoute({
|
|
593
|
+
GET: () => functions.getConfig(),
|
|
594
|
+
PUT: async (req, res) => {
|
|
595
|
+
const body = req.body;
|
|
596
|
+
const { configType, fieldPath, value } = body;
|
|
597
|
+
if (!configType || !fieldPath) {
|
|
598
|
+
sendError(res, 400, "Missing configType or fieldPath");
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
return functions.updateConfigField(configType, fieldPath, value);
|
|
602
|
+
}
|
|
603
|
+
}));
|
|
604
|
+
}
|
|
251
605
|
|
|
252
606
|
function ValaxyDevtools(options = {}) {
|
|
253
607
|
let config;
|
|
@@ -255,8 +609,6 @@ function ValaxyDevtools(options = {}) {
|
|
|
255
609
|
function configureServer(server) {
|
|
256
610
|
const _print = server.printUrls;
|
|
257
611
|
const base = (options.base ?? server.config.base) || "/";
|
|
258
|
-
const functions = getFunctions(server, options);
|
|
259
|
-
createRPCServer(NAMESPACE, server.ws, functions);
|
|
260
612
|
const devtoolsUrl = `${base}__valaxy_devtools__/`;
|
|
261
613
|
if (!isDevDevtools) {
|
|
262
614
|
server.middlewares.use(devtoolsUrl, sirv(DIR_CLIENT, {
|
|
@@ -279,7 +631,7 @@ function ValaxyDevtools(options = {}) {
|
|
|
279
631
|
const colorUrl = (url2) => colors.green(url2.replace(/:(\d+)\//, (_, port) => `:${colors.bold(port)}/`));
|
|
280
632
|
console.log(` ${colors.green("\u279C")} ${colors.bold("Inspect")}: ${colorUrl(`${host}${base}__inspect/`)}`);
|
|
281
633
|
};
|
|
282
|
-
registerApi(server);
|
|
634
|
+
registerApi(server, config, options);
|
|
283
635
|
}
|
|
284
636
|
const plugin = {
|
|
285
637
|
name: NAMESPACE,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@valaxyjs/devtools",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.28.
|
|
4
|
+
"version": "0.28.1",
|
|
5
5
|
"repository": {
|
|
6
6
|
"url": "https://github.com/YunYouJun/valaxy"
|
|
7
7
|
},
|
|
@@ -26,25 +26,25 @@
|
|
|
26
26
|
"axios": "^1.13.6",
|
|
27
27
|
"body-parser": "^2.2.2",
|
|
28
28
|
"cors": "^2.8.6",
|
|
29
|
+
"fs-extra": "^11.3.4",
|
|
29
30
|
"http-proxy-middleware": "^3.0.5",
|
|
30
31
|
"js-yaml": "^4.1.1",
|
|
32
|
+
"magicast": "^0.5.2",
|
|
31
33
|
"pathe": "^2.0.3",
|
|
32
|
-
"sirv": "^3.0.2"
|
|
33
|
-
"vite-dev-rpc": "^1.1.0"
|
|
34
|
+
"sirv": "^3.0.2"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
36
37
|
"@iconify-json/ri": "^1.2.10",
|
|
37
38
|
"@intlify/unplugin-vue-i18n": "^11.0.7",
|
|
38
|
-
"@primevue/themes": "^4.5.4",
|
|
39
39
|
"@types/body-parser": "^1.19.6",
|
|
40
40
|
"@types/splitpanes": "^2.2.6",
|
|
41
41
|
"@types/wicg-file-system-access": "^2023.10.7",
|
|
42
42
|
"gray-matter": "^4.0.3",
|
|
43
|
-
"
|
|
43
|
+
"reka-ui": "^2.9.2",
|
|
44
44
|
"splitpanes": "^4.0.4",
|
|
45
45
|
"typescript": "^5.9.3",
|
|
46
46
|
"unbuild": "^3.6.1",
|
|
47
|
-
"vite": "^8.0.
|
|
47
|
+
"vite": "^8.0.2",
|
|
48
48
|
"vue-i18n": "^11.3.0"
|
|
49
49
|
},
|
|
50
50
|
"scripts": {
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"build": "rimraf dist && pnpm run build:client && npm run build:node",
|
|
53
53
|
"build:client": "vite build src/client",
|
|
54
54
|
"build:node": "unbuild",
|
|
55
|
-
"dev": "
|
|
55
|
+
"dev": "pnpm run stub && pnpm run dev:client",
|
|
56
56
|
"dev:client": "vite dev src/client --port 5001",
|
|
57
57
|
"watch:client": "vite build src/client --watch",
|
|
58
58
|
"stub": "unbuild --stub",
|
package/rpc.d.ts
CHANGED
|
@@ -1,5 +1,56 @@
|
|
|
1
1
|
import type { ClientCollectionData, ClientOptions, ClientPageData, ClientPostList } from './client/types'
|
|
2
2
|
|
|
3
|
+
export interface BatchFrontmatterOperation {
|
|
4
|
+
/**
|
|
5
|
+
* 操作类型
|
|
6
|
+
* - set: 设置/更新字段值
|
|
7
|
+
* - delete: 删除字段
|
|
8
|
+
* - rename: 重命名字段(oldKey -> newKey)
|
|
9
|
+
*/
|
|
10
|
+
type: 'set' | 'delete' | 'rename'
|
|
11
|
+
key: string
|
|
12
|
+
value?: any
|
|
13
|
+
newKey?: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface BatchUpdateResult {
|
|
17
|
+
total: number
|
|
18
|
+
updated: number
|
|
19
|
+
errors: { filePath: string, error: string }[]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface ConfigData {
|
|
23
|
+
siteConfig: Record<string, any>
|
|
24
|
+
valaxyConfig: Record<string, any>
|
|
25
|
+
themeConfig: Record<string, any>
|
|
26
|
+
siteConfigExists: boolean
|
|
27
|
+
valaxyConfigExists: boolean
|
|
28
|
+
siteConfigPath: string
|
|
29
|
+
valaxyConfigPath: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface ConfigUpdateRequest {
|
|
33
|
+
configType: 'site' | 'valaxy' | 'theme'
|
|
34
|
+
fieldPath: string
|
|
35
|
+
value: any
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface CreatePostOptions {
|
|
39
|
+
/** 文章标题 */
|
|
40
|
+
title: string
|
|
41
|
+
/** 文件路径(相对于 pages/posts/),如 'my-post.md' 或 'sub/my-post.md' */
|
|
42
|
+
path?: string
|
|
43
|
+
/** 标签 */
|
|
44
|
+
tags?: string[]
|
|
45
|
+
/** 分类 */
|
|
46
|
+
categories?: string[]
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface UpdateFrontmatterRequest {
|
|
50
|
+
filePath: string
|
|
51
|
+
frontmatter: Record<string, any>
|
|
52
|
+
}
|
|
53
|
+
|
|
3
54
|
export interface ServerFunctions {
|
|
4
55
|
// add: (a: number, b: number) => number
|
|
5
56
|
/**
|
|
@@ -18,6 +69,30 @@ export interface ServerFunctions {
|
|
|
18
69
|
* 获取合集列表
|
|
19
70
|
*/
|
|
20
71
|
getCollectionList: () => Promise<ClientCollectionData[]>
|
|
72
|
+
/**
|
|
73
|
+
* 更新单个页面的 frontmatter(整体覆盖)
|
|
74
|
+
*/
|
|
75
|
+
updateFrontmatter: (req: UpdateFrontmatterRequest) => Promise<{ success: boolean }>
|
|
76
|
+
/**
|
|
77
|
+
* 批量修改文章的 frontmatter
|
|
78
|
+
*/
|
|
79
|
+
batchUpdateFrontmatter: (filePaths: string[], operations: BatchFrontmatterOperation[]) => Promise<BatchUpdateResult>
|
|
80
|
+
/**
|
|
81
|
+
* 获取配置数据
|
|
82
|
+
*/
|
|
83
|
+
getConfig: () => Promise<ConfigData>
|
|
84
|
+
/**
|
|
85
|
+
* 更新配置字段
|
|
86
|
+
*/
|
|
87
|
+
updateConfigField: (configType: 'site' | 'valaxy' | 'theme', fieldPath: string, value: any) => Promise<{ success: boolean, error?: string }>
|
|
88
|
+
/**
|
|
89
|
+
* 运行 frontmatter 迁移
|
|
90
|
+
*/
|
|
91
|
+
runMigration: (filePaths: string[], frontmatter: Record<string, any>) => Promise<{ success: boolean }>
|
|
92
|
+
/**
|
|
93
|
+
* 创建新文章
|
|
94
|
+
*/
|
|
95
|
+
createPost: (options: CreatePostOptions) => Promise<{ success: boolean, filePath?: string, error?: string }>
|
|
21
96
|
}
|
|
22
97
|
|
|
23
98
|
export interface ClientFunctions {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{C as e,d as t}from"./vue.runtime.esm-bundler-D01s-oMJ.js";import{t as n}from"./index-B12droL0.js";var r={};function i(n,r){return e(),t(`div`,null,` About `)}var a=n(r,[[`render`,i]]);export{a as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{B as e,C as t,E as n,G as r,M as i,U as a,_ as o,b as s,d as c,g as l,h as u,j as d,k as f,l as p,s as m,w as h,x as g,y as _}from"./vue.runtime.esm-bundler-D01s-oMJ.js";var v={__name:`splitpanes`,props:{horizontal:{type:Boolean,default:!1},pushOtherPanes:{type:Boolean,default:!0},maximizePanes:{type:Boolean,default:!0},rtl:{type:Boolean,default:!1},firstSplitter:{type:Boolean,default:!1}},emits:[`ready`,`resize`,`resized`,`pane-click`,`pane-maximize`,`pane-add`,`pane-remove`,`splitter-click`,`splitter-dblclick`],setup(n,{emit:r}){let a=r,o=n,c=d(),u=e([]),v=m(()=>u.value.reduce((e,t)=>(e[~~t.id]=t)&&e,{})),y=m(()=>u.value.length),b=e(null),x=e(!1),S=e({mouseDown:!1,dragging:!1,activeSplitter:null,cursorOffset:0}),C=e({splitter:null,timeoutId:null}),w=m(()=>({[`splitpanes splitpanes--${o.horizontal?`horizontal`:`vertical`}`]:!0,"splitpanes--dragging":S.value.dragging})),T=()=>{document.addEventListener(`mousemove`,O,{passive:!1}),document.addEventListener(`mouseup`,k),`ontouchstart`in window&&(document.addEventListener(`touchmove`,O,{passive:!1}),document.addEventListener(`touchend`,k))},E=()=>{document.removeEventListener(`mousemove`,O,{passive:!1}),document.removeEventListener(`mouseup`,k),`ontouchstart`in window&&(document.removeEventListener(`touchmove`,O,{passive:!1}),document.removeEventListener(`touchend`,k))},D=(e,t)=>{let n=e.target.closest(`.splitpanes__splitter`);if(n){let{left:t,top:r}=n.getBoundingClientRect(),{clientX:i,clientY:a}=`ontouchstart`in window&&e.touches?e.touches[0]:e;S.value.cursorOffset=o.horizontal?a-r:i-t}T(),S.value.mouseDown=!0,S.value.activeSplitter=t},O=e=>{S.value.mouseDown&&(e.preventDefault(),S.value.dragging=!0,requestAnimationFrame(()=>{F(N(e)),$(`resize`,{event:e},!0)}))},k=e=>{S.value.dragging&&(window.getSelection().removeAllRanges(),$(`resized`,{event:e},!0)),S.value.mouseDown=!1,S.value.activeSplitter=null,setTimeout(()=>{S.value.dragging=!1,E()},100)},A=(e,t)=>{`ontouchstart`in window&&(e.preventDefault(),C.value.splitter===t?(clearTimeout(C.value.timeoutId),C.value.timeoutId=null,j(e,t),C.value.splitter=null):(C.value.splitter=t,C.value.timeoutId=setTimeout(()=>C.value.splitter=null,500))),S.value.dragging||$(`splitter-click`,{event:e,index:t},!0)},j=(e,t)=>{if($(`splitter-dblclick`,{event:e,index:t},!0),o.maximizePanes){let n=0;u.value=u.value.map((e,r)=>(e.size=r===t?e.max:e.min,r!==t&&(n+=e.min),e)),u.value[t].size-=n,$(`pane-maximize`,{event:e,index:t,pane:u.value[t]}),$(`resized`,{event:e,index:t},!0)}},M=(e,t)=>{$(`pane-click`,{event:e,index:v.value[t].index,pane:v.value[t]})},N=e=>{let t=b.value.getBoundingClientRect(),{clientX:n,clientY:r}=`ontouchstart`in window&&e.touches?e.touches[0]:e;return{x:n-(o.horizontal?0:S.value.cursorOffset)-t.left,y:r-(o.horizontal?S.value.cursorOffset:0)-t.top}},P=e=>{e=e[o.horizontal?`y`:`x`];let t=b.value[o.horizontal?`clientHeight`:`clientWidth`];return o.rtl&&!o.horizontal&&(e=t-e),e*100/t},F=e=>{let t=S.value.activeSplitter,n={prevPanesSize:L(t),nextPanesSize:R(t),prevReachedMinPanes:0,nextReachedMinPanes:0},r=0+(o.pushOtherPanes?0:n.prevPanesSize),i=100-(o.pushOtherPanes?0:n.nextPanesSize),a=Math.max(Math.min(P(e),i),r),s=[t,t+1],c=u.value[s[0]]||null,l=u.value[s[1]]||null,d=c.max<100&&a>=c.max+n.prevPanesSize,f=l.max<100&&a<=100-(l.max+R(t+1));if(d||f){d?(c.size=c.max,l.size=Math.max(100-c.max-n.prevPanesSize-n.nextPanesSize,0)):(c.size=Math.max(100-l.max-n.prevPanesSize-R(t+1),0),l.size=l.max);return}if(o.pushOtherPanes){let e=I(n,a);if(!e)return;({sums:n,panesToResize:s}=e),c=u.value[s[0]]||null,l=u.value[s[1]]||null}c!==null&&(c.size=Math.min(Math.max(a-n.prevPanesSize-n.prevReachedMinPanes,c.min),c.max)),l!==null&&(l.size=Math.min(Math.max(100-a-n.nextPanesSize-n.nextReachedMinPanes,l.min),l.max))},I=(e,t)=>{let n=S.value.activeSplitter,r=[n,n+1];return t<e.prevPanesSize+u.value[r[0]].min&&(r[0]=z(n).index,e.prevReachedMinPanes=0,r[0]<n&&u.value.forEach((t,i)=>{i>r[0]&&i<=n&&(t.size=t.min,e.prevReachedMinPanes+=t.min)}),e.prevPanesSize=L(r[0]),r[0]===void 0)?(e.prevReachedMinPanes=0,u.value[0].size=u.value[0].min,u.value.forEach((t,r)=>{r>0&&r<=n&&(t.size=t.min,e.prevReachedMinPanes+=t.min)}),u.value[r[1]].size=100-e.prevReachedMinPanes-u.value[0].min-e.prevPanesSize-e.nextPanesSize,null):t>100-e.nextPanesSize-u.value[r[1]].min&&(r[1]=B(n).index,e.nextReachedMinPanes=0,r[1]>n+1&&u.value.forEach((t,i)=>{i>n&&i<r[1]&&(t.size=t.min,e.nextReachedMinPanes+=t.min)}),e.nextPanesSize=R(r[1]-1),r[1]===void 0)?(e.nextReachedMinPanes=0,u.value.forEach((t,r)=>{r<y.value-1&&r>=n+1&&(t.size=t.min,e.nextReachedMinPanes+=t.min)}),u.value[r[0]].size=100-e.prevPanesSize-R(r[0]-1),null):{sums:e,panesToResize:r}},L=e=>u.value.reduce((t,n,r)=>t+(r<e?n.size:0),0),R=e=>u.value.reduce((t,n,r)=>t+(r>e+1?n.size:0),0),z=e=>[...u.value].reverse().find(t=>t.index<e&&t.size>t.min)||{},B=e=>u.value.find(t=>t.index>e+1&&t.size>t.min)||{},V=()=>{let e=Array.from(b.value?.children||[]);for(let t of e){let e=t.classList.contains(`splitpanes__pane`),n=t.classList.contains(`splitpanes__splitter`);!e&&!n&&(t.remove(),console.warn(`Splitpanes: Only <pane> elements are allowed at the root of <splitpanes>. One of your DOM nodes was removed.`))}},H=(e,t,n=!1)=>{let r=e-1,i=document.createElement(`div`);i.classList.add(`splitpanes__splitter`),n||(i.onmousedown=e=>D(e,r),typeof window<`u`&&`ontouchstart`in window&&(i.ontouchstart=e=>D(e,r)),i.onclick=e=>A(e,r+1)),i.ondblclick=e=>j(e,r+1),t.parentNode.insertBefore(i,t)},U=e=>{e.onmousedown=void 0,e.onclick=void 0,e.ondblclick=void 0,e.remove()},W=()=>{let e=Array.from(b.value?.children||[]);for(let t of e)t.className.includes(`splitpanes__splitter`)&&U(t);let t=0;for(let n of e)n.className.includes(`splitpanes__pane`)&&(!t&&o.firstSplitter?H(t,n,!0):t&&H(t,n),t++)},G=({uid:e,...t})=>{let n=v.value[e];for(let[e,r]of Object.entries(t))n[e]=r},K=e=>{let t=-1;Array.from(b.value?.children||[]).some(n=>(n.className.includes(`splitpanes__pane`)&&t++,n.isSameNode(e.el))),u.value.splice(t,0,{...e,index:t}),u.value.forEach((e,t)=>e.index=t),x.value&&_(()=>{W(),J({addedPane:u.value[t]}),$(`pane-add`,{pane:u.value[t]})})},q=e=>{let t=u.value.findIndex(t=>t.id===e);u.value[t].el=null;let n=u.value.splice(t,1)[0];u.value.forEach((e,t)=>e.index=t),_(()=>{W(),$(`pane-remove`,{pane:n}),J({removedPane:{...n}})})},J=(e={})=>{!e.addedPane&&!e.removedPane?X():u.value.some(e=>e.givenSize!==null||e.min||e.max<100)?Z(e):Y(),x.value&&$(`resized`)},Y=()=>{let e=100/y.value,t=0,n=[],r=[];for(let i of u.value)i.size=Math.max(Math.min(e,i.max),i.min),t-=i.size,i.size>=i.max&&n.push(i.id),i.size<=i.min&&r.push(i.id);t>.1&&Q(t,n,r)},X=()=>{let e=100,t=[],n=[],r=0;for(let i of u.value)e-=i.size,i.givenSize!==null&&r++,i.size>=i.max&&t.push(i.id),i.size<=i.min&&n.push(i.id);let i=100;if(e>.1){for(let t of u.value)t.givenSize===null&&(t.size=Math.max(Math.min(e/(y.value-r),t.max),t.min)),i-=t.size;i>.1&&Q(i,t,n)}},Z=({addedPane:e,removedPane:t}={})=>{let n=100/y.value,r=0,i=[],a=[];(e?.givenSize??null)!==null&&(n=(100-e.givenSize)/(y.value-1));for(let e of u.value)r-=e.size,e.size>=e.max&&i.push(e.id),e.size<=e.min&&a.push(e.id);if(!(Math.abs(r)<.1)){for(let t of u.value)e?.givenSize!==null&&e?.id===t.id||(t.size=Math.max(Math.min(n,t.max),t.min)),r-=t.size,t.size>=t.max&&i.push(t.id),t.size<=t.min&&a.push(t.id);r>.1&&Q(r,i,a)}},Q=(e,t,n)=>{let r;r=e>0?e/(y.value-t.length):e/(y.value-n.length),u.value.forEach((i,a)=>{if(e>0&&!t.includes(i.id)){let t=Math.max(Math.min(i.size+r,i.max),i.min),n=t-i.size;e-=n,i.size=t}else if(!n.includes(i.id)){let t=Math.max(Math.min(i.size+r,i.max),i.min),n=t-i.size;e-=n,i.size=t}}),Math.abs(e)>.1&&_(()=>{x.value&&console.warn(`Splitpanes: Could not resize panes correctly due to their constraints.`)})},$=(e,t=void 0,n=!1)=>{let r=t?.index??S.value.activeSplitter??null;a(e,{...t,...r!==null&&{index:r},...n&&r!==null&&{prevPane:u.value[r-(o.firstSplitter?1:0)],nextPane:u.value[r+(o.firstSplitter?0:1)]},panes:u.value.map(e=>({min:e.min,max:e.max,size:e.size}))})};i(()=>o.firstSplitter,()=>W()),g(()=>{V(),W(),J(),$(`ready`),x.value=!0}),s(()=>x.value=!1);let ee=()=>l(`div`,{ref:b,class:w.value},c.default?.call(c));return h(`panes`,u),h(`indexedPanes`,v),h(`horizontal`,m(()=>o.horizontal)),h(`requestUpdate`,G),h(`onPaneAdd`,K),h(`onPaneRemove`,q),h(`onPaneClick`,M),(e,n)=>(t(),p(f(ee)))}},y={__name:`pane`,props:{size:{type:[Number,String]},minSize:{type:[Number,String],default:0},maxSize:{type:[Number,String],default:100}},setup(l){let d=l,f=o(`requestUpdate`),p=o(`onPaneAdd`),h=o(`horizontal`),_=o(`onPaneRemove`),v=o(`onPaneClick`),y=u()?.uid,b=o(`indexedPanes`),x=m(()=>b.value[y]),S=e(null),C=m(()=>{let e=isNaN(d.size)||d.size===void 0?0:parseFloat(d.size);return Math.max(Math.min(e,T.value),w.value)}),w=m(()=>{let e=parseFloat(d.minSize);return isNaN(e)?0:e}),T=m(()=>{let e=parseFloat(d.maxSize);return isNaN(e)?100:e}),E=m(()=>`${h.value?`height`:`width`}: ${x.value?.size}%`);return i(()=>C.value,e=>f({uid:y,size:e})),i(()=>w.value,e=>f({uid:y,min:e})),i(()=>T.value,e=>f({uid:y,max:e})),g(()=>{p({id:y,el:S.value,min:w.value,max:T.value,givenSize:d.size===void 0?null:C.value,size:C.value})}),s(()=>_(y)),(e,i)=>(t(),c(`div`,{ref_key:`paneEl`,ref:S,class:`splitpanes__pane`,onClick:i[0]||=t=>a(v)(t,e._.uid),style:r(E.value)},[n(e.$slots,`default`)],4))}};e(!1),e({userRoot:``});var b=e({posts:[]}),x=e([]);e(``),e();export{y as i,b as n,v as r,x as t};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{C as e,d as t}from"./vue.runtime.esm-bundler-D01s-oMJ.js";import{t as n}from"./index-B12droL0.js";var r={};function i(n,r){return e(),t(`div`,null,` Categories `)}var a=n(r,[[`render`,i]]);export{a as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{B as e,C as t,K as n,N as r,T as i,U as a,W as o,a as s,c,d as l,f as u,l as d,m as f,p,u as m}from"./vue.runtime.esm-bundler-D01s-oMJ.js";import{i as h,r as g,t as _}from"./app-D2GYSWUd.js";var v={class:`h-full overflow-auto`},y=[`onClick`],b={class:`font-bold text-sm`},x={class:`text-xs op-50 mt-1`},S={key:0,class:`text-center op-50 py-8 text-sm`},C={key:0,class:`overflow-auto h-full p-4`},w={class:`flex items-start gap-4 mb-4`},T=[`src`],E={class:`text-lg font-bold`},D={class:`text-xs op-50 mt-1`},O={key:0,class:`text-sm op-70 mt-2`},k={class:`text-xs mt-2`},A={class:`font-bold text-sm mb-2 op-70`},j={class:`border rounded dark:border-gray-700`},M={class:`op-40 text-xs w-6 text-right`},N={class:`flex-1`},P=[`href`],F={key:1,class:`text-xs op-40 font-mono`},I={key:0,class:`text-center op-50 py-4 text-sm`},L={key:1,class:`flex items-center justify-center h-full op-40 text-sm`},R=f({__name:`collections`,setup(f){let R=e(null);function z(e){R.value=e}return(e,f)=>(t(),d(a(g),{class:`h-full`},{default:r(()=>[p(a(h),{"min-size":`20`,size:`30`},{default:r(()=>[c(`div`,v,[(t(!0),l(s,null,i(a(_),e=>(t(),l(`div`,{key:e.key,class:o([`cursor-pointer p-3 border-b border-gray-100 dark:border-gray-800 hover:bg-gray-50 dark:hover:bg-gray-800 transition`,{"bg-gray-100 dark:bg-gray-700":R.value?.key===e.key}]),onClick:t=>z(e)},[c(`div`,b,n(e.title||e.key),1),c(`div`,x,n(e.items.length)+` items `,1)],10,y))),128)),a(_).length?m(``,!0):(t(),l(`div`,S,` No collections found `))])]),_:1}),p(a(h),null,{default:r(()=>[R.value?(t(),l(`div`,C,[c(`div`,w,[R.value.cover?(t(),l(`img`,{key:0,src:R.value.cover,class:`max-h-30 rounded shadow`},null,8,T)):m(``,!0),c(`div`,null,[c(`h2`,E,n(R.value.title||R.value.key),1),c(`div`,D,` key: `+n(R.value.key),1),R.value.description?(t(),l(`div`,O,n(R.value.description),1)):m(``,!0),c(`div`,k,[c(`span`,{class:o([`px-1.5 py-0.5 rounded`,R.value.collapse?`bg-blue-100 text-blue-600 dark:bg-blue-900 dark:text-blue-300`:`bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-300`])},n(R.value.collapse?`collapsed`:`expanded`),3)])])]),c(`h3`,A,` Items (`+n(R.value.items.length)+`) `,1),c(`div`,j,[(t(!0),l(s,null,i(R.value.items,(e,r)=>(t(),l(`div`,{key:e.key||e.link,class:`flex items-center gap-2 px-3 py-2 text-sm border-b last:border-b-0 dark:border-gray-700`},[c(`span`,M,n(r+1),1),c(`span`,N,n(e.title),1),e.link?(t(),l(`a`,{key:0,href:e.link,target:`_blank`,rel:`noopener noreferrer`,class:`text-xs text-blue-500 font-mono inline-flex items-center gap-0.5`},[u(n(e.link)+` `,1),f[0]||=c(`span`,{class:`i-ri-external-link-line text-xs`},null,-1)],8,P)):(t(),l(`span`,F,n(e.key),1))]))),128)),R.value.items.length?m(``,!0):(t(),l(`div`,I,` No items `))])])):(t(),l(`div`,L,` Select a collection to view details `))]),_:1})]),_:1}))}});export{R as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
var e={button:{about:{t:0,b:{t:2,i:[{t:3}],s:`About`}},back:{t:0,b:{t:2,i:[{t:3}],s:`Back`}},go:{t:0,b:{t:2,i:[{t:3}],s:`GO`}},home:{t:0,b:{t:2,i:[{t:3}],s:`Home`}},toggle_dark:{t:0,b:{t:2,i:[{t:3}],s:`Toggle dark mode`}},toggle_langs:{t:0,b:{t:2,i:[{t:3}],s:`Change languages`}},save_frontmatter:{t:0,b:{t:2,i:[{t:3}],s:`Save Frontmatter`}}},pageData:{path:{t:0,b:{t:2,i:[{t:3}],s:`Path`}},filePath:{t:0,b:{t:2,i:[{t:3}],s:`File Path`}},routePath:{t:0,b:{t:2,i:[{t:3}],s:`Route Path`}}}};export{e as default};
|