termi-kids 0.2.3 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/surfaces/home.js +14 -164
- package/package.json +1 -1
package/dist/surfaces/home.js
CHANGED
|
@@ -12,9 +12,6 @@ import * as p from '@clack/prompts';
|
|
|
12
12
|
import { atomicWriteFileSync, termiHome } from '../config/paths.js';
|
|
13
13
|
import { saveSettings } from '../config/settings.js';
|
|
14
14
|
import { consumeGuardReadyNotice, guardFetchState, guardProgressBar, } from '../safety/guarddownload.js';
|
|
15
|
-
import { nameIsOkay } from '../safety/prefilter.js';
|
|
16
|
-
import { scaffolds, scaffoldById } from '../projects/scaffolds/index.js';
|
|
17
|
-
import { suggestProjectNames } from '../setup/wizard.js';
|
|
18
15
|
import { BADGES, celebrate, confetti } from '../ui/celebrate.js';
|
|
19
16
|
import { mascot } from '../ui/mascot.js';
|
|
20
17
|
import { style } from '../ui/theme.js';
|
|
@@ -99,174 +96,27 @@ export function recapFromTermiMd(text) {
|
|
|
99
96
|
}
|
|
100
97
|
return null;
|
|
101
98
|
}
|
|
102
|
-
async function
|
|
99
|
+
async function loadStore() {
|
|
103
100
|
try {
|
|
104
|
-
|
|
105
|
-
const create = await import('../projects/create.js');
|
|
106
|
-
return { store, create };
|
|
101
|
+
return await import('../projects/store.js');
|
|
107
102
|
}
|
|
108
103
|
catch {
|
|
109
104
|
return null;
|
|
110
105
|
}
|
|
111
106
|
}
|
|
112
|
-
const CUSTOM_NAME = '__custom__';
|
|
113
|
-
async function pickProjectName(themeLabel) {
|
|
114
|
-
const suggestions = suggestProjectNames(themeLabel);
|
|
115
|
-
const pick = await p.select({
|
|
116
|
-
message: 'Pick a name.',
|
|
117
|
-
options: [
|
|
118
|
-
...suggestions.map((name) => ({ value: name, label: name })),
|
|
119
|
-
{ value: CUSTOM_NAME, label: 'Type my own name' },
|
|
120
|
-
],
|
|
121
|
-
});
|
|
122
|
-
if (p.isCancel(pick)) {
|
|
123
|
-
return null;
|
|
124
|
-
}
|
|
125
|
-
if (pick !== CUSTOM_NAME) {
|
|
126
|
-
return pick;
|
|
127
|
-
}
|
|
128
|
-
const typed = await p.text({
|
|
129
|
-
message: 'What is its name?',
|
|
130
|
-
validate: (value) => {
|
|
131
|
-
const trimmed = (value ?? '').trim();
|
|
132
|
-
if (trimmed.length === 0)
|
|
133
|
-
return 'It needs a name.';
|
|
134
|
-
if (!nameIsOkay(trimmed))
|
|
135
|
-
return 'That name will not work. Try another one.';
|
|
136
|
-
return undefined;
|
|
137
|
-
},
|
|
138
|
-
});
|
|
139
|
-
if (p.isCancel(typed)) {
|
|
140
|
-
return null;
|
|
141
|
-
}
|
|
142
|
-
return typed.trim();
|
|
143
|
-
}
|
|
144
|
-
async function remixFlow(store, create) {
|
|
145
|
-
const metas = store.listProjects();
|
|
146
|
-
const sourceSlug = await p.select({
|
|
147
|
-
message: 'Which project do you want to remix?',
|
|
148
|
-
options: metas.map((m) => ({ value: m.slug, label: m.prettyName })),
|
|
149
|
-
});
|
|
150
|
-
if (p.isCancel(sourceSlug)) {
|
|
151
|
-
return null;
|
|
152
|
-
}
|
|
153
|
-
const source = store.openProject(sourceSlug);
|
|
154
|
-
if (source === null) {
|
|
155
|
-
p.log.warn('I could not open that one.');
|
|
156
|
-
return null;
|
|
157
|
-
}
|
|
158
|
-
for (;;) {
|
|
159
|
-
const name = await p.text({
|
|
160
|
-
message: 'Name your remix.',
|
|
161
|
-
validate: (value) => ((value ?? '').trim().length > 0 ? undefined : 'It needs a name.'),
|
|
162
|
-
});
|
|
163
|
-
if (p.isCancel(name)) {
|
|
164
|
-
return null;
|
|
165
|
-
}
|
|
166
|
-
const trimmed = name.trim();
|
|
167
|
-
if (create.slugifyName(trimmed).collision) {
|
|
168
|
-
p.log.warn('That name is taken. Try another one.');
|
|
169
|
-
continue;
|
|
170
|
-
}
|
|
171
|
-
const made = create.createProject(source.meta.scaffoldId, source.meta.themeId, trimmed);
|
|
172
|
-
for (const file of source.listKidFiles()) {
|
|
173
|
-
const content = source.readFile(file.relPath);
|
|
174
|
-
if (content !== null) {
|
|
175
|
-
made.project.writeFile(file.relPath, content);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
await awardBadge('remixer');
|
|
179
|
-
return made.project;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
107
|
/**
|
|
183
|
-
*
|
|
184
|
-
*
|
|
108
|
+
* New project entry: always Build a game (blank shell + idea list).
|
|
109
|
+
* Multi-scaffold stock starters are no longer on the kid path.
|
|
185
110
|
*/
|
|
186
111
|
export async function runNewProject(settings) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
p.log.warn('Project tools are not ready yet.');
|
|
191
|
-
return null;
|
|
192
|
-
}
|
|
193
|
-
const { store, create } = mods;
|
|
194
|
-
const existing = store.listProjects();
|
|
195
|
-
const options = scaffolds.map((s) => ({
|
|
196
|
-
value: s.id,
|
|
197
|
-
label: `${s.emoji} ${s.label}`,
|
|
198
|
-
hint: s.ageNote,
|
|
199
|
-
}));
|
|
200
|
-
if (existing.length > 0) {
|
|
201
|
-
options.push({
|
|
202
|
-
value: '__remix__',
|
|
203
|
-
label: 'Remix one of your projects',
|
|
204
|
-
hint: 'Copy a project and make it new.',
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
const pick = await p.select({ message: 'What do you want to make?', options });
|
|
208
|
-
if (p.isCancel(pick)) {
|
|
209
|
-
return null;
|
|
210
|
-
}
|
|
211
|
-
if (pick === '__remix__') {
|
|
212
|
-
return remixFlow(store, create);
|
|
213
|
-
}
|
|
214
|
-
const scaffold = scaffoldById(pick);
|
|
215
|
-
if (scaffold === undefined) {
|
|
216
|
-
return null;
|
|
217
|
-
}
|
|
218
|
-
const themeId = await p.select({
|
|
219
|
-
message: 'Pick a style.',
|
|
220
|
-
options: scaffold.themes.map((t) => ({ value: t.id, label: `${t.emoji} ${t.label}` })),
|
|
221
|
-
});
|
|
222
|
-
if (p.isCancel(themeId)) {
|
|
223
|
-
return null;
|
|
224
|
-
}
|
|
225
|
-
const theme = scaffold.themes.find((t) => t.id === themeId);
|
|
226
|
-
if (theme === undefined) {
|
|
227
|
-
return null;
|
|
112
|
+
try {
|
|
113
|
+
const build = await import('./buildGame.js');
|
|
114
|
+
return await build.runBuildGame(settings);
|
|
228
115
|
}
|
|
229
|
-
|
|
230
|
-
|
|
116
|
+
catch {
|
|
117
|
+
p.log.warn('Build a game is taking a nap. Try again soon.');
|
|
231
118
|
return null;
|
|
232
119
|
}
|
|
233
|
-
const slugInfo = create.slugifyName(prettyName);
|
|
234
|
-
if (slugInfo.collision) {
|
|
235
|
-
const what = await p.select({
|
|
236
|
-
message: 'You already have a project with that name.',
|
|
237
|
-
options: [
|
|
238
|
-
{ value: 'open', label: 'Open it' },
|
|
239
|
-
{ value: 'suffix', label: `Call this one ${prettyName} 2` },
|
|
240
|
-
{ value: 'rename', label: 'Pick a new name' },
|
|
241
|
-
],
|
|
242
|
-
});
|
|
243
|
-
if (p.isCancel(what)) {
|
|
244
|
-
return null;
|
|
245
|
-
}
|
|
246
|
-
if (what === 'open') {
|
|
247
|
-
return store.openProject(slugInfo.slug);
|
|
248
|
-
}
|
|
249
|
-
if (what === 'suffix') {
|
|
250
|
-
prettyName = `${prettyName} 2`;
|
|
251
|
-
}
|
|
252
|
-
else {
|
|
253
|
-
const again = await pickProjectName(theme.label);
|
|
254
|
-
if (again === null) {
|
|
255
|
-
return null;
|
|
256
|
-
}
|
|
257
|
-
prettyName = again;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
const made = create.createProject(scaffold.id, theme.id, prettyName);
|
|
261
|
-
await awardBadge('first-project');
|
|
262
|
-
if (store.listProjects().length >= 5) {
|
|
263
|
-
await awardBadge('five-projects');
|
|
264
|
-
}
|
|
265
|
-
const starters = made.starterPrompts.slice(0, 2);
|
|
266
|
-
if (starters.length > 0) {
|
|
267
|
-
p.note(starters.map((line) => `- ${line}`).join('\n'), 'Try saying');
|
|
268
|
-
}
|
|
269
|
-
return made.project;
|
|
270
120
|
}
|
|
271
121
|
/**
|
|
272
122
|
* Opens the chat for a project and keeps going while the kid asks for
|
|
@@ -445,12 +295,12 @@ export async function showHome(settings) {
|
|
|
445
295
|
}
|
|
446
296
|
}
|
|
447
297
|
async function openPicked(settings) {
|
|
448
|
-
const
|
|
449
|
-
if (
|
|
298
|
+
const store = await loadStore();
|
|
299
|
+
if (store === null) {
|
|
450
300
|
p.log.warn('Project tools are not ready yet.');
|
|
451
301
|
return;
|
|
452
302
|
}
|
|
453
|
-
const metas =
|
|
303
|
+
const metas = store.listProjects();
|
|
454
304
|
if (metas.length === 0) {
|
|
455
305
|
p.log.info('No games yet. Pick "Build a game"!');
|
|
456
306
|
return;
|
|
@@ -460,14 +310,14 @@ async function openPicked(settings) {
|
|
|
460
310
|
? settings.lastProjectSlug
|
|
461
311
|
: first?.slug;
|
|
462
312
|
const pick = await p.select({
|
|
463
|
-
message: 'Pick a
|
|
313
|
+
message: 'Pick a game.',
|
|
464
314
|
options: metas.map((m) => ({ value: m.slug, label: m.prettyName })),
|
|
465
315
|
...(initial !== undefined ? { initialValue: initial } : {}),
|
|
466
316
|
});
|
|
467
317
|
if (p.isCancel(pick)) {
|
|
468
318
|
return;
|
|
469
319
|
}
|
|
470
|
-
const project =
|
|
320
|
+
const project = store.openProject(pick);
|
|
471
321
|
if (project === null) {
|
|
472
322
|
p.log.warn('I could not open that one.');
|
|
473
323
|
return;
|
package/package.json
CHANGED