create-marp-presentation 1.1.0 → 1.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.
- package/README.md +45 -37
- package/index.js +111 -164
- package/lib/add-themes-command.js +381 -0
- package/lib/errors.js +62 -0
- package/lib/frontmatter.js +71 -0
- package/lib/prompts.js +222 -0
- package/lib/theme-manager.js +238 -0
- package/lib/theme-resolver.js +227 -0
- package/lib/vscode-integration.js +198 -0
- package/package.json +13 -5
- package/template/README.md +28 -17
- package/template/package.json +13 -1
- package/template/scripts/theme-cli.js +248 -0
- package/themes/beam/beam.css +141 -0
- package/themes/default-clean/default-clean.css +57 -0
- package/themes/gaia-dark/gaia-dark.css +27 -0
- package/themes/marpx/marpx.css +1735 -0
- package/themes/marpx/socrates.css +105 -0
- package/themes/uncover-minimal/uncover-minimal.css +22 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
// lib/vscode-integration.js
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const { VSCodeIntegrationError } = require('./errors');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* DTO for VSCode settings structure
|
|
8
|
+
* Encapsulates the settings format and provides type-safe accessors
|
|
9
|
+
*/
|
|
10
|
+
class VSCodeSettingsDTO {
|
|
11
|
+
/**
|
|
12
|
+
* @param {Object} settings - Raw settings object
|
|
13
|
+
*/
|
|
14
|
+
constructor(settings = {}) {
|
|
15
|
+
this._settings = settings;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Get Marp themes array
|
|
20
|
+
* Supports both flat key "markdown.marp.themes" and nested markdown.marp.themes
|
|
21
|
+
* @returns {string[]} Array of theme file paths
|
|
22
|
+
*/
|
|
23
|
+
getMarpThemes() {
|
|
24
|
+
// Support both flat key and nested structure for backward compatibility
|
|
25
|
+
if (this._settings['markdown.marp.themes']) {
|
|
26
|
+
return this._settings['markdown.marp.themes'];
|
|
27
|
+
}
|
|
28
|
+
return this._settings.markdown?.marp?.themes || [];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Set Marp themes array
|
|
33
|
+
* Always uses flat key format for VSCode
|
|
34
|
+
* @param {string[]} themes - Array of theme file paths
|
|
35
|
+
*/
|
|
36
|
+
setMarpThemes(themes) {
|
|
37
|
+
// Always use flat key format for VSCode
|
|
38
|
+
this._settings['markdown.marp.themes'] = themes;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Merge another settings object into this one
|
|
43
|
+
* Combines themes without duplicates
|
|
44
|
+
* @param {VSCodeSettingsDTO} other - Other settings to merge
|
|
45
|
+
*/
|
|
46
|
+
merge(other) {
|
|
47
|
+
const otherThemes = other.getMarpThemes();
|
|
48
|
+
const currentThemes = this.getMarpThemes();
|
|
49
|
+
const merged = [...new Set([...currentThemes, ...otherThemes])];
|
|
50
|
+
this.setMarpThemes(merged);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Convert to plain object for JSON serialization
|
|
55
|
+
* @returns {Object} Plain settings object
|
|
56
|
+
*/
|
|
57
|
+
toObject() {
|
|
58
|
+
return { ...this._settings };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Manages VSCode settings integration for Marp themes
|
|
64
|
+
* Handles .vscode/settings.json for markdown.marp.themes configuration
|
|
65
|
+
*/
|
|
66
|
+
class VSCodeIntegration {
|
|
67
|
+
/**
|
|
68
|
+
* @param {string} projectPath - Path to the project root
|
|
69
|
+
*/
|
|
70
|
+
constructor(projectPath) {
|
|
71
|
+
if (!projectPath) {
|
|
72
|
+
throw new VSCodeIntegrationError('Project path is required');
|
|
73
|
+
}
|
|
74
|
+
this.projectPath = projectPath;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Returns the path to .vscode/settings.json
|
|
79
|
+
* @returns {string} Absolute path to settings.json
|
|
80
|
+
*/
|
|
81
|
+
getSettingsPath() {
|
|
82
|
+
return path.join(this.projectPath, '.vscode', 'settings.json');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Reads settings.json from .vscode directory
|
|
87
|
+
* Returns empty object if file doesn't exist
|
|
88
|
+
* Creates backup of corrupted JSON files
|
|
89
|
+
*
|
|
90
|
+
* @returns {Object} Parsed settings object
|
|
91
|
+
* @throws {VSCodeIntegrationError} If backup creation fails
|
|
92
|
+
*/
|
|
93
|
+
readSettings() {
|
|
94
|
+
const settingsPath = this.getSettingsPath();
|
|
95
|
+
|
|
96
|
+
// Return empty object if file doesn't exist
|
|
97
|
+
if (!fs.existsSync(settingsPath)) {
|
|
98
|
+
return {};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
const content = fs.readFileSync(settingsPath, 'utf-8');
|
|
103
|
+
const settings = JSON.parse(content);
|
|
104
|
+
return settings;
|
|
105
|
+
} catch (error) {
|
|
106
|
+
if (error instanceof SyntaxError) {
|
|
107
|
+
// Create backup of corrupted file
|
|
108
|
+
this._backupCorruptedSettings(settingsPath);
|
|
109
|
+
return {};
|
|
110
|
+
}
|
|
111
|
+
throw new VSCodeIntegrationError(`Failed to read settings: ${error.message}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Writes settings to .vscode/settings.json
|
|
117
|
+
* Creates .vscode directory if it doesn't exist
|
|
118
|
+
*
|
|
119
|
+
* @param {Object} settings - Settings object to write
|
|
120
|
+
* @throws {VSCodeIntegrationError} If directory creation or write fails
|
|
121
|
+
*/
|
|
122
|
+
writeSettings(settings) {
|
|
123
|
+
const vscodeDir = path.join(this.projectPath, '.vscode');
|
|
124
|
+
const settingsPath = this.getSettingsPath();
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
// Create .vscode directory if it doesn't exist
|
|
128
|
+
if (!fs.existsSync(vscodeDir)) {
|
|
129
|
+
fs.mkdirSync(vscodeDir, { recursive: true });
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Write settings with pretty formatting (2-space indent)
|
|
133
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf-8');
|
|
134
|
+
} catch (error) {
|
|
135
|
+
throw new VSCodeIntegrationError(`Failed to write settings: ${error.message}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Creates a new DTO instance from current settings
|
|
141
|
+
* @returns {VSCodeSettingsDTO} DTO instance
|
|
142
|
+
*/
|
|
143
|
+
createSettingsDTO() {
|
|
144
|
+
const settings = this.readSettings();
|
|
145
|
+
return new VSCodeSettingsDTO(settings);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Syncs Marp themes to VSCode settings
|
|
150
|
+
* Updates markdown.marp.themes array in settings.json
|
|
151
|
+
* Preserves existing settings while updating theme list
|
|
152
|
+
*
|
|
153
|
+
* @param {string[]} themes - Array of theme file paths
|
|
154
|
+
* @throws {VSCodeIntegrationError} If settings read/write fails
|
|
155
|
+
*/
|
|
156
|
+
syncThemes(themes) {
|
|
157
|
+
const dto = this.createSettingsDTO();
|
|
158
|
+
dto.setMarpThemes(themes);
|
|
159
|
+
this.writeSettings(dto.toObject());
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Reads and returns settings as DTO
|
|
164
|
+
* @returns {VSCodeSettingsDTO} DTO instance
|
|
165
|
+
*/
|
|
166
|
+
readSettingsAsDTO() {
|
|
167
|
+
const settings = this.readSettings();
|
|
168
|
+
return new VSCodeSettingsDTO(settings);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Creates a backup of corrupted settings.json
|
|
173
|
+
* Backup filename: settings.json.corrupted.YYYYMMDD-HHMMSS
|
|
174
|
+
*
|
|
175
|
+
* @private
|
|
176
|
+
* @param {string} settingsPath - Path to corrupted settings file
|
|
177
|
+
* @throws {VSCodeIntegrationError} If backup creation fails
|
|
178
|
+
*/
|
|
179
|
+
_backupCorruptedSettings(settingsPath) {
|
|
180
|
+
try {
|
|
181
|
+
const timestamp = new Date()
|
|
182
|
+
.toISOString()
|
|
183
|
+
.replace(/[:.]/g, '-')
|
|
184
|
+
.replace('T', '-')
|
|
185
|
+
.slice(0, 19);
|
|
186
|
+
const backupPath = `${settingsPath}.corrupted.${timestamp}`;
|
|
187
|
+
|
|
188
|
+
fs.copyFileSync(settingsPath, backupPath);
|
|
189
|
+
console.warn(`Corrupted settings.json backed up to: ${backupPath}`);
|
|
190
|
+
} catch (error) {
|
|
191
|
+
throw new VSCodeIntegrationError(
|
|
192
|
+
`Failed to create backup of corrupted settings: ${error.message}`
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
module.exports = { VSCodeIntegration, VSCodeSettingsDTO };
|
package/package.json
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-marp-presentation",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Create a new Marp presentation project with one command",
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "Create a new Marp presentation project with one command. Manage themes quickly and easily with the CLI.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"create-marp-presentation": "index.js"
|
|
7
7
|
},
|
|
8
8
|
"files": [
|
|
9
9
|
"index.js",
|
|
10
|
+
"lib/",
|
|
10
11
|
"template/",
|
|
11
|
-
"template-optional/"
|
|
12
|
+
"template-optional/",
|
|
13
|
+
"themes/"
|
|
12
14
|
],
|
|
13
15
|
"scripts": {
|
|
14
16
|
"test": "jest"
|
|
@@ -38,10 +40,16 @@
|
|
|
38
40
|
"testEnvironment": "node",
|
|
39
41
|
"testMatch": [
|
|
40
42
|
"**/tests/**/*.test.js"
|
|
41
|
-
]
|
|
43
|
+
],
|
|
44
|
+
"maxWorkers": 1,
|
|
45
|
+
"setupFilesAfterEnv": ["<rootDir>/jest.setup.js"]
|
|
42
46
|
},
|
|
43
|
-
"
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@inquirer/prompts": "^7.10.1",
|
|
44
49
|
"fast-glob": "^3.3.3",
|
|
50
|
+
"gray-matter": "^4.0.3"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
45
53
|
"jest": "^29.7.0"
|
|
46
54
|
}
|
|
47
55
|
}
|
package/template/README.md
CHANGED
|
@@ -2,36 +2,47 @@
|
|
|
2
2
|
|
|
3
3
|
A template for creating presentations with Marp.
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
### Live Preview
|
|
5
|
+
## Quick Start
|
|
8
6
|
|
|
9
7
|
```bash
|
|
10
8
|
npm run dev
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
Opens a browser with auto-reload on changes.
|
|
11
|
+
Opens a browser with live preview and auto-reload on changes.
|
|
12
|
+
|
|
13
|
+
Edit `presentation.md` to create your slides.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
## Building
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm run build:html # HTML presentation (interactive)
|
|
19
|
+
npm run build:pdf # PDF file
|
|
20
|
+
npm run build:pptx # PowerPoint
|
|
21
|
+
npm run build:all # All formats at once
|
|
22
|
+
```
|
|
16
23
|
|
|
17
|
-
|
|
24
|
+
The output appears in the `output/` folder.
|
|
18
25
|
|
|
19
|
-
##
|
|
26
|
+
## Theme Management
|
|
20
27
|
|
|
21
28
|
```bash
|
|
22
|
-
npm run
|
|
23
|
-
npm run
|
|
24
|
-
npm run
|
|
25
|
-
npm run
|
|
29
|
+
npm run theme:add # Add themes from library
|
|
30
|
+
npm run theme:list # List available/installed themes
|
|
31
|
+
npm run theme:set <name> # Set active theme
|
|
32
|
+
npm run theme:create # Create custom theme
|
|
26
33
|
```
|
|
27
34
|
|
|
28
|
-
|
|
35
|
+
For detailed theme management guide, see [docs/theme-management.md](docs/theme-management.md).
|
|
29
36
|
|
|
30
|
-
##
|
|
37
|
+
## VSCode Setup
|
|
31
38
|
|
|
32
|
-
|
|
39
|
+
1. Install [Marp for VSCode](https://marketplace.visualstudio.com/items?itemName=marp-team.marp-vscode)
|
|
40
|
+
2. Open this project in VSCode
|
|
41
|
+
3. Click "Marp: Open Preview" in the editor toolbar
|
|
42
|
+
|
|
43
|
+
## Static Files
|
|
33
44
|
|
|
34
|
-
|
|
45
|
+
Place images and other files in `static/` folder.
|
|
35
46
|
|
|
36
47
|
You can add additional folders in `marp.config.js`:
|
|
37
48
|
|
|
@@ -42,7 +53,7 @@ module.exports = {
|
|
|
42
53
|
};
|
|
43
54
|
```
|
|
44
55
|
|
|
45
|
-
## Cleaning
|
|
56
|
+
## Cleaning
|
|
46
57
|
|
|
47
58
|
```bash
|
|
48
59
|
npm run clean
|
|
@@ -54,4 +65,4 @@ Removes the `output/` folder.
|
|
|
54
65
|
|
|
55
66
|
- [Marp Documentation](https://marp.app/)
|
|
56
67
|
- [Marp CLI](https://github.com/marp-team/marp-cli)
|
|
57
|
-
- [
|
|
68
|
+
- [Theme Management Guide](docs/theme-management.md)
|
package/template/package.json
CHANGED
|
@@ -9,7 +9,16 @@
|
|
|
9
9
|
"build:pptx": "marp presentation.md -o output/presentation.pptx --allow-local-files && npm run copy:static",
|
|
10
10
|
"build:all": "npm run build:html && npm run build:pdf && npm run build:pptx",
|
|
11
11
|
"copy:static": "node scripts/copy-static.js",
|
|
12
|
-
"clean": "rimraf output"
|
|
12
|
+
"clean": "rimraf output",
|
|
13
|
+
"theme": "node scripts/theme-cli.js",
|
|
14
|
+
"theme:add": "npx create-marp-presentation theme:add .",
|
|
15
|
+
"theme:list": "node scripts/theme-cli.js list",
|
|
16
|
+
"theme:create": "node scripts/theme-cli.js create",
|
|
17
|
+
"theme:set": "node scripts/theme-cli.js set"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@inquirer/prompts": "^7.10.1",
|
|
21
|
+
"gray-matter": "^4.0.3"
|
|
13
22
|
},
|
|
14
23
|
"devDependencies": {
|
|
15
24
|
"@marp-team/marp-cli": "^4.1.2",
|
|
@@ -18,5 +27,8 @@
|
|
|
18
27
|
},
|
|
19
28
|
"engines": {
|
|
20
29
|
"node": ">=20.0.0"
|
|
30
|
+
},
|
|
31
|
+
"marp": {
|
|
32
|
+
"themeSet": "./themes"
|
|
21
33
|
}
|
|
22
34
|
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* theme-cli.js - CLI for managing Marp themes in a project
|
|
5
|
+
*
|
|
6
|
+
* This script provides commands to:
|
|
7
|
+
* - List installed themes
|
|
8
|
+
* - Create new themes
|
|
9
|
+
* - Update presentation.md with selected theme
|
|
10
|
+
* - Sync themes to VSCode settings
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
// Import lib modules
|
|
17
|
+
const {
|
|
18
|
+
ThemeResolver,
|
|
19
|
+
Theme
|
|
20
|
+
} = require('./lib/theme-resolver');
|
|
21
|
+
const { ThemeManager } = require('./lib/theme-manager');
|
|
22
|
+
const { Prompts } = require('./lib/prompts');
|
|
23
|
+
const { Frontmatter } = require('./lib/frontmatter');
|
|
24
|
+
const { VSCodeIntegration } = require('./lib/vscode-integration');
|
|
25
|
+
const {
|
|
26
|
+
ThemeError,
|
|
27
|
+
ThemeNotFoundError,
|
|
28
|
+
ThemeAlreadyExistsError,
|
|
29
|
+
PresentationNotFoundError
|
|
30
|
+
} = require('./lib/errors');
|
|
31
|
+
|
|
32
|
+
// Get command from arguments
|
|
33
|
+
const command = process.argv[2] || 'help';
|
|
34
|
+
const args = process.argv.slice(3);
|
|
35
|
+
|
|
36
|
+
// Paths
|
|
37
|
+
const projectRoot = process.cwd();
|
|
38
|
+
const templatePath = path.join(__dirname, '..');
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Show help message
|
|
42
|
+
*/
|
|
43
|
+
function showHelp() {
|
|
44
|
+
console.log(`
|
|
45
|
+
Marp Theme CLI
|
|
46
|
+
|
|
47
|
+
Usage:
|
|
48
|
+
npm run theme <command> [options]
|
|
49
|
+
|
|
50
|
+
Commands:
|
|
51
|
+
list List installed themes
|
|
52
|
+
create <name> Create a new theme
|
|
53
|
+
set <theme> Set active theme in presentation.md
|
|
54
|
+
switch <theme> Alias for 'set' - change active theme
|
|
55
|
+
sync Sync installed themes to VSCode settings
|
|
56
|
+
help Show this help message
|
|
57
|
+
|
|
58
|
+
Options:
|
|
59
|
+
--force Overwrite existing themes (for theme:add)
|
|
60
|
+
|
|
61
|
+
Examples:
|
|
62
|
+
npm run theme list
|
|
63
|
+
npm run theme create my-theme
|
|
64
|
+
npm run theme set beam
|
|
65
|
+
npm run theme sync
|
|
66
|
+
|
|
67
|
+
Note:
|
|
68
|
+
Run "npm run theme:add" to add themes from the theme library.
|
|
69
|
+
`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* List installed themes
|
|
74
|
+
*/
|
|
75
|
+
async function listThemes() {
|
|
76
|
+
console.log('\n=== Installed Themes ===\n');
|
|
77
|
+
|
|
78
|
+
const projectThemesPath = path.join(projectRoot, 'themes');
|
|
79
|
+
let installedThemes = [];
|
|
80
|
+
|
|
81
|
+
if (fs.existsSync(projectThemesPath)) {
|
|
82
|
+
try {
|
|
83
|
+
installedThemes = ThemeResolver.scanDirectory(projectThemesPath);
|
|
84
|
+
} catch (error) {
|
|
85
|
+
// Directory exists but no themes
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (installedThemes.length === 0) {
|
|
90
|
+
console.log(' No themes installed.');
|
|
91
|
+
console.log(' Run "npm run theme:add" to install themes.\n');
|
|
92
|
+
} else {
|
|
93
|
+
for (const theme of installedThemes) {
|
|
94
|
+
const deps = theme.dependencies.length > 0
|
|
95
|
+
? ` (depends on: ${theme.dependencies.join(', ')})`
|
|
96
|
+
: '';
|
|
97
|
+
console.log(` ${theme.name}${deps}`);
|
|
98
|
+
}
|
|
99
|
+
console.log();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Create a new theme
|
|
105
|
+
*/
|
|
106
|
+
async function createTheme(themeName) {
|
|
107
|
+
if (!themeName) {
|
|
108
|
+
console.error('\nError: Theme name is required.\n');
|
|
109
|
+
console.log('Usage: npm run theme create <theme-name>\n');
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const manager = new ThemeManager(projectRoot);
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
console.log(`\nCreating theme: ${themeName}\n`);
|
|
117
|
+
|
|
118
|
+
// Prompt for parent theme - need objects with isSystem property
|
|
119
|
+
const scannedThemes = manager.scanThemes();
|
|
120
|
+
const systemThemeObjects = ThemeManager.SYSTEM_THEMES.map(name => ({
|
|
121
|
+
name,
|
|
122
|
+
isSystem: true
|
|
123
|
+
}));
|
|
124
|
+
const allThemes = [...scannedThemes, ...systemThemeObjects];
|
|
125
|
+
const parentTheme = await Prompts.promptParentTheme(allThemes);
|
|
126
|
+
|
|
127
|
+
// Prompt for directory location
|
|
128
|
+
const existingDirs = manager.listDirectories();
|
|
129
|
+
const location = await Prompts.promptDirectoryLocation(existingDirs);
|
|
130
|
+
|
|
131
|
+
let newFolderName = null;
|
|
132
|
+
if (location === 'new') {
|
|
133
|
+
newFolderName = await Prompts.promptNewFolderName();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const result = manager.createTheme(
|
|
137
|
+
themeName,
|
|
138
|
+
parentTheme,
|
|
139
|
+
location,
|
|
140
|
+
newFolderName
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
console.log(`\n✓ Theme created: ${result.path}\n`);
|
|
144
|
+
console.log('Next steps:');
|
|
145
|
+
console.log(` 1. Edit ${result.path} to customize the theme`);
|
|
146
|
+
console.log(` 2. Run "npm run theme set ${themeName}" to use it in your presentation\n`);
|
|
147
|
+
} catch (error) {
|
|
148
|
+
console.error(`\nError: ${error.message}\n`);
|
|
149
|
+
process.exit(1);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Sync VSCode settings with installed themes
|
|
155
|
+
*/
|
|
156
|
+
async function syncThemes() {
|
|
157
|
+
const projectThemesPath = path.join(projectRoot, 'themes');
|
|
158
|
+
let installedThemes = [];
|
|
159
|
+
|
|
160
|
+
if (fs.existsSync(projectThemesPath)) {
|
|
161
|
+
try {
|
|
162
|
+
installedThemes = ThemeResolver.scanDirectory(projectThemesPath);
|
|
163
|
+
} catch (error) {
|
|
164
|
+
// Directory exists but no themes
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (installedThemes.length === 0) {
|
|
169
|
+
console.log('\nNo themes installed to sync.\n');
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Build theme paths for VSCode
|
|
174
|
+
const themePaths = installedThemes.map(theme => {
|
|
175
|
+
// For themes in subdirectories, use themes/subdir/theme.css
|
|
176
|
+
// For themes at root level, use themes/theme.css
|
|
177
|
+
if (theme.path.includes(`${path.sep}themes${path.sep}`)) {
|
|
178
|
+
const relativePath = theme.path.split(`${path.sep}themes${path.sep}`)[1];
|
|
179
|
+
return `themes/${relativePath}`;
|
|
180
|
+
}
|
|
181
|
+
return `themes/${theme.name}.css`;
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// Sync with VSCode
|
|
185
|
+
const vscode = new VSCodeIntegration(projectRoot);
|
|
186
|
+
vscode.syncThemes(themePaths);
|
|
187
|
+
|
|
188
|
+
console.log(`\n✓ Synced ${themePaths.length} theme(s) to VSCode settings:`);
|
|
189
|
+
themePaths.forEach(p => console.log(` - ${p}`));
|
|
190
|
+
console.log();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Main CLI entry point
|
|
195
|
+
*/
|
|
196
|
+
async function main() {
|
|
197
|
+
switch (command) {
|
|
198
|
+
case 'list':
|
|
199
|
+
await listThemes();
|
|
200
|
+
break;
|
|
201
|
+
|
|
202
|
+
case 'create':
|
|
203
|
+
await createTheme(args[0]);
|
|
204
|
+
break;
|
|
205
|
+
|
|
206
|
+
case 'set':
|
|
207
|
+
case 'switch':
|
|
208
|
+
try {
|
|
209
|
+
const themeManager = new ThemeManager(projectRoot);
|
|
210
|
+
themeManager.setActiveTheme(args[0]);
|
|
211
|
+
console.log(`\n✓ Theme set to "${args[0]}" in presentation.md`);
|
|
212
|
+
|
|
213
|
+
// VSCode integration - sync ALL themes in project
|
|
214
|
+
themeManager.updateVSCodeSettings();
|
|
215
|
+
|
|
216
|
+
console.log('Next steps:');
|
|
217
|
+
console.log(' npm run dev # Start live preview\n');
|
|
218
|
+
} catch (error) {
|
|
219
|
+
if (error.name === 'ThemeNotFoundError') {
|
|
220
|
+
console.error(`\nError: ${error.message}\n`);
|
|
221
|
+
console.log('Run "npm run theme list" to see available themes.\n');
|
|
222
|
+
process.exit(1);
|
|
223
|
+
}
|
|
224
|
+
if (error.name === 'PresentationNotFoundError') {
|
|
225
|
+
console.error(`\nError: presentation.md not found in ${projectRoot}\n`);
|
|
226
|
+
process.exit(1);
|
|
227
|
+
}
|
|
228
|
+
console.error(`\nError: ${error.message}\n`);
|
|
229
|
+
process.exit(1);
|
|
230
|
+
}
|
|
231
|
+
break;
|
|
232
|
+
|
|
233
|
+
case 'sync':
|
|
234
|
+
await syncThemes();
|
|
235
|
+
break;
|
|
236
|
+
|
|
237
|
+
case 'help':
|
|
238
|
+
default:
|
|
239
|
+
showHelp();
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Run CLI
|
|
245
|
+
main().catch(error => {
|
|
246
|
+
console.error(`\nUnexpected error: ${error.message}\n`);
|
|
247
|
+
process.exit(1);
|
|
248
|
+
});
|