@strategicnerds/slide-nerds 0.1.1 → 0.2.0

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 (75) hide show
  1. package/dist/cli/commands/brand.d.ts +3 -0
  2. package/dist/cli/commands/brand.d.ts.map +1 -0
  3. package/dist/cli/commands/brand.js +145 -0
  4. package/dist/cli/commands/brand.js.map +1 -0
  5. package/dist/cli/commands/config.d.ts +18 -0
  6. package/dist/cli/commands/config.d.ts.map +1 -0
  7. package/dist/cli/commands/config.js +37 -0
  8. package/dist/cli/commands/config.js.map +1 -0
  9. package/dist/cli/commands/link.d.ts +12 -0
  10. package/dist/cli/commands/link.d.ts.map +1 -0
  11. package/dist/cli/commands/link.js +106 -0
  12. package/dist/cli/commands/link.js.map +1 -0
  13. package/dist/cli/commands/login.d.ts +4 -0
  14. package/dist/cli/commands/login.d.ts.map +1 -0
  15. package/dist/cli/commands/login.js +92 -0
  16. package/dist/cli/commands/login.js.map +1 -0
  17. package/dist/cli/commands/push.d.ts +13 -0
  18. package/dist/cli/commands/push.d.ts.map +1 -0
  19. package/dist/cli/commands/push.js +156 -0
  20. package/dist/cli/commands/push.js.map +1 -0
  21. package/dist/cli/index.js +8 -0
  22. package/dist/cli/index.js.map +1 -1
  23. package/dist/runtime/export-api.d.ts +2 -1
  24. package/dist/runtime/export-api.d.ts.map +1 -1
  25. package/dist/runtime/export-api.js +150 -7
  26. package/dist/runtime/export-api.js.map +1 -1
  27. package/dist/runtime/index.d.ts +6 -0
  28. package/dist/runtime/index.d.ts.map +1 -1
  29. package/dist/runtime/index.js +5 -0
  30. package/dist/runtime/index.js.map +1 -1
  31. package/dist/runtime/light-table.d.ts.map +1 -1
  32. package/dist/runtime/light-table.js +34 -10
  33. package/dist/runtime/light-table.js.map +1 -1
  34. package/dist/runtime/live/LiveAudienceCount.d.ts +4 -0
  35. package/dist/runtime/live/LiveAudienceCount.d.ts.map +1 -0
  36. package/dist/runtime/live/LiveAudienceCount.js +90 -0
  37. package/dist/runtime/live/LiveAudienceCount.js.map +1 -0
  38. package/dist/runtime/live/LivePoll.d.ts +9 -0
  39. package/dist/runtime/live/LivePoll.d.ts.map +1 -0
  40. package/dist/runtime/live/LivePoll.js +122 -0
  41. package/dist/runtime/live/LivePoll.js.map +1 -0
  42. package/dist/runtime/live/LiveQA.d.ts +4 -0
  43. package/dist/runtime/live/LiveQA.d.ts.map +1 -0
  44. package/dist/runtime/live/LiveQA.js +138 -0
  45. package/dist/runtime/live/LiveQA.js.map +1 -0
  46. package/dist/runtime/live/LiveReactions.d.ts +4 -0
  47. package/dist/runtime/live/LiveReactions.d.ts.map +1 -0
  48. package/dist/runtime/live/LiveReactions.js +124 -0
  49. package/dist/runtime/live/LiveReactions.js.map +1 -0
  50. package/dist/runtime/live/LiveWordCloud.d.ts +8 -0
  51. package/dist/runtime/live/LiveWordCloud.d.ts.map +1 -0
  52. package/dist/runtime/live/LiveWordCloud.js +141 -0
  53. package/dist/runtime/live/LiveWordCloud.js.map +1 -0
  54. package/dist/runtime/live/index.d.ts +7 -0
  55. package/dist/runtime/live/index.d.ts.map +1 -0
  56. package/dist/runtime/live/index.js +6 -0
  57. package/dist/runtime/live/index.js.map +1 -0
  58. package/dist/runtime/live/types.d.ts +40 -0
  59. package/dist/runtime/live/types.d.ts.map +1 -0
  60. package/dist/runtime/live/types.js +2 -0
  61. package/dist/runtime/live/types.js.map +1 -0
  62. package/dist/runtime/live/use-live-session.d.ts +17 -0
  63. package/dist/runtime/live/use-live-session.d.ts.map +1 -0
  64. package/dist/runtime/live/use-live-session.js +73 -0
  65. package/dist/runtime/live/use-live-session.js.map +1 -0
  66. package/dist/runtime/pptx-export.d.ts +2 -0
  67. package/dist/runtime/pptx-export.d.ts.map +1 -0
  68. package/dist/runtime/pptx-export.js +344 -0
  69. package/dist/runtime/pptx-export.js.map +1 -0
  70. package/dist/runtime/slide-controls.d.ts.map +1 -1
  71. package/dist/runtime/slide-controls.js +25 -22
  72. package/dist/runtime/slide-controls.js.map +1 -1
  73. package/package.json +3 -1
  74. package/skills/live/SKILL.md +244 -0
  75. package/templates/next-app/next.config.ts.tmpl +25 -0
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare const registerBrandCommand: (program: Command) => void;
3
+ //# sourceMappingURL=brand.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/brand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AA6InC,eAAO,MAAM,oBAAoB,GAAI,SAAS,OAAO,KAAG,IAwCvD,CAAA"}
@@ -0,0 +1,145 @@
1
+ import path from 'node:path';
2
+ import fs from 'fs-extra';
3
+ import { getCredentials, getServiceUrl } from './config.js';
4
+ const fetchWithAuth = async (url, options = {}) => {
5
+ const creds = await getCredentials();
6
+ if (!creds) {
7
+ throw new Error('Not logged in. Run `slidenerds login` first.');
8
+ }
9
+ return fetch(url, {
10
+ ...options,
11
+ headers: {
12
+ Authorization: `Bearer ${creds.access_token}`,
13
+ 'Content-Type': 'application/json',
14
+ ...options.headers,
15
+ },
16
+ });
17
+ };
18
+ const brandGet = async (name) => {
19
+ const serviceUrl = await getServiceUrl();
20
+ const encodedName = encodeURIComponent(name);
21
+ const resp = await fetchWithAuth(`${serviceUrl}/api/brands?name=${encodedName}`);
22
+ if (!resp.ok) {
23
+ const err = await resp.json().catch(() => ({ error: 'Unknown error' }));
24
+ throw new Error(err.error ?? `Failed to fetch brand config: ${resp.status}`);
25
+ }
26
+ const configs = (await resp.json());
27
+ if (configs.length === 0) {
28
+ throw new Error(`No brand config found with name "${name}"`);
29
+ }
30
+ const config = configs[0].config;
31
+ const filePath = path.join(process.cwd(), 'brand.config.ts');
32
+ const fileContent = `import type { BrandConfig } from '@strategicnerds/slide-nerds'
33
+
34
+ export default ${JSON.stringify(config, null, 2)} satisfies BrandConfig
35
+ `;
36
+ await fs.writeFile(filePath, fileContent, 'utf-8');
37
+ console.log(`Wrote brand config "${name}" to brand.config.ts`);
38
+ };
39
+ const brandSet = async (name) => {
40
+ const filePath = path.join(process.cwd(), 'brand.config.ts');
41
+ if (!(await fs.pathExists(filePath))) {
42
+ throw new Error('No brand.config.ts found in the current directory.');
43
+ }
44
+ const serviceUrl = await getServiceUrl();
45
+ // Build and import the brand config from the local file.
46
+ // Use a timestamp query param to bust any module cache.
47
+ const absolutePath = path.resolve(filePath);
48
+ const imported = await import(`${absolutePath}?t=${Date.now()}`);
49
+ const config = imported.default;
50
+ if (!config || typeof config !== 'object') {
51
+ throw new Error('brand.config.ts must export a default object.');
52
+ }
53
+ // Check if a config with this name already exists
54
+ const encodedName = encodeURIComponent(name);
55
+ const listResp = await fetchWithAuth(`${serviceUrl}/api/brands?name=${encodedName}`);
56
+ if (!listResp.ok) {
57
+ throw new Error(`Failed to check existing configs: ${listResp.status}`);
58
+ }
59
+ const existing = (await listResp.json());
60
+ if (existing.length > 0) {
61
+ // Update existing
62
+ const resp = await fetchWithAuth(`${serviceUrl}/api/brands/${existing[0].id}`, {
63
+ method: 'PATCH',
64
+ body: JSON.stringify({ config }),
65
+ });
66
+ if (!resp.ok) {
67
+ const err = await resp.json().catch(() => ({ error: 'Unknown error' }));
68
+ throw new Error(err.error ?? `Failed to update brand config: ${resp.status}`);
69
+ }
70
+ console.log(`Updated brand config "${name}"`);
71
+ }
72
+ else {
73
+ // Create new
74
+ const resp = await fetchWithAuth(`${serviceUrl}/api/brands`, {
75
+ method: 'POST',
76
+ body: JSON.stringify({ name, config }),
77
+ });
78
+ if (!resp.ok) {
79
+ const err = await resp.json().catch(() => ({ error: 'Unknown error' }));
80
+ throw new Error(err.error ?? `Failed to create brand config: ${resp.status}`);
81
+ }
82
+ console.log(`Created brand config "${name}"`);
83
+ }
84
+ };
85
+ const brandList = async () => {
86
+ const serviceUrl = await getServiceUrl();
87
+ const resp = await fetchWithAuth(`${serviceUrl}/api/brands`);
88
+ if (!resp.ok) {
89
+ const err = await resp.json().catch(() => ({ error: 'Unknown error' }));
90
+ throw new Error(err.error ?? `Failed to list brand configs: ${resp.status}`);
91
+ }
92
+ const configs = (await resp.json());
93
+ if (configs.length === 0) {
94
+ console.log('No brand configs found.');
95
+ return;
96
+ }
97
+ console.log(`Found ${configs.length} brand config(s):\n`);
98
+ for (const c of configs) {
99
+ const scope = c.team_id ? 'team' : 'personal';
100
+ const updated = new Date(c.updated_at).toLocaleDateString();
101
+ console.log(` ${c.name} (${scope}) -- updated ${updated}`);
102
+ }
103
+ };
104
+ export const registerBrandCommand = (program) => {
105
+ const brand = program
106
+ .command('brand')
107
+ .description('Manage brand configurations on slidenerds.com');
108
+ brand
109
+ .command('get <name>')
110
+ .description('Download a brand config and write it to brand.config.ts')
111
+ .action(async (name) => {
112
+ try {
113
+ await brandGet(name);
114
+ }
115
+ catch (err) {
116
+ console.error(`brand get failed: ${err instanceof Error ? err.message : err}`);
117
+ process.exit(1);
118
+ }
119
+ });
120
+ brand
121
+ .command('set <name>')
122
+ .description('Upload the local brand.config.ts to slidenerds.com')
123
+ .action(async (name) => {
124
+ try {
125
+ await brandSet(name);
126
+ }
127
+ catch (err) {
128
+ console.error(`brand set failed: ${err instanceof Error ? err.message : err}`);
129
+ process.exit(1);
130
+ }
131
+ });
132
+ brand
133
+ .command('list')
134
+ .description('List all saved brand configs')
135
+ .action(async () => {
136
+ try {
137
+ await brandList();
138
+ }
139
+ catch (err) {
140
+ console.error(`brand list failed: ${err instanceof Error ? err.message : err}`);
141
+ process.exit(1);
142
+ }
143
+ });
144
+ };
145
+ //# sourceMappingURL=brand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand.js","sourceRoot":"","sources":["../../../src/cli/commands/brand.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ type Credentials = {
2
+ access_token: string;
3
+ refresh_token: string;
4
+ url: string;
5
+ };
6
+ type ProjectConfig = {
7
+ deck_id: string;
8
+ deck_name: string;
9
+ service_url: string;
10
+ };
11
+ export declare const getCredentials: () => Promise<Credentials | null>;
12
+ export declare const saveCredentials: (creds: Credentials) => Promise<void>;
13
+ export declare const clearCredentials: () => Promise<void>;
14
+ export declare const getProjectConfig: (dir?: string) => Promise<ProjectConfig | null>;
15
+ export declare const saveProjectConfig: (config: ProjectConfig, dir?: string) => Promise<void>;
16
+ export declare const getServiceUrl: () => Promise<string>;
17
+ export {};
18
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/config.ts"],"names":[],"mappings":"AAQA,KAAK,WAAW,GAAG;IACjB,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;IACrB,GAAG,EAAE,MAAM,CAAA;CACZ,CAAA;AAED,KAAK,aAAa,GAAG;IACnB,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,eAAO,MAAM,cAAc,QAAa,OAAO,CAAC,WAAW,GAAG,IAAI,CAIjE,CAAA;AAED,eAAO,MAAM,eAAe,GAAU,OAAO,WAAW,KAAG,OAAO,CAAC,IAAI,CAGtE,CAAA;AAED,eAAO,MAAM,gBAAgB,QAAa,OAAO,CAAC,IAAI,CAIrD,CAAA;AAED,eAAO,MAAM,gBAAgB,GAAU,MAAK,MAAsB,KAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAKhG,CAAA;AAED,eAAO,MAAM,iBAAiB,GAC5B,QAAQ,aAAa,EACrB,MAAK,MAAsB,KAC1B,OAAO,CAAC,IAAI,CAGd,CAAA;AAED,eAAO,MAAM,aAAa,QAAa,OAAO,CAAC,MAAM,CAGpD,CAAA"}
@@ -0,0 +1,37 @@
1
+ import path from 'node:path';
2
+ import os from 'node:os';
3
+ import fs from 'fs-extra';
4
+ const CONFIG_DIR = path.join(os.homedir(), '.slidenerds');
5
+ const CREDENTIALS_FILE = path.join(CONFIG_DIR, 'credentials.json');
6
+ const PROJECT_CONFIG_FILE = '.slidenerds.json';
7
+ export const getCredentials = async () => {
8
+ if (!(await fs.pathExists(CREDENTIALS_FILE)))
9
+ return null;
10
+ const data = await fs.readJson(CREDENTIALS_FILE);
11
+ return data;
12
+ };
13
+ export const saveCredentials = async (creds) => {
14
+ await fs.ensureDir(CONFIG_DIR);
15
+ await fs.writeJson(CREDENTIALS_FILE, creds, { spaces: 2 });
16
+ };
17
+ export const clearCredentials = async () => {
18
+ if (await fs.pathExists(CREDENTIALS_FILE)) {
19
+ await fs.remove(CREDENTIALS_FILE);
20
+ }
21
+ };
22
+ export const getProjectConfig = async (dir = process.cwd()) => {
23
+ const configPath = path.join(dir, PROJECT_CONFIG_FILE);
24
+ if (!(await fs.pathExists(configPath)))
25
+ return null;
26
+ const data = await fs.readJson(configPath);
27
+ return data;
28
+ };
29
+ export const saveProjectConfig = async (config, dir = process.cwd()) => {
30
+ const configPath = path.join(dir, PROJECT_CONFIG_FILE);
31
+ await fs.writeJson(configPath, config, { spaces: 2 });
32
+ };
33
+ export const getServiceUrl = async () => {
34
+ const creds = await getCredentials();
35
+ return creds?.url ?? 'https://slidenerds.com';
36
+ };
37
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/cli/commands/config.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,EAAE,MAAM,UAAU,CAAA;AAEzB,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,aAAa,CAAC,CAAA;AACzD,MAAM,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,kBAAkB,CAAC,CAAA;AAClE,MAAM,mBAAmB,GAAG,kBAAkB,CAAA;AAc9C,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,IAAiC,EAAE;IACpE,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;QAAE,OAAO,IAAI,CAAA;IACzD,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAA;IAChD,OAAO,IAAmB,CAAA;AAC5B,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,EAAE,KAAkB,EAAiB,EAAE;IACzE,MAAM,EAAE,CAAC,SAAS,CAAC,UAAU,CAAC,CAAA;IAC9B,MAAM,EAAE,CAAC,SAAS,CAAC,gBAAgB,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAA;AAC5D,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,IAAmB,EAAE;IACxD,IAAI,MAAM,EAAE,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;QAC1C,MAAM,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAA;IACnC,CAAC;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,EAAE,MAAc,OAAO,CAAC,GAAG,EAAE,EAAiC,EAAE;IACnG,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,mBAAmB,CAAC,CAAA;IACtD,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QAAE,OAAO,IAAI,CAAA;IACnD,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAA;IAC1C,OAAO,IAAqB,CAAA;AAC9B,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,EACpC,MAAqB,EACrB,MAAc,OAAO,CAAC,GAAG,EAAE,EACZ,EAAE;IACjB,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,mBAAmB,CAAC,CAAA;IACtD,MAAM,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAA;AACvD,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,IAAqB,EAAE;IACvD,MAAM,KAAK,GAAG,MAAM,cAAc,EAAE,CAAA;IACpC,OAAO,KAAK,EAAE,GAAG,IAAI,wBAAwB,CAAA;AAC/C,CAAC,CAAA"}
@@ -0,0 +1,12 @@
1
+ import { Command } from 'commander';
2
+ export declare const linkProject: (options: {
3
+ name?: string;
4
+ deckId?: string;
5
+ serviceUrl: string;
6
+ accessToken: string;
7
+ }) => Promise<{
8
+ deckId: string;
9
+ deckName: string;
10
+ }>;
11
+ export declare const registerLinkCommand: (program: Command) => void;
12
+ //# sourceMappingURL=link.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/link.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AA4BnC,eAAO,MAAM,WAAW,GAAU,SAAS;IACzC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;CACpB,KAAG,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAgD/C,CAAA;AAED,eAAO,MAAM,mBAAmB,GAAI,SAAS,OAAO,KAAG,IA4CtD,CAAA"}
@@ -0,0 +1,106 @@
1
+ import path from 'node:path';
2
+ import fs from 'fs-extra';
3
+ import { getCredentials, getProjectConfig, saveProjectConfig, getServiceUrl } from './config.js';
4
+ const detectProjectName = async (dir) => {
5
+ const pkgPath = path.join(dir, 'package.json');
6
+ if (await fs.pathExists(pkgPath)) {
7
+ const pkg = await fs.readJson(pkgPath);
8
+ return pkg.name ?? path.basename(dir);
9
+ }
10
+ return path.basename(dir);
11
+ };
12
+ const isSlidenerdProject = async (dir) => {
13
+ const brandConfig = path.join(dir, 'brand.config.ts');
14
+ if (await fs.pathExists(brandConfig))
15
+ return true;
16
+ const pkgPath = path.join(dir, 'package.json');
17
+ if (await fs.pathExists(pkgPath)) {
18
+ const pkg = await fs.readJson(pkgPath);
19
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
20
+ return '@strategicnerds/slide-nerds' in deps;
21
+ }
22
+ return false;
23
+ };
24
+ export const linkProject = async (options) => {
25
+ const dir = process.cwd();
26
+ const { serviceUrl, accessToken } = options;
27
+ // If a deck ID is provided, just link to it
28
+ if (options.deckId) {
29
+ const resp = await fetch(`${serviceUrl}/api/decks/${options.deckId}`, {
30
+ headers: { Authorization: `Bearer ${accessToken}` },
31
+ });
32
+ if (!resp.ok)
33
+ throw new Error('Deck not found');
34
+ const deck = await resp.json();
35
+ await saveProjectConfig({
36
+ deck_id: deck.id,
37
+ deck_name: deck.name,
38
+ service_url: serviceUrl,
39
+ }, dir);
40
+ return { deckId: deck.id, deckName: deck.name };
41
+ }
42
+ // Otherwise, create a new deck
43
+ const name = options.name ?? await detectProjectName(dir);
44
+ const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
45
+ const resp = await fetch(`${serviceUrl}/api/decks`, {
46
+ method: 'POST',
47
+ headers: {
48
+ Authorization: `Bearer ${accessToken}`,
49
+ 'Content-Type': 'application/json',
50
+ },
51
+ body: JSON.stringify({ name, slug, source_type: 'push' }),
52
+ });
53
+ if (!resp.ok) {
54
+ const err = await resp.json().catch(() => ({ error: 'Unknown error' }));
55
+ throw new Error(err.error ?? `Failed to create deck: ${resp.status}`);
56
+ }
57
+ const deck = await resp.json();
58
+ await saveProjectConfig({
59
+ deck_id: deck.id,
60
+ deck_name: deck.name,
61
+ service_url: serviceUrl,
62
+ }, dir);
63
+ return { deckId: deck.id, deckName: deck.name };
64
+ };
65
+ export const registerLinkCommand = (program) => {
66
+ program
67
+ .command('link')
68
+ .description('Link this project to a deck on slidenerds.com')
69
+ .option('--name <name>', 'Deck name (defaults to package name)')
70
+ .option('--deck-id <id>', 'Link to an existing deck by ID')
71
+ .action(async (options) => {
72
+ const dir = process.cwd();
73
+ if (!(await isSlidenerdProject(dir))) {
74
+ console.error('This does not look like a slidenerds project.');
75
+ console.error('Run this from a directory with brand.config.ts or @strategicnerds/slide-nerds as a dependency.');
76
+ process.exit(1);
77
+ }
78
+ const existing = await getProjectConfig(dir);
79
+ if (existing) {
80
+ console.log(`Already linked to "${existing.deck_name}" (${existing.deck_id})`);
81
+ console.log('Delete .slidenerds.json to unlink.');
82
+ return;
83
+ }
84
+ const creds = await getCredentials();
85
+ if (!creds) {
86
+ console.error('Not logged in. Run `slidenerds login` first.');
87
+ process.exit(1);
88
+ }
89
+ try {
90
+ const serviceUrl = await getServiceUrl();
91
+ const { deckId, deckName } = await linkProject({
92
+ name: options.name,
93
+ deckId: options.deckId,
94
+ serviceUrl,
95
+ accessToken: creds.access_token,
96
+ });
97
+ console.log(`Linked to "${deckName}" (${deckId})`);
98
+ console.log('Run `slidenerds push` to upload your deck.');
99
+ }
100
+ catch (err) {
101
+ console.error(`Link failed: ${err instanceof Error ? err.message : err}`);
102
+ process.exit(1);
103
+ }
104
+ });
105
+ };
106
+ //# sourceMappingURL=link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link.js","sourceRoot":"","sources":["../../../src/cli/commands/link.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { Command } from 'commander';
2
+ export declare const loginFlow: (serviceUrl: string) => Promise<boolean>;
3
+ export declare const registerLoginCommand: (program: Command) => void;
4
+ //# sourceMappingURL=login.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AA2BnC,eAAO,MAAM,SAAS,GAAU,YAAY,MAAM,KAAG,OAAO,CAAC,OAAO,CAmDnE,CAAA;AAED,eAAO,MAAM,oBAAoB,GAAI,SAAS,OAAO,KAAG,IAsBvD,CAAA"}
@@ -0,0 +1,92 @@
1
+ import http from 'node:http';
2
+ import { execFile } from 'node:child_process';
3
+ import { saveCredentials, clearCredentials } from './config.js';
4
+ const findOpenPort = () => {
5
+ return new Promise((resolve, reject) => {
6
+ const server = http.createServer();
7
+ server.listen(0, () => {
8
+ const addr = server.address();
9
+ if (addr && typeof addr === 'object') {
10
+ const port = addr.port;
11
+ server.close(() => resolve(port));
12
+ }
13
+ else {
14
+ reject(new Error('Could not find open port'));
15
+ }
16
+ });
17
+ });
18
+ };
19
+ const openBrowser = (url) => {
20
+ const cmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
21
+ execFile(cmd, [url], () => {
22
+ // Ignore errors -- user can open manually
23
+ });
24
+ };
25
+ export const loginFlow = async (serviceUrl) => {
26
+ const port = await findOpenPort();
27
+ const callbackUrl = `http://localhost:${port}/callback`;
28
+ return new Promise((resolve) => {
29
+ const timeout = setTimeout(() => {
30
+ console.log('Login timed out.');
31
+ server.close();
32
+ resolve(false);
33
+ }, 5 * 60 * 1000);
34
+ const finish = (success) => {
35
+ clearTimeout(timeout);
36
+ server.close();
37
+ resolve(success);
38
+ };
39
+ const server = http.createServer(async (req, res) => {
40
+ const url = new URL(req.url ?? '/', `http://localhost:${port}`);
41
+ if (url.pathname === '/callback') {
42
+ const accessToken = url.searchParams.get('access_token');
43
+ const refreshToken = url.searchParams.get('refresh_token');
44
+ if (accessToken) {
45
+ await saveCredentials({
46
+ access_token: accessToken,
47
+ refresh_token: refreshToken ?? '',
48
+ url: serviceUrl,
49
+ });
50
+ res.writeHead(200, { 'Content-Type': 'text/html' });
51
+ res.end('<html><body><h1>Logged in to SlideNerds</h1><p>You can close this window.</p></body></html>');
52
+ finish(true);
53
+ }
54
+ else {
55
+ res.writeHead(400, { 'Content-Type': 'text/html' });
56
+ res.end('<html><body><h1>Login failed</h1><p>No token received.</p></body></html>');
57
+ finish(false);
58
+ }
59
+ }
60
+ });
61
+ server.listen(port, () => {
62
+ const loginUrl = `${serviceUrl}/cli/auth?callback=${encodeURIComponent(callbackUrl)}`;
63
+ console.log(`\nOpening browser to log in...\n`);
64
+ console.log(`If the browser doesn't open, visit:\n${loginUrl}\n`);
65
+ openBrowser(loginUrl);
66
+ });
67
+ });
68
+ };
69
+ export const registerLoginCommand = (program) => {
70
+ program
71
+ .command('login')
72
+ .description('Authenticate with slidenerds.com')
73
+ .option('--url <url>', 'Service URL', 'https://slidenerds.com')
74
+ .action(async (options) => {
75
+ const success = await loginFlow(options.url);
76
+ if (success) {
77
+ console.log('Logged in successfully.');
78
+ }
79
+ else {
80
+ console.error('Login failed.');
81
+ process.exit(1);
82
+ }
83
+ });
84
+ program
85
+ .command('logout')
86
+ .description('Clear stored credentials')
87
+ .action(async () => {
88
+ await clearCredentials();
89
+ console.log('Logged out.');
90
+ });
91
+ };
92
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../../src/cli/commands/login.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { Command } from 'commander';
2
+ export declare const pushDeck: (options: {
3
+ dir: string;
4
+ deckId: string;
5
+ serviceUrl: string;
6
+ accessToken: string;
7
+ skipBuild?: boolean;
8
+ }) => Promise<{
9
+ version: number;
10
+ url: string;
11
+ }>;
12
+ export declare const registerPushCommand: (program: Command) => void;
13
+ //# sourceMappingURL=push.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/push.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAsGnC,eAAO,MAAM,QAAQ,GAAU,SAAS;IACtC,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,KAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CA0C3C,CAAA;AAED,eAAO,MAAM,mBAAmB,GAAI,SAAS,OAAO,KAAG,IAqCtD,CAAA"}
@@ -0,0 +1,156 @@
1
+ import path from 'node:path';
2
+ import fs from 'fs-extra';
3
+ import { execFile } from 'node:child_process';
4
+ import { getCredentials, getProjectConfig, getServiceUrl } from './config.js';
5
+ const execAsync = (cmd, args, cwd) => {
6
+ return new Promise((resolve, reject) => {
7
+ execFile(cmd, args, { cwd, maxBuffer: 10 * 1024 * 1024 }, (err, stdout, stderr) => {
8
+ if (err)
9
+ reject(err);
10
+ else
11
+ resolve({ stdout, stderr });
12
+ });
13
+ });
14
+ };
15
+ const buildProject = async (dir) => {
16
+ console.log('Building project...');
17
+ // Check if next.config has output: 'export' or if we need to add it
18
+ const nextConfigPath = path.join(dir, 'next.config.ts');
19
+ const nextConfigJsPath = path.join(dir, 'next.config.js');
20
+ const nextConfigMjsPath = path.join(dir, 'next.config.mjs');
21
+ const configPath = (await fs.pathExists(nextConfigPath))
22
+ ? nextConfigPath
23
+ : (await fs.pathExists(nextConfigJsPath))
24
+ ? nextConfigJsPath
25
+ : (await fs.pathExists(nextConfigMjsPath))
26
+ ? nextConfigMjsPath
27
+ : null;
28
+ if (!configPath) {
29
+ throw new Error('No next.config.ts/js/mjs found. Is this a Next.js project?');
30
+ }
31
+ const configContent = await fs.readFile(configPath, 'utf-8');
32
+ const hasExportOutput = /output\s*:\s*['"]export['"]/.test(configContent);
33
+ // Temporarily add output: 'export' if not present
34
+ let restoreConfig = null;
35
+ if (!hasExportOutput) {
36
+ const backup = configContent;
37
+ const modified = configContent.replace(/const\s+nextConfig\s*:\s*NextConfig\s*=\s*\{/, "const nextConfig: NextConfig = {\n output: 'export',");
38
+ if (modified === configContent) {
39
+ // Fallback: try the JS pattern
40
+ const modifiedJs = configContent.replace(/const\s+nextConfig\s*=\s*\{/, "const nextConfig = {\n output: 'export',");
41
+ if (modifiedJs !== configContent) {
42
+ await fs.writeFile(configPath, modifiedJs);
43
+ restoreConfig = async () => fs.writeFile(configPath, backup);
44
+ }
45
+ }
46
+ else {
47
+ await fs.writeFile(configPath, modified);
48
+ restoreConfig = async () => fs.writeFile(configPath, backup);
49
+ }
50
+ }
51
+ try {
52
+ // Find npx path
53
+ const npxCmd = process.platform === 'win32' ? 'npx.cmd' : 'npx';
54
+ await execAsync(npxCmd, ['next', 'build'], dir);
55
+ }
56
+ finally {
57
+ if (restoreConfig)
58
+ await restoreConfig();
59
+ }
60
+ const outDir = path.join(dir, 'out');
61
+ if (!(await fs.pathExists(outDir))) {
62
+ throw new Error('Build did not produce an "out" directory. Make sure next.config has output: "export".');
63
+ }
64
+ return outDir;
65
+ };
66
+ const createZipFromDir = async (sourceDir) => {
67
+ // Use tar + gzip as a cross-platform zip alternative
68
+ // We'll create the zip using the archiver pattern with just fs
69
+ // For simplicity, use the system zip command
70
+ const zipPath = path.join(path.dirname(sourceDir), 'slidenerds-push.zip');
71
+ // Clean up any previous zip
72
+ if (await fs.pathExists(zipPath))
73
+ await fs.remove(zipPath);
74
+ const zipCmd = process.platform === 'win32' ? 'tar' : 'zip';
75
+ const zipArgs = process.platform === 'win32'
76
+ ? ['-cf', zipPath, '-C', sourceDir, '.']
77
+ : ['-r', zipPath, '.'];
78
+ const zipCwd = process.platform === 'win32' ? sourceDir : sourceDir;
79
+ await execAsync(zipCmd, zipArgs, zipCwd);
80
+ const buffer = await fs.readFile(zipPath);
81
+ await fs.remove(zipPath);
82
+ return buffer;
83
+ };
84
+ export const pushDeck = async (options) => {
85
+ const { dir, deckId, serviceUrl, accessToken, skipBuild } = options;
86
+ // Build the project
87
+ let outDir;
88
+ if (skipBuild) {
89
+ outDir = path.join(dir, 'out');
90
+ if (!(await fs.pathExists(outDir))) {
91
+ throw new Error('No "out" directory found. Run `next build` first or remove --skip-build.');
92
+ }
93
+ }
94
+ else {
95
+ outDir = await buildProject(dir);
96
+ }
97
+ // Create zip
98
+ console.log('Packaging...');
99
+ const zipBuffer = await createZipFromDir(outDir);
100
+ const sizeMb = (zipBuffer.length / 1024 / 1024).toFixed(1);
101
+ console.log(`Package size: ${sizeMb} MB`);
102
+ // Upload
103
+ console.log('Uploading...');
104
+ const formData = new FormData();
105
+ formData.append('file', new Blob([zipBuffer.buffer], { type: 'application/zip' }), 'deck.zip');
106
+ const resp = await fetch(`${serviceUrl}/api/decks/${deckId}/push`, {
107
+ method: 'POST',
108
+ headers: { Authorization: `Bearer ${accessToken}` },
109
+ body: formData,
110
+ });
111
+ if (!resp.ok) {
112
+ const err = await resp.json().catch(() => ({ error: `HTTP ${resp.status}` }));
113
+ throw new Error(err.error ?? `Upload failed: ${resp.status}`);
114
+ }
115
+ const result = await resp.json();
116
+ return {
117
+ version: result.version,
118
+ url: `${serviceUrl}/d/${result.deck?.slug ?? deckId}`,
119
+ };
120
+ };
121
+ export const registerPushCommand = (program) => {
122
+ program
123
+ .command('push')
124
+ .description('Build and upload your deck to slidenerds.com')
125
+ .option('--skip-build', 'Skip the build step (use existing out/ directory)')
126
+ .action(async (options) => {
127
+ const dir = process.cwd();
128
+ const config = await getProjectConfig(dir);
129
+ if (!config) {
130
+ console.error('Project not linked. Run `slidenerds link` first.');
131
+ process.exit(1);
132
+ }
133
+ const creds = await getCredentials();
134
+ if (!creds) {
135
+ console.error('Not logged in. Run `slidenerds login` first.');
136
+ process.exit(1);
137
+ }
138
+ try {
139
+ const serviceUrl = await getServiceUrl();
140
+ const { version, url } = await pushDeck({
141
+ dir,
142
+ deckId: config.deck_id,
143
+ serviceUrl,
144
+ accessToken: creds.access_token,
145
+ skipBuild: options.skipBuild,
146
+ });
147
+ console.log(`\nPushed version ${version}`);
148
+ console.log(`View at: ${url}\n`);
149
+ }
150
+ catch (err) {
151
+ console.error(`Push failed: ${err instanceof Error ? err.message : err}`);
152
+ process.exit(1);
153
+ }
154
+ });
155
+ };
156
+ //# sourceMappingURL=push.js.map