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.
Files changed (3) hide show
  1. package/README.md +67 -0
  2. package/dist/cli.js +597 -0
  3. 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();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vslides",
3
- "version": "1.0.23",
3
+ "version": "1.0.25",
4
4
  "description": "CLI for Vercel Slides API",
5
5
  "license": "MIT",
6
6
  "author": "Vercel",