vslides 1.0.23 → 1.0.25
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/README.md +67 -0
- package/dist/cli.js +597 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -329,6 +329,73 @@ rm .vslides.json
|
|
|
329
329
|
vslides init
|
|
330
330
|
```
|
|
331
331
|
|
|
332
|
+
## Library Commands
|
|
333
|
+
|
|
334
|
+
Share and reuse curated slide content across presentations.
|
|
335
|
+
|
|
336
|
+
| Command | Description |
|
|
337
|
+
|---------|-------------|
|
|
338
|
+
| `vslides library list` | List library items |
|
|
339
|
+
| `vslides library search <query>` | Search library |
|
|
340
|
+
| `vslides library publish` | Publish presentation to library |
|
|
341
|
+
| `vslides library import <id>` | Import library item |
|
|
342
|
+
| `vslides library status` | Check import status |
|
|
343
|
+
| `vslides library upgrade [id]` | Upgrade imports |
|
|
344
|
+
| `vslides library tags` | List taxonomy values |
|
|
345
|
+
| `vslides library info <id>` | Show item details |
|
|
346
|
+
|
|
347
|
+
### Publishing
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
# Interactive (prompts for metadata)
|
|
351
|
+
vslides library publish
|
|
352
|
+
|
|
353
|
+
# With flags
|
|
354
|
+
vslides library publish \
|
|
355
|
+
--name "ISR Deep Dive" \
|
|
356
|
+
--department "Engineering" \
|
|
357
|
+
--audience "Developers" \
|
|
358
|
+
--product "Next.js"
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
### Importing
|
|
362
|
+
|
|
363
|
+
```bash
|
|
364
|
+
# Import a library item
|
|
365
|
+
vslides library import <id>
|
|
366
|
+
|
|
367
|
+
# Creates:
|
|
368
|
+
# - ./library/<name>.md (the slide content)
|
|
369
|
+
# - .vslides-library.json (dependency tracking)
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
Add to your slides.md:
|
|
373
|
+
```markdown
|
|
374
|
+
---
|
|
375
|
+
src: ./library/<name>.md
|
|
376
|
+
---
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### Checking for Updates
|
|
380
|
+
|
|
381
|
+
```bash
|
|
382
|
+
# Show import status
|
|
383
|
+
vslides library status
|
|
384
|
+
|
|
385
|
+
# Upgrade specific item
|
|
386
|
+
vslides library upgrade <id>
|
|
387
|
+
|
|
388
|
+
# Upgrade all
|
|
389
|
+
vslides library upgrade --all
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
### Library Files
|
|
393
|
+
|
|
394
|
+
| File | Location | Purpose |
|
|
395
|
+
|------|----------|---------|
|
|
396
|
+
| `library/*.md` | Project directory | Imported library content |
|
|
397
|
+
| `.vslides-library.json` | Project directory | Dependency tracking |
|
|
398
|
+
|
|
332
399
|
## Slide Format
|
|
333
400
|
|
|
334
401
|
Slides use Markdown with YAML frontmatter:
|
package/dist/cli.js
CHANGED
|
@@ -3408,6 +3408,65 @@ async function searchUsers(cliAuthToken, query) {
|
|
|
3408
3408
|
}
|
|
3409
3409
|
});
|
|
3410
3410
|
}
|
|
3411
|
+
async function searchLibrary(params) {
|
|
3412
|
+
const searchParams = new URLSearchParams();
|
|
3413
|
+
if (params.query)
|
|
3414
|
+
searchParams.set("q", params.query);
|
|
3415
|
+
if (params.department)
|
|
3416
|
+
searchParams.set("department", String(params.department));
|
|
3417
|
+
if (params.audiences) {
|
|
3418
|
+
params.audiences.forEach((id) => searchParams.append("audience", String(id)));
|
|
3419
|
+
}
|
|
3420
|
+
if (params.products) {
|
|
3421
|
+
params.products.forEach((id) => searchParams.append("product", String(id)));
|
|
3422
|
+
}
|
|
3423
|
+
if (params.status)
|
|
3424
|
+
searchParams.set("status", params.status);
|
|
3425
|
+
if (params.limit)
|
|
3426
|
+
searchParams.set("limit", String(params.limit));
|
|
3427
|
+
if (params.offset)
|
|
3428
|
+
searchParams.set("offset", String(params.offset));
|
|
3429
|
+
const queryString = searchParams.toString();
|
|
3430
|
+
const url2 = queryString ? `/api/library?${queryString}` : "/api/library";
|
|
3431
|
+
return request(url2, { skipAuthCheck: true });
|
|
3432
|
+
}
|
|
3433
|
+
async function getLibraryItem(id) {
|
|
3434
|
+
return request(`/api/library/${id}`, { skipAuthCheck: true });
|
|
3435
|
+
}
|
|
3436
|
+
async function createLibraryItem(token, data) {
|
|
3437
|
+
return request("/api/library", {
|
|
3438
|
+
method: "POST",
|
|
3439
|
+
headers: {
|
|
3440
|
+
"Content-Type": "application/json",
|
|
3441
|
+
"X-CLI-Auth-Token": token
|
|
3442
|
+
},
|
|
3443
|
+
body: JSON.stringify(data)
|
|
3444
|
+
});
|
|
3445
|
+
}
|
|
3446
|
+
async function updateLibraryItem(token, id, data) {
|
|
3447
|
+
return request(`/api/library/${id}`, {
|
|
3448
|
+
method: "PUT",
|
|
3449
|
+
headers: {
|
|
3450
|
+
"Content-Type": "application/json",
|
|
3451
|
+
"X-CLI-Auth-Token": token
|
|
3452
|
+
},
|
|
3453
|
+
body: JSON.stringify(data)
|
|
3454
|
+
});
|
|
3455
|
+
}
|
|
3456
|
+
async function publishLibraryItem(token, id) {
|
|
3457
|
+
return request(`/api/library/${id}/publish`, {
|
|
3458
|
+
method: "POST",
|
|
3459
|
+
headers: {
|
|
3460
|
+
"X-CLI-Auth-Token": token
|
|
3461
|
+
}
|
|
3462
|
+
});
|
|
3463
|
+
}
|
|
3464
|
+
async function getLibraryImportBundle(id) {
|
|
3465
|
+
return request(`/api/library/${id}/import`, { skipAuthCheck: true });
|
|
3466
|
+
}
|
|
3467
|
+
async function getLibraryTags() {
|
|
3468
|
+
return request("/api/library/tags", { skipAuthCheck: true });
|
|
3469
|
+
}
|
|
3411
3470
|
|
|
3412
3471
|
// src/lib/config.ts
|
|
3413
3472
|
var import_node_fs2 = require("node:fs");
|
|
@@ -3549,6 +3608,9 @@ function success(message) {
|
|
|
3549
3608
|
function error(message) {
|
|
3550
3609
|
console.log(`ERROR: ${message}`);
|
|
3551
3610
|
}
|
|
3611
|
+
function warning(message) {
|
|
3612
|
+
console.log(`WARNING: ${message}`);
|
|
3613
|
+
}
|
|
3552
3614
|
function status(value) {
|
|
3553
3615
|
console.log(`STATUS: ${value}`);
|
|
3554
3616
|
}
|
|
@@ -4676,6 +4738,532 @@ async function find(query) {
|
|
|
4676
4738
|
}
|
|
4677
4739
|
}
|
|
4678
4740
|
|
|
4741
|
+
// src/commands/library-list.ts
|
|
4742
|
+
function isVerifiedRecently(dateStr) {
|
|
4743
|
+
const date = new Date(dateStr);
|
|
4744
|
+
const ninetyDaysAgo = /* @__PURE__ */ new Date();
|
|
4745
|
+
ninetyDaysAgo.setDate(ninetyDaysAgo.getDate() - 90);
|
|
4746
|
+
return date > ninetyDaysAgo;
|
|
4747
|
+
}
|
|
4748
|
+
function truncate(str, length) {
|
|
4749
|
+
if (str.length <= length)
|
|
4750
|
+
return str;
|
|
4751
|
+
return str.slice(0, length - 3) + "...";
|
|
4752
|
+
}
|
|
4753
|
+
async function libraryList(options) {
|
|
4754
|
+
const tagsResult = await getLibraryTags();
|
|
4755
|
+
if (!tagsResult.ok) {
|
|
4756
|
+
error("Failed to fetch taxonomy");
|
|
4757
|
+
process.exit(ExitCode.NetworkError);
|
|
4758
|
+
}
|
|
4759
|
+
const tags = tagsResult.data;
|
|
4760
|
+
const params = {
|
|
4761
|
+
limit: parseInt(options.limit || "20"),
|
|
4762
|
+
status: options.all ? void 0 : "published"
|
|
4763
|
+
};
|
|
4764
|
+
if (options.department) {
|
|
4765
|
+
const dept = tags.departments.find(
|
|
4766
|
+
(d) => d.name.toLowerCase() === options.department.toLowerCase()
|
|
4767
|
+
);
|
|
4768
|
+
if (dept)
|
|
4769
|
+
params.department = dept.id;
|
|
4770
|
+
}
|
|
4771
|
+
if (options.audience) {
|
|
4772
|
+
const aud = tags.audiences.find(
|
|
4773
|
+
(a) => a.name.toLowerCase() === options.audience.toLowerCase()
|
|
4774
|
+
);
|
|
4775
|
+
if (aud)
|
|
4776
|
+
params.audiences = [aud.id];
|
|
4777
|
+
}
|
|
4778
|
+
if (options.product) {
|
|
4779
|
+
const prod = tags.products.find(
|
|
4780
|
+
(p) => p.name.toLowerCase() === options.product.toLowerCase()
|
|
4781
|
+
);
|
|
4782
|
+
if (prod)
|
|
4783
|
+
params.products = [prod.id];
|
|
4784
|
+
}
|
|
4785
|
+
const result = await searchLibrary(params);
|
|
4786
|
+
if (!result.ok) {
|
|
4787
|
+
error("Failed to search library");
|
|
4788
|
+
process.exit(ExitCode.NetworkError);
|
|
4789
|
+
}
|
|
4790
|
+
const { items, total } = result.data;
|
|
4791
|
+
if (items.length === 0) {
|
|
4792
|
+
info("No library items found.");
|
|
4793
|
+
return;
|
|
4794
|
+
}
|
|
4795
|
+
info(`
|
|
4796
|
+
Library Items (${total} total)
|
|
4797
|
+
`);
|
|
4798
|
+
for (const item of items) {
|
|
4799
|
+
const verifiedIcon = item.lastVerified ? isVerifiedRecently(item.lastVerified) ? "[OK]" : "[STALE]" : "[?]";
|
|
4800
|
+
info(`${verifiedIcon} ${item.name} (${item.id})`);
|
|
4801
|
+
info(` ${item.department.name} | ${item.audiences.map((a) => a.name).join(", ")}`);
|
|
4802
|
+
if (item.description) {
|
|
4803
|
+
info(` ${truncate(item.description, 60)}`);
|
|
4804
|
+
}
|
|
4805
|
+
info(` v${item.version} | by ${item.owner}`);
|
|
4806
|
+
newline();
|
|
4807
|
+
}
|
|
4808
|
+
if (total > items.length) {
|
|
4809
|
+
info(`Showing ${items.length} of ${total}. Use --limit to see more.`);
|
|
4810
|
+
}
|
|
4811
|
+
}
|
|
4812
|
+
|
|
4813
|
+
// src/commands/library-search.ts
|
|
4814
|
+
async function librarySearch(query, options) {
|
|
4815
|
+
const result = await searchLibrary({
|
|
4816
|
+
query,
|
|
4817
|
+
limit: parseInt(options.limit || "20")
|
|
4818
|
+
});
|
|
4819
|
+
if (!result.ok) {
|
|
4820
|
+
error("Search failed");
|
|
4821
|
+
process.exit(ExitCode.NetworkError);
|
|
4822
|
+
}
|
|
4823
|
+
const { items, total } = result.data;
|
|
4824
|
+
if (items.length === 0) {
|
|
4825
|
+
info(`No results for "${query}"`);
|
|
4826
|
+
return;
|
|
4827
|
+
}
|
|
4828
|
+
info(`
|
|
4829
|
+
Search results for "${query}" (${total} found)
|
|
4830
|
+
`);
|
|
4831
|
+
for (const item of items) {
|
|
4832
|
+
info(`${item.name} (${item.id})`);
|
|
4833
|
+
info(` ${item.department.name} | ${item.audiences.map((a) => a.name).join(", ")}`);
|
|
4834
|
+
if (item.products.length > 0) {
|
|
4835
|
+
info(` Products: ${item.products.map((p) => p.name).join(", ")}`);
|
|
4836
|
+
}
|
|
4837
|
+
info(` v${item.version} | Import: vslides library import ${item.id}`);
|
|
4838
|
+
newline();
|
|
4839
|
+
}
|
|
4840
|
+
}
|
|
4841
|
+
|
|
4842
|
+
// src/commands/library-publish.ts
|
|
4843
|
+
var import_node_fs7 = require("node:fs");
|
|
4844
|
+
var import_node_path6 = require("node:path");
|
|
4845
|
+
var readline = __toESM(require("node:readline"));
|
|
4846
|
+
function prompt(question) {
|
|
4847
|
+
const rl = readline.createInterface({
|
|
4848
|
+
input: process.stdin,
|
|
4849
|
+
output: process.stdout
|
|
4850
|
+
});
|
|
4851
|
+
return new Promise((resolve) => {
|
|
4852
|
+
rl.question(question, (answer) => {
|
|
4853
|
+
rl.close();
|
|
4854
|
+
resolve(answer.trim());
|
|
4855
|
+
});
|
|
4856
|
+
});
|
|
4857
|
+
}
|
|
4858
|
+
function promptSelect(question, options) {
|
|
4859
|
+
return new Promise((resolve) => {
|
|
4860
|
+
console.log(question);
|
|
4861
|
+
options.forEach((opt, idx) => {
|
|
4862
|
+
console.log(` ${idx + 1}. ${opt.name}`);
|
|
4863
|
+
});
|
|
4864
|
+
const rl = readline.createInterface({
|
|
4865
|
+
input: process.stdin,
|
|
4866
|
+
output: process.stdout
|
|
4867
|
+
});
|
|
4868
|
+
rl.question("Enter number: ", (answer) => {
|
|
4869
|
+
rl.close();
|
|
4870
|
+
const idx = parseInt(answer.trim()) - 1;
|
|
4871
|
+
if (idx >= 0 && idx < options.length) {
|
|
4872
|
+
resolve(options[idx].id);
|
|
4873
|
+
} else {
|
|
4874
|
+
resolve(options[0].id);
|
|
4875
|
+
}
|
|
4876
|
+
});
|
|
4877
|
+
});
|
|
4878
|
+
}
|
|
4879
|
+
function promptMultiSelect(question, options) {
|
|
4880
|
+
return new Promise((resolve) => {
|
|
4881
|
+
console.log(question);
|
|
4882
|
+
options.forEach((opt, idx) => {
|
|
4883
|
+
console.log(` ${idx + 1}. ${opt.name}`);
|
|
4884
|
+
});
|
|
4885
|
+
const rl = readline.createInterface({
|
|
4886
|
+
input: process.stdin,
|
|
4887
|
+
output: process.stdout
|
|
4888
|
+
});
|
|
4889
|
+
rl.question("Enter numbers (comma-separated): ", (answer) => {
|
|
4890
|
+
rl.close();
|
|
4891
|
+
const indices = answer.split(",").map((s) => parseInt(s.trim()) - 1).filter((idx) => idx >= 0 && idx < options.length);
|
|
4892
|
+
const ids = indices.map((idx) => options[idx].id);
|
|
4893
|
+
resolve(ids);
|
|
4894
|
+
});
|
|
4895
|
+
});
|
|
4896
|
+
}
|
|
4897
|
+
async function promptConfirm(question) {
|
|
4898
|
+
const rl = readline.createInterface({
|
|
4899
|
+
input: process.stdin,
|
|
4900
|
+
output: process.stdout
|
|
4901
|
+
});
|
|
4902
|
+
return new Promise((resolve) => {
|
|
4903
|
+
rl.question(question, (answer) => {
|
|
4904
|
+
rl.close();
|
|
4905
|
+
const normalized = answer.trim().toLowerCase();
|
|
4906
|
+
resolve(normalized === "" || normalized === "y" || normalized === "yes");
|
|
4907
|
+
});
|
|
4908
|
+
});
|
|
4909
|
+
}
|
|
4910
|
+
function extractTitle(markdown) {
|
|
4911
|
+
const titleMatch = markdown.match(/^title:\s*(.+)$/m);
|
|
4912
|
+
if (titleMatch)
|
|
4913
|
+
return titleMatch[1].trim();
|
|
4914
|
+
const headingMatch = markdown.match(/^#\s+(.+)$/m);
|
|
4915
|
+
if (headingMatch)
|
|
4916
|
+
return headingMatch[1].trim();
|
|
4917
|
+
return void 0;
|
|
4918
|
+
}
|
|
4919
|
+
async function libraryPublish(options) {
|
|
4920
|
+
const auth = getCachedAuth();
|
|
4921
|
+
if (!auth) {
|
|
4922
|
+
error("Not logged in");
|
|
4923
|
+
instructions(["Run `vslides login` to authenticate"]);
|
|
4924
|
+
process.exit(ExitCode.AuthRequired);
|
|
4925
|
+
}
|
|
4926
|
+
if (!isAuthValid(auth)) {
|
|
4927
|
+
error("Login expired");
|
|
4928
|
+
instructions(["Run `vslides login` to re-authenticate"]);
|
|
4929
|
+
process.exit(ExitCode.AuthRequired);
|
|
4930
|
+
}
|
|
4931
|
+
const config = readConfig();
|
|
4932
|
+
if (!config) {
|
|
4933
|
+
error("No .vslides.json found. Run this from a presentation directory.");
|
|
4934
|
+
process.exit(ExitCode.ValidationError);
|
|
4935
|
+
}
|
|
4936
|
+
const slidesPath = (0, import_node_path6.join)(process.cwd(), "slides.md");
|
|
4937
|
+
if (!(0, import_node_fs7.existsSync)(slidesPath)) {
|
|
4938
|
+
error("No slides.md found.");
|
|
4939
|
+
process.exit(ExitCode.ValidationError);
|
|
4940
|
+
}
|
|
4941
|
+
const markdown = (0, import_node_fs7.readFileSync)(slidesPath, "utf-8");
|
|
4942
|
+
const tagsResult = await getLibraryTags();
|
|
4943
|
+
if (!tagsResult.ok) {
|
|
4944
|
+
error("Failed to fetch taxonomy");
|
|
4945
|
+
process.exit(ExitCode.NetworkError);
|
|
4946
|
+
}
|
|
4947
|
+
const tags = tagsResult.data;
|
|
4948
|
+
const existingLibraryId = options.update || config.libraryId;
|
|
4949
|
+
if (existingLibraryId) {
|
|
4950
|
+
info(`Updating library item: ${existingLibraryId}`);
|
|
4951
|
+
const updateResult = await updateLibraryItem(auth.token, existingLibraryId, { markdown });
|
|
4952
|
+
if (!updateResult.ok) {
|
|
4953
|
+
error("Failed to update library item");
|
|
4954
|
+
process.exit(ExitCode.NetworkError);
|
|
4955
|
+
}
|
|
4956
|
+
success(`Updated library item ${existingLibraryId} to v${updateResult.data.version}`);
|
|
4957
|
+
return;
|
|
4958
|
+
}
|
|
4959
|
+
const name = options.name || await prompt(`Name [${extractTitle(markdown) || config.slug}]: `) || extractTitle(markdown) || config.slug;
|
|
4960
|
+
const description = options.description || await prompt("Description (optional): ");
|
|
4961
|
+
let departmentId;
|
|
4962
|
+
if (options.department) {
|
|
4963
|
+
const dept = tags.departments.find(
|
|
4964
|
+
(d) => d.name.toLowerCase() === options.department.toLowerCase()
|
|
4965
|
+
);
|
|
4966
|
+
departmentId = dept?.id || tags.departments[0].id;
|
|
4967
|
+
} else {
|
|
4968
|
+
departmentId = await promptSelect("\nDepartment:", tags.departments);
|
|
4969
|
+
}
|
|
4970
|
+
let audienceIds;
|
|
4971
|
+
if (options.audience && options.audience.length > 0) {
|
|
4972
|
+
audienceIds = options.audience.map((name2) => tags.audiences.find((a) => a.name.toLowerCase() === name2.toLowerCase())?.id).filter((id) => id !== void 0);
|
|
4973
|
+
if (audienceIds.length === 0)
|
|
4974
|
+
audienceIds = [tags.audiences[0].id];
|
|
4975
|
+
} else {
|
|
4976
|
+
audienceIds = await promptMultiSelect("\nAudiences (select at least one):", tags.audiences);
|
|
4977
|
+
if (audienceIds.length === 0) {
|
|
4978
|
+
error("At least one audience required");
|
|
4979
|
+
process.exit(ExitCode.ValidationError);
|
|
4980
|
+
}
|
|
4981
|
+
}
|
|
4982
|
+
let productIds = [];
|
|
4983
|
+
if (options.product && options.product.length > 0) {
|
|
4984
|
+
productIds = options.product.map((name2) => tags.products.find((p) => p.name.toLowerCase() === name2.toLowerCase())?.id).filter((id) => id !== void 0);
|
|
4985
|
+
} else {
|
|
4986
|
+
productIds = await promptMultiSelect("\nProducts (optional):", tags.products);
|
|
4987
|
+
}
|
|
4988
|
+
const departmentName = tags.departments.find((d) => d.id === departmentId)?.name || "Unknown";
|
|
4989
|
+
const audienceNames = audienceIds.map((id) => tags.audiences.find((a) => a.id === id)?.name).filter(Boolean).join(", ");
|
|
4990
|
+
const productNames = productIds.length > 0 ? productIds.map((id) => tags.products.find((p) => p.id === id)?.name).filter(Boolean).join(", ") : "(none)";
|
|
4991
|
+
newline();
|
|
4992
|
+
console.log("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
|
|
4993
|
+
console.log("\u2502 Publishing to Library \u2502");
|
|
4994
|
+
console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
4995
|
+
console.log(`\u2502 Name: ${name.padEnd(25)}\u2502`);
|
|
4996
|
+
if (description) {
|
|
4997
|
+
const descDisplay = description.length > 25 ? description.slice(0, 22) + "..." : description;
|
|
4998
|
+
console.log(`\u2502 Description: ${descDisplay.padEnd(25)}\u2502`);
|
|
4999
|
+
}
|
|
5000
|
+
console.log(`\u2502 Department: ${departmentName.padEnd(25)}\u2502`);
|
|
5001
|
+
console.log(`\u2502 Audiences: ${audienceNames.padEnd(25)}\u2502`);
|
|
5002
|
+
console.log(`\u2502 Products: ${productNames.padEnd(25)}\u2502`);
|
|
5003
|
+
console.log("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
|
|
5004
|
+
newline();
|
|
5005
|
+
const confirmed = await promptConfirm("Publish with these settings? [Y/n] ");
|
|
5006
|
+
if (!confirmed) {
|
|
5007
|
+
info("Aborted.");
|
|
5008
|
+
process.exit(0);
|
|
5009
|
+
}
|
|
5010
|
+
newline();
|
|
5011
|
+
info("Publishing to library...");
|
|
5012
|
+
const createResult = await createLibraryItem(auth.token, {
|
|
5013
|
+
name,
|
|
5014
|
+
description: description || void 0,
|
|
5015
|
+
markdown,
|
|
5016
|
+
departmentId,
|
|
5017
|
+
audienceIds,
|
|
5018
|
+
productIds: productIds.length > 0 ? productIds : void 0,
|
|
5019
|
+
sourceSlug: config.slug
|
|
5020
|
+
});
|
|
5021
|
+
if (!createResult.ok) {
|
|
5022
|
+
error("Failed to create library item");
|
|
5023
|
+
process.exit(ExitCode.NetworkError);
|
|
5024
|
+
}
|
|
5025
|
+
success(`Created library item: ${createResult.data.id} (draft)`);
|
|
5026
|
+
if (!options.draft) {
|
|
5027
|
+
const publishResult = await publishLibraryItem(auth.token, createResult.data.id);
|
|
5028
|
+
if (publishResult.ok) {
|
|
5029
|
+
success("Published!");
|
|
5030
|
+
if (publishResult.data.deployUrl) {
|
|
5031
|
+
success(`Preview deployed: ${publishResult.data.deployUrl}`);
|
|
5032
|
+
} else if (publishResult.data.deployError) {
|
|
5033
|
+
warning(`Preview not available: ${publishResult.data.deployError}`);
|
|
5034
|
+
}
|
|
5035
|
+
} else {
|
|
5036
|
+
error("Failed to publish (item remains in draft)");
|
|
5037
|
+
}
|
|
5038
|
+
}
|
|
5039
|
+
config.libraryId = createResult.data.id;
|
|
5040
|
+
config.libraryVersion = 1;
|
|
5041
|
+
writeConfig(config);
|
|
5042
|
+
newline();
|
|
5043
|
+
info("Library item created!");
|
|
5044
|
+
newline();
|
|
5045
|
+
info(`ID: ${createResult.data.id}`);
|
|
5046
|
+
info(`Status: ${options.draft ? "draft" : "published"}`);
|
|
5047
|
+
newline();
|
|
5048
|
+
info("Others can import with:");
|
|
5049
|
+
info(` vslides library import ${createResult.data.id}`);
|
|
5050
|
+
}
|
|
5051
|
+
|
|
5052
|
+
// src/commands/library-import.ts
|
|
5053
|
+
var import_node_fs8 = require("node:fs");
|
|
5054
|
+
var import_node_path7 = require("node:path");
|
|
5055
|
+
var LIBRARY_CONFIG_FILE = ".vslides-library.json";
|
|
5056
|
+
function readLibraryConfig() {
|
|
5057
|
+
const configPath = (0, import_node_path7.join)(process.cwd(), LIBRARY_CONFIG_FILE);
|
|
5058
|
+
if (!(0, import_node_fs8.existsSync)(configPath)) {
|
|
5059
|
+
return { imports: {} };
|
|
5060
|
+
}
|
|
5061
|
+
return JSON.parse((0, import_node_fs8.readFileSync)(configPath, "utf-8"));
|
|
5062
|
+
}
|
|
5063
|
+
function writeLibraryConfig(config) {
|
|
5064
|
+
const configPath = (0, import_node_path7.join)(process.cwd(), LIBRARY_CONFIG_FILE);
|
|
5065
|
+
(0, import_node_fs8.writeFileSync)(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
5066
|
+
}
|
|
5067
|
+
async function libraryImport(id) {
|
|
5068
|
+
info(`Fetching library item: ${id}`);
|
|
5069
|
+
const result = await getLibraryImportBundle(id);
|
|
5070
|
+
if (!result.ok) {
|
|
5071
|
+
if (result.status === 404) {
|
|
5072
|
+
error(`Library item not found: ${id}`);
|
|
5073
|
+
} else {
|
|
5074
|
+
error("Failed to fetch import bundle");
|
|
5075
|
+
}
|
|
5076
|
+
process.exit(ExitCode.NetworkError);
|
|
5077
|
+
}
|
|
5078
|
+
const bundle = result.data;
|
|
5079
|
+
const libraryDir = (0, import_node_path7.join)(process.cwd(), "library");
|
|
5080
|
+
if (!(0, import_node_fs8.existsSync)(libraryDir)) {
|
|
5081
|
+
(0, import_node_fs8.mkdirSync)(libraryDir, { recursive: true });
|
|
5082
|
+
}
|
|
5083
|
+
const filename = bundle.name.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "") + ".md";
|
|
5084
|
+
const filePath = (0, import_node_path7.join)(libraryDir, filename);
|
|
5085
|
+
(0, import_node_fs8.writeFileSync)(filePath, bundle.markdown);
|
|
5086
|
+
success(`Downloaded: ./library/${filename} (v${bundle.version})`);
|
|
5087
|
+
if (bundle.assets.length > 0) {
|
|
5088
|
+
info(`Assets: ${bundle.assets.length} images (using blob URLs)`);
|
|
5089
|
+
}
|
|
5090
|
+
const config = readLibraryConfig();
|
|
5091
|
+
config.imports[id] = {
|
|
5092
|
+
version: bundle.version,
|
|
5093
|
+
importedAt: Date.now(),
|
|
5094
|
+
localPath: `./library/${filename}`
|
|
5095
|
+
};
|
|
5096
|
+
writeLibraryConfig(config);
|
|
5097
|
+
newline();
|
|
5098
|
+
info("Import complete!");
|
|
5099
|
+
newline();
|
|
5100
|
+
info("Add to your slides.md:");
|
|
5101
|
+
info(` ---`);
|
|
5102
|
+
info(` src: ./library/${filename}`);
|
|
5103
|
+
info(` ---`);
|
|
5104
|
+
newline();
|
|
5105
|
+
info("To check for updates later:");
|
|
5106
|
+
info(" vslides library status");
|
|
5107
|
+
}
|
|
5108
|
+
|
|
5109
|
+
// src/commands/library-status.ts
|
|
5110
|
+
var import_node_fs9 = require("node:fs");
|
|
5111
|
+
var import_node_path8 = require("node:path");
|
|
5112
|
+
var LIBRARY_CONFIG_FILE2 = ".vslides-library.json";
|
|
5113
|
+
async function libraryStatus() {
|
|
5114
|
+
const configPath = (0, import_node_path8.join)(process.cwd(), LIBRARY_CONFIG_FILE2);
|
|
5115
|
+
if (!(0, import_node_fs9.existsSync)(configPath)) {
|
|
5116
|
+
info("No library imports found in this project.");
|
|
5117
|
+
instructions(["Import items with: vslides library import <id>"]);
|
|
5118
|
+
return;
|
|
5119
|
+
}
|
|
5120
|
+
const config = JSON.parse((0, import_node_fs9.readFileSync)(configPath, "utf-8"));
|
|
5121
|
+
const imports = Object.entries(config.imports);
|
|
5122
|
+
if (imports.length === 0) {
|
|
5123
|
+
info("No library imports found.");
|
|
5124
|
+
return;
|
|
5125
|
+
}
|
|
5126
|
+
info("\nLibrary Import Status\n");
|
|
5127
|
+
for (const [id, info2] of imports) {
|
|
5128
|
+
const result = await getLibraryItem(id);
|
|
5129
|
+
if (!result.ok) {
|
|
5130
|
+
info(`[ERROR] ${id}`);
|
|
5131
|
+
info(" Could not fetch item (may have been deleted)");
|
|
5132
|
+
newline();
|
|
5133
|
+
continue;
|
|
5134
|
+
}
|
|
5135
|
+
const item = result.data;
|
|
5136
|
+
const isUpToDate = item.version === info2.version;
|
|
5137
|
+
const status2 = isUpToDate ? `v${info2.version} (current)` : `v${info2.version} -> v${item.version} available`;
|
|
5138
|
+
info(`${item.name} (${id})`);
|
|
5139
|
+
info(` ${isUpToDate ? "[OK]" : "[UPDATE]"} ${status2}`);
|
|
5140
|
+
info(` Local: ${info2.localPath}`);
|
|
5141
|
+
if (!isUpToDate) {
|
|
5142
|
+
info(` Run: vslides library upgrade ${id}`);
|
|
5143
|
+
}
|
|
5144
|
+
newline();
|
|
5145
|
+
}
|
|
5146
|
+
}
|
|
5147
|
+
|
|
5148
|
+
// src/commands/library-upgrade.ts
|
|
5149
|
+
var import_node_fs10 = require("node:fs");
|
|
5150
|
+
var import_node_path9 = require("node:path");
|
|
5151
|
+
var LIBRARY_CONFIG_FILE3 = ".vslides-library.json";
|
|
5152
|
+
async function libraryUpgrade(id, options) {
|
|
5153
|
+
const configPath = (0, import_node_path9.join)(process.cwd(), LIBRARY_CONFIG_FILE3);
|
|
5154
|
+
if (!(0, import_node_fs10.existsSync)(configPath)) {
|
|
5155
|
+
info("No library imports found.");
|
|
5156
|
+
return;
|
|
5157
|
+
}
|
|
5158
|
+
const config = JSON.parse((0, import_node_fs10.readFileSync)(configPath, "utf-8"));
|
|
5159
|
+
let idsToUpgrade = [];
|
|
5160
|
+
if (options.all) {
|
|
5161
|
+
idsToUpgrade = Object.keys(config.imports);
|
|
5162
|
+
} else if (id) {
|
|
5163
|
+
if (!config.imports[id]) {
|
|
5164
|
+
error(`Library item ${id} not found in imports.`);
|
|
5165
|
+
process.exit(ExitCode.ValidationError);
|
|
5166
|
+
}
|
|
5167
|
+
idsToUpgrade = [id];
|
|
5168
|
+
} else {
|
|
5169
|
+
error("Specify an ID or use --all");
|
|
5170
|
+
process.exit(ExitCode.UsageError);
|
|
5171
|
+
}
|
|
5172
|
+
let upgraded = 0;
|
|
5173
|
+
let skipped = 0;
|
|
5174
|
+
for (const itemId of idsToUpgrade) {
|
|
5175
|
+
const info2 = config.imports[itemId];
|
|
5176
|
+
const itemResult = await getLibraryItem(itemId);
|
|
5177
|
+
if (!itemResult.ok) {
|
|
5178
|
+
error(`Failed to fetch ${itemId}`);
|
|
5179
|
+
continue;
|
|
5180
|
+
}
|
|
5181
|
+
const item = itemResult.data;
|
|
5182
|
+
if (item.version === info2.version) {
|
|
5183
|
+
info(`${item.name}: already up to date (v${item.version})`);
|
|
5184
|
+
skipped++;
|
|
5185
|
+
continue;
|
|
5186
|
+
}
|
|
5187
|
+
info(`Upgrading ${item.name}: v${info2.version} -> v${item.version}`);
|
|
5188
|
+
const bundleResult = await getLibraryImportBundle(itemId);
|
|
5189
|
+
if (!bundleResult.ok) {
|
|
5190
|
+
error(`Failed to fetch bundle for ${itemId}`);
|
|
5191
|
+
continue;
|
|
5192
|
+
}
|
|
5193
|
+
const bundle = bundleResult.data;
|
|
5194
|
+
(0, import_node_fs10.writeFileSync)((0, import_node_path9.join)(process.cwd(), info2.localPath), bundle.markdown);
|
|
5195
|
+
config.imports[itemId].version = bundle.version;
|
|
5196
|
+
config.imports[itemId].importedAt = Date.now();
|
|
5197
|
+
success(`Upgraded to v${bundle.version}`);
|
|
5198
|
+
upgraded++;
|
|
5199
|
+
}
|
|
5200
|
+
(0, import_node_fs10.writeFileSync)(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
5201
|
+
newline();
|
|
5202
|
+
info(`Upgraded: ${upgraded}, Skipped: ${skipped}`);
|
|
5203
|
+
}
|
|
5204
|
+
|
|
5205
|
+
// src/commands/library-tags.ts
|
|
5206
|
+
async function libraryTags() {
|
|
5207
|
+
const result = await getLibraryTags();
|
|
5208
|
+
if (!result.ok) {
|
|
5209
|
+
error("Failed to fetch taxonomy");
|
|
5210
|
+
process.exit(ExitCode.NetworkError);
|
|
5211
|
+
}
|
|
5212
|
+
const { departments, audiences, products } = result.data;
|
|
5213
|
+
info("\nDepartments:");
|
|
5214
|
+
for (const dept of departments) {
|
|
5215
|
+
info(` ${dept.id}. ${dept.name}`);
|
|
5216
|
+
}
|
|
5217
|
+
info("\nAudiences:");
|
|
5218
|
+
for (const aud of audiences) {
|
|
5219
|
+
info(` ${aud.id}. ${aud.name}`);
|
|
5220
|
+
}
|
|
5221
|
+
info("\nProducts:");
|
|
5222
|
+
for (const prod of products) {
|
|
5223
|
+
info(` ${prod.id}. ${prod.name}`);
|
|
5224
|
+
}
|
|
5225
|
+
newline();
|
|
5226
|
+
}
|
|
5227
|
+
|
|
5228
|
+
// src/commands/library-info.ts
|
|
5229
|
+
async function libraryInfo(id) {
|
|
5230
|
+
const result = await getLibraryItem(id);
|
|
5231
|
+
if (!result.ok) {
|
|
5232
|
+
if (result.status === 404) {
|
|
5233
|
+
error(`Library item not found: ${id}`);
|
|
5234
|
+
} else {
|
|
5235
|
+
error("Failed to fetch library item");
|
|
5236
|
+
}
|
|
5237
|
+
process.exit(ExitCode.NetworkError);
|
|
5238
|
+
}
|
|
5239
|
+
const item = result.data;
|
|
5240
|
+
info(`
|
|
5241
|
+
${item.name}
|
|
5242
|
+
`);
|
|
5243
|
+
info(`ID: ${item.id}`);
|
|
5244
|
+
info(`Status: ${item.status}`);
|
|
5245
|
+
info(`Version: ${item.version}`);
|
|
5246
|
+
info(`Department: ${item.department.name}`);
|
|
5247
|
+
info(`Audiences: ${item.audiences.map((a) => a.name).join(", ")}`);
|
|
5248
|
+
if (item.products.length > 0) {
|
|
5249
|
+
info(`Products: ${item.products.map((p) => p.name).join(", ")}`);
|
|
5250
|
+
}
|
|
5251
|
+
info(`Owner: ${item.owner}`);
|
|
5252
|
+
info(`Created: ${new Date(item.createdAt).toLocaleDateString()}`);
|
|
5253
|
+
info(`Updated: ${new Date(item.updatedAt).toLocaleDateString()}`);
|
|
5254
|
+
if (item.lastVerified) {
|
|
5255
|
+
info(`Last Verified: ${new Date(item.lastVerified).toLocaleDateString()}`);
|
|
5256
|
+
}
|
|
5257
|
+
if (item.description) {
|
|
5258
|
+
newline();
|
|
5259
|
+
info(`Description:
|
|
5260
|
+
${item.description}`);
|
|
5261
|
+
}
|
|
5262
|
+
newline();
|
|
5263
|
+
info(`Import: vslides library import ${item.id}`);
|
|
5264
|
+
newline();
|
|
5265
|
+
}
|
|
5266
|
+
|
|
4679
5267
|
// src/cli.ts
|
|
4680
5268
|
function wrapCommand(fn) {
|
|
4681
5269
|
return (...args) => {
|
|
@@ -4722,4 +5310,13 @@ program.command("export <format>").description("Export presentation to PDF or PP
|
|
|
4722
5310
|
program.command("deploy").description("Deploy presentation as static site").option("--status", "Check deployment status").action(wrapCommand((options) => deploy2(options)));
|
|
4723
5311
|
program.command("history").description("List saved versions").action(wrapCommand(history));
|
|
4724
5312
|
program.command("revert <version>").description("Revert to a previous version").action(wrapCommand(revert));
|
|
5313
|
+
var library = program.command("library").description("Manage slide library");
|
|
5314
|
+
library.command("list").description("List library items").option("-d, --department <name>", "Filter by department").option("-a, --audience <name>", "Filter by audience").option("-p, --product <name>", "Filter by product").option("--all", "Include drafts and deprecated").option("-l, --limit <number>", "Number of results", "20").action(wrapCommand((options) => libraryList(options)));
|
|
5315
|
+
library.command("search <query>").description("Search library items").option("-l, --limit <number>", "Number of results", "20").action(wrapCommand((query, options) => librarySearch(query, options)));
|
|
5316
|
+
library.command("publish").description("Publish current presentation to library").option("-n, --name <name>", "Library item name").option("-d, --description <desc>", "Description").option("--department <name>", "Department").option("--audience <name...>", "Audiences").option("--product <name...>", "Products").option("--update <id>", "Update existing library item").option("--draft", "Create as draft (do not publish)").action(wrapCommand((options) => libraryPublish(options)));
|
|
5317
|
+
library.command("import <id>").description("Import library item into current project").action(wrapCommand((id) => libraryImport(id)));
|
|
5318
|
+
library.command("status").description("Check status of imported library items").action(wrapCommand(libraryStatus));
|
|
5319
|
+
library.command("upgrade [id]").description("Upgrade imported library items").option("--all", "Upgrade all imports").action(wrapCommand((id, options) => libraryUpgrade(id, options)));
|
|
5320
|
+
library.command("tags").description("List available taxonomy values").action(wrapCommand(libraryTags));
|
|
5321
|
+
library.command("info <id>").description("Show details of a library item").action(wrapCommand((id) => libraryInfo(id)));
|
|
4725
5322
|
program.parse();
|