@vsceasy/cli 0.1.5 → 0.1.7

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/CHANGELOG.md CHANGED
@@ -5,6 +5,7 @@ All notable changes follow [Keep a Changelog](https://keepachangelog.com/en/1.1.
5
5
  ## [Unreleased]
6
6
 
7
7
  ### Added
8
+ - **`vsceasy create` post-scaffold setup** — after generating the project, `create` now offers to **initialize a git repository** (`git init`) and **install dependencies** (`bun`, falling back to `npm`). Both prompts default to yes in an interactive terminal. New `--git` / `--install` flags (and `--git=false` / `--install=false`) skip the prompts for scripting/CI. Non-interactive runs without the flags skip both, as before.
8
9
  - **`vsceasy job add`** — scaffold recurring / event-triggered jobs into `src/jobs/`. Schedules: `--every "60s"`, `--dailyAt "09:00"`, `--on startup|saveDocument|openDocument|changeActiveEditor|changeConfig`, `--onFile "**/*.md"`. Optional `--minIntervalMs` throttles re-runs via globalState. Runtime (`bootstrap`) auto-registers timers/listeners + cleanup on deactivate, catches errors so they don't crash the host.
9
10
  - **`command add --when <expr>`** — declare VS Code `when` clauses on commands. Auto-written to `contributes.commands[].enablement` and `contributes.menus.commandPalette` by `bun run gen`. Enables context-aware visibility (e.g. `editorTextFocus`, `resourceLangId == typescript`).
10
11
  - **`vsceasy helper add <kind>`** — generate typed runtime wrappers in `src/helpers/`:
@@ -40,6 +41,11 @@ All notable changes follow [Keep a Changelog](https://keepachangelog.com/en/1.1.
40
41
  - Bumped to **0.1.0** — first signed/tagged release.
41
42
  - Dropped Svelte/Vue/Vanilla mentions from docs and CLI options. Only React UI is supported in this release.
42
43
 
44
+ ### Fixed
45
+ - **`crud add --menu` flag parsing** — passing a raw policy string (`--menu none`, `--menu new:<id>`, `--menu existing:<id>`) non-interactively was mis-mapped to `existing:<literal>`, so `--menu none` failed with `Menu not found: src/menus/none.ts`. The flag forms now work the same as the interactive choices (which was already the documented behavior).
46
+ - **Generated CRUD form wiped on reveal** — the scaffolded form panel re-ran its loader on every focus/visibility change. With no row pending it reset to an empty "New" form, discarding whatever the user was typing (e.g. ticking a boolean then losing focus). The loader now only resets on the initial mount; later reveals adopt a newly-requested edit row but otherwise leave the in-progress form untouched. Re-run `crud add` (or pull the change into existing `*/App.tsx` form panels) to pick up the fix.
47
+ - **Generated CRUD date fields didn't prefill when editing** — `<input type="date">` only accepts a `yyyy-MM-dd` value, but stored dates are ISO strings (or `Date` objects), so editing a row showed the date field blank. The generated form now normalizes through a `toDateInput()` helper. Re-run `crud add` (or add the helper to existing form panels) to pick up the fix.
48
+
43
49
  ## [0.0.1] — 2026-05-21
44
50
 
45
51
  - Initial MVP. React template, typed RPC bridge, file-based registry, CLI generators (`panel`, `command`, `menu`, `rpc`, `statusBar`, `subpanel`), `doctor`, `upgrade`.
package/README.md CHANGED
@@ -1,6 +1,10 @@
1
- # vsceasy
1
+ <p align="center">
2
+ <img src="assets/logo-mark.svg" alt="vsceasy octopus mascot" width="96" height="96" />
3
+ </p>
2
4
 
3
- Build VS Code extensions fast. React UI + typed RPC bridge between extension and webview + zero-config build.
5
+ <h1 align="center">vsceasy</h1>
6
+
7
+ <p align="center">Build VS Code extensions fast. React UI + typed RPC bridge between extension and webview + zero-config build.</p>
4
8
 
5
9
  > Status: v0.1 — React UI. Typed RPC bridge + file-based registry + scaffolding for panels, commands, menus, tree views, subpanels, status bars.
6
10
 
@@ -20,13 +24,13 @@ vsceasy --version
20
24
 
21
25
  ```bash
22
26
  bunx @vsceasy/cli create my-extension
27
+ # prompts to init git + install deps, then:
23
28
  cd my-extension
24
- bun install
25
29
  bun run dev
26
30
  # press F5 in VS Code to launch the Extension Development Host
27
31
  ```
28
32
 
29
- Or with flags:
33
+ Or with flags — `--git` / `--install` skip the post-scaffold prompts:
30
34
 
31
35
  ```bash
32
36
  bunx @vsceasy/cli create \
@@ -34,7 +38,9 @@ bunx @vsceasy/cli create \
34
38
  --displayName "My Extension" \
35
39
  --description "Does cool things" \
36
40
  --publisher my-publisher \
37
- --ui react
41
+ --ui react \
42
+ --git \
43
+ --install
38
44
  ```
39
45
 
40
46
  ## What you get
package/dist/bin/cli.js CHANGED
@@ -3826,7 +3826,7 @@ var init_scaffold = __esm(() => {
3826
3826
  });
3827
3827
 
3828
3828
  // src/lib/templatesData.ts
3829
- var TEMPLATES_VERSION = "0.1.5", TEMPLATE_FILES;
3829
+ var TEMPLATES_VERSION = "0.1.7", TEMPLATE_FILES;
3830
3830
  var init_templatesData = __esm(() => {
3831
3831
  TEMPLATE_FILES = {
3832
3832
  "_generators/command/command.ts.tpl": `import { defineCommand } from '../shared/vsceasy';
@@ -4025,21 +4025,35 @@ type FormState = Partial<{{Name}}>;
4025
4025
 
4026
4026
  const emptyForm: FormState = {{emptyFormLiteral}};
4027
4027
 
4028
+ // \`<input type="date">\` only accepts a \`yyyy-MM-dd\` value. Stored dates may be
4029
+ // ISO strings or Date objects, so normalize before binding to the input.
4030
+ function toDateInput(v: unknown): string {
4031
+ if (v == null || v === '') return '';
4032
+ const d = v instanceof Date ? v : new Date(v as string);
4033
+ return Number.isNaN(d.getTime()) ? '' : d.toISOString().slice(0, 10);
4034
+ }
4035
+
4028
4036
  export function App() {
4029
4037
  const [form, setForm] = useState<FormState>(emptyForm);
4030
4038
  const [editingId, setEditingId] = useState<{{Name}}['{{primaryKey}}'] | null>(null);
4031
4039
  const [error, setError] = useState<string | null>(null);
4032
4040
  const [saving, setSaving] = useState(false);
4033
4041
 
4034
- const load = useCallback(async () => {
4035
- // The list stashes the row id before revealing this panel. Pull it (the host
4036
- // clears it after handing it over) and pre-fill the form for editing.
4042
+ const load = useCallback(async (initial: boolean) => {
4043
+ // The list stashes a row id before revealing this panel. Pull it (the host
4044
+ // clears it after handing it over).
4037
4045
  const id = await api.pendingId();
4038
4046
  if (id == null || id === '') {
4039
- setForm(emptyForm);
4040
- setEditingId(null);
4047
+ // No row was requested. On the first mount, start with an empty "new" form.
4048
+ // On later reveals (focus/visibility), DON'T reset — that would wipe a form
4049
+ // the user is busy filling in. Just leave the current state as-is.
4050
+ if (initial) {
4051
+ setForm(emptyForm);
4052
+ setEditingId(null);
4053
+ }
4041
4054
  return;
4042
4055
  }
4056
+ // The list asked to edit a specific row — load it, replacing the current form.
4043
4057
  const row = await api.get(id);
4044
4058
  if (row) {
4045
4059
  setForm(row);
@@ -4048,11 +4062,12 @@ export function App() {
4048
4062
  }, []);
4049
4063
 
4050
4064
  useEffect(() => {
4051
- void load();
4052
- // Webviews retain state when hidden, so re-load whenever the panel is
4053
- // revealed — the list may have asked to edit a different row.
4054
- const onFocus = () => { void load(); };
4055
- const onVisible = () => { if (document.visibilityState === 'visible') void load(); };
4065
+ void load(true);
4066
+ // Webviews retain state when hidden, so re-check on reveal: the list may have
4067
+ // asked to edit a different row. When nothing is pending, \`load\` leaves the
4068
+ // in-progress form untouched (see above).
4069
+ const onFocus = () => { void load(false); };
4070
+ const onVisible = () => { if (document.visibilityState === 'visible') void load(false); };
4056
4071
  window.addEventListener('focus', onFocus);
4057
4072
  document.addEventListener('visibilitychange', onVisible);
4058
4073
  return () => {
@@ -7692,65 +7707,6 @@ var init_findProject = __esm(() => {
7692
7707
  path3 = __toESM(require("path"));
7693
7708
  });
7694
7709
 
7695
- // src/commands/create.ts
7696
- function toTitle(s) {
7697
- return s.replace(/[-_]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
7698
- }
7699
- var import_cli_maker, path4, createCommand, create_default;
7700
- var init_create = __esm(() => {
7701
- init_scaffold();
7702
- init_findProject();
7703
- import_cli_maker = __toESM(require_dist(), 1);
7704
- path4 = __toESM(require("path"));
7705
- createCommand = {
7706
- name: "create",
7707
- description: "Scaffold a new VS Code extension project",
7708
- params: [
7709
- { name: "name", description: "Extension package name (e.g. my-extension or @scope/my-ext)", required: true, type: import_cli_maker.ParamType.Text },
7710
- { name: "displayName", description: "Human-readable extension name", required: false, type: import_cli_maker.ParamType.Text },
7711
- { name: "description", description: "Short description", required: false, type: import_cli_maker.ParamType.Text },
7712
- { name: "publisher", description: "VS Code publisher id", required: false, type: import_cli_maker.ParamType.Text },
7713
- { name: "ui", description: "UI framework", required: false, type: import_cli_maker.ParamType.List, options: ["react"] },
7714
- { name: "preset", description: "Project preset (minimal = empty extension, full = panel + RPC sample)", required: false, type: import_cli_maker.ParamType.List, options: ["minimal", "full"] },
7715
- { name: "dir", description: "Target directory (defaults to ./<name>)", required: false, type: import_cli_maker.ParamType.Text }
7716
- ],
7717
- action: async (args) => {
7718
- const name = args.name;
7719
- const simpleName = name.replace(/^@[^/]+\//, "");
7720
- const ui = args.ui ?? "react";
7721
- const preset = args.preset ?? "full";
7722
- const targetDir = path4.resolve(process.cwd(), args.dir ?? simpleName);
7723
- try {
7724
- await scaffold({
7725
- name,
7726
- displayName: args.displayName ?? toTitle(simpleName),
7727
- description: args.description ?? `${simpleName} VS Code extension`,
7728
- publisher: args.publisher ?? "your-publisher",
7729
- ui,
7730
- preset,
7731
- targetDir,
7732
- templatesRoot: findTemplatesRoot()
7733
- });
7734
- const rel = path4.relative(process.cwd(), targetDir) || ".";
7735
- console.log(`
7736
- ✓ Created ${name} at ${rel}
7737
- `);
7738
- console.log("Next steps:");
7739
- console.log(` cd ${rel}`);
7740
- console.log(" bun install");
7741
- console.log(" bun run launch # builds + opens Extension Development Host");
7742
- console.log(" # or `bun run dev` + F5 inside VS Code for watch mode\n");
7743
- } catch (err) {
7744
- console.error(`
7745
- ✗ Failed to scaffold: ${err.message}
7746
- `);
7747
- process.exitCode = 1;
7748
- }
7749
- }
7750
- };
7751
- create_default = createCommand;
7752
- });
7753
-
7754
7710
  // src/lib/interactive.ts
7755
7711
  function rl() {
7756
7712
  return readline.createInterface({ input: process.stdin, output: process.stdout });
@@ -7758,11 +7714,11 @@ function rl() {
7758
7714
  async function askText(question, defaultValue) {
7759
7715
  const r = rl();
7760
7716
  const hint = defaultValue ? ` ${DIM}[${defaultValue}]${RST}` : "";
7761
- return new Promise((resolve3) => {
7717
+ return new Promise((resolve2) => {
7762
7718
  r.question(`${CYAN}?${RST} ${question}${hint}: `, (ans) => {
7763
7719
  r.close();
7764
7720
  const v = (ans ?? "").trim();
7765
- resolve3(v.length ? v : defaultValue ?? "");
7721
+ resolve2(v.length ? v : defaultValue ?? "");
7766
7722
  });
7767
7723
  });
7768
7724
  }
@@ -7803,7 +7759,7 @@ async function selectArrow(question, options, opts) {
7803
7759
  if (index >= v.length)
7804
7760
  index = Math.max(0, v.length - 1);
7805
7761
  };
7806
- return new Promise((resolve3, reject) => {
7762
+ return new Promise((resolve2, reject) => {
7807
7763
  const stdin = process.stdin;
7808
7764
  const stdout = process.stdout;
7809
7765
  let lastLines = 0;
@@ -7880,7 +7836,7 @@ async function selectArrow(question, options, opts) {
7880
7836
  cleanup();
7881
7837
  stdout.write(`${CYAN}?${RST} ${BOLD}${question}${RST} ${GREEN}${v[index].label}${RST}
7882
7838
  `);
7883
- resolve3(v[index].value);
7839
+ resolve2(v[index].value);
7884
7840
  return;
7885
7841
  }
7886
7842
  if (data === "" || data === "\b") {
@@ -7949,6 +7905,132 @@ var init_interactive = __esm(() => {
7949
7905
  style = { DIM, BOLD, CYAN, GREEN, YELLOW, INV, RST };
7950
7906
  });
7951
7907
 
7908
+ // src/commands/create.ts
7909
+ function toTitle(s) {
7910
+ return s.replace(/[-_]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
7911
+ }
7912
+ function toBool(v) {
7913
+ if (v === undefined || v === null || v === "")
7914
+ return;
7915
+ if (typeof v === "boolean")
7916
+ return v;
7917
+ const s = String(v).trim().toLowerCase();
7918
+ if (["true", "1", "yes", "y"].includes(s))
7919
+ return true;
7920
+ if (["false", "0", "no", "n"].includes(s))
7921
+ return false;
7922
+ return;
7923
+ }
7924
+ function which(cmd) {
7925
+ const r = import_child_process.spawnSync(process.platform === "win32" ? "where" : "which", [cmd], { stdio: "ignore" });
7926
+ return r.status === 0;
7927
+ }
7928
+ function initGit(cwd) {
7929
+ const r = import_child_process.spawnSync("git", ["init"], { cwd, stdio: "inherit" });
7930
+ if (r.status === 0) {
7931
+ console.log("✓ Initialized git repository");
7932
+ return true;
7933
+ }
7934
+ console.warn("! Could not initialize git repository");
7935
+ return false;
7936
+ }
7937
+ function runInstall(pm, cwd) {
7938
+ console.log(`
7939
+ Installing dependencies with ${pm}...
7940
+ `);
7941
+ const r = import_child_process.spawnSync(pm, ["install"], { cwd, stdio: "inherit" });
7942
+ if (r.status === 0) {
7943
+ console.log(`
7944
+ ✓ Dependencies installed`);
7945
+ return true;
7946
+ }
7947
+ console.warn(`
7948
+ ! ${pm} install failed — run it manually`);
7949
+ return false;
7950
+ }
7951
+ var import_cli_maker, path4, import_child_process, createCommand, create_default;
7952
+ var init_create = __esm(() => {
7953
+ init_scaffold();
7954
+ init_findProject();
7955
+ init_interactive();
7956
+ import_cli_maker = __toESM(require_dist(), 1);
7957
+ path4 = __toESM(require("path"));
7958
+ import_child_process = require("child_process");
7959
+ createCommand = {
7960
+ name: "create",
7961
+ description: "Scaffold a new VS Code extension project",
7962
+ params: [
7963
+ { name: "name", description: "Extension package name (e.g. my-extension or @scope/my-ext)", required: true, type: import_cli_maker.ParamType.Text },
7964
+ { name: "displayName", description: "Human-readable extension name", required: false, type: import_cli_maker.ParamType.Text },
7965
+ { name: "description", description: "Short description", required: false, type: import_cli_maker.ParamType.Text },
7966
+ { name: "publisher", description: "VS Code publisher id", required: false, type: import_cli_maker.ParamType.Text },
7967
+ { name: "ui", description: "UI framework", required: false, type: import_cli_maker.ParamType.List, options: ["react"] },
7968
+ { name: "preset", description: "Project preset (minimal = empty extension, full = panel + RPC sample)", required: false, type: import_cli_maker.ParamType.List, options: ["minimal", "full"] },
7969
+ { name: "dir", description: "Target directory (defaults to ./<name>)", required: false, type: import_cli_maker.ParamType.Text },
7970
+ { name: "git", description: "Initialize a git repository (skips the prompt)", required: false, type: import_cli_maker.ParamType.Boolean },
7971
+ { name: "install", description: "Install dependencies after scaffolding (skips the prompt)", required: false, type: import_cli_maker.ParamType.Boolean }
7972
+ ],
7973
+ action: async (args) => {
7974
+ const name = args.name;
7975
+ const simpleName = name.replace(/^@[^/]+\//, "");
7976
+ const ui = args.ui ?? "react";
7977
+ const preset = args.preset ?? "full";
7978
+ const targetDir = path4.resolve(process.cwd(), args.dir ?? simpleName);
7979
+ try {
7980
+ await scaffold({
7981
+ name,
7982
+ displayName: args.displayName ?? toTitle(simpleName),
7983
+ description: args.description ?? `${simpleName} VS Code extension`,
7984
+ publisher: args.publisher ?? "your-publisher",
7985
+ ui,
7986
+ preset,
7987
+ targetDir,
7988
+ templatesRoot: findTemplatesRoot()
7989
+ });
7990
+ const rel = path4.relative(process.cwd(), targetDir) || ".";
7991
+ console.log(`
7992
+ ✓ Created ${name} at ${rel}
7993
+ `);
7994
+ const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
7995
+ const gitFlag = toBool(args.git);
7996
+ const installFlag = toBool(args.install);
7997
+ const wantGit = gitFlag ?? (interactive ? await confirm("Initialize a git repository?", true) : false);
7998
+ if (wantGit) {
7999
+ if (which("git"))
8000
+ initGit(targetDir);
8001
+ else
8002
+ console.warn("! git not found — skipping repository init");
8003
+ }
8004
+ let pm = null;
8005
+ const wantInstall = installFlag ?? (interactive ? await confirm("Install dependencies?", true) : false);
8006
+ let installed = false;
8007
+ if (wantInstall) {
8008
+ pm = which("bun") ? "bun" : which("npm") ? "npm" : null;
8009
+ if (pm)
8010
+ installed = runInstall(pm, targetDir);
8011
+ else
8012
+ console.warn("! No package manager (bun/npm) found — skipping install");
8013
+ }
8014
+ const run = pm ?? "bun";
8015
+ console.log(`
8016
+ Next steps:`);
8017
+ console.log(` cd ${rel}`);
8018
+ if (!installed)
8019
+ console.log(` ${run} install`);
8020
+ console.log(` ${run} run launch # builds + opens Extension Development Host`);
8021
+ console.log(` # or \`${run} run dev\` + F5 inside VS Code for watch mode
8022
+ `);
8023
+ } catch (err) {
8024
+ console.error(`
8025
+ ✗ Failed to scaffold: ${err.message}
8026
+ `);
8027
+ process.exitCode = 1;
8028
+ }
8029
+ }
8030
+ };
8031
+ create_default = createCommand;
8032
+ });
8033
+
7952
8034
  // src/lib/validate.ts
7953
8035
  function assertId(field, value) {
7954
8036
  const trimmed = (value ?? "").trim();
@@ -8116,18 +8198,18 @@ function appendApi(apiPath, apiName, body, created, modified, skipped) {
8116
8198
  }
8117
8199
  function runGen(cwd) {
8118
8200
  const tryRun = (cmd, args) => {
8119
- const r = import_child_process.spawnSync(cmd, args, { cwd, stdio: "inherit" });
8201
+ const r = import_child_process2.spawnSync(cmd, args, { cwd, stdio: "inherit" });
8120
8202
  return r.status === 0;
8121
8203
  };
8122
- if (which("bun") && tryRun("bun", ["run", "gen"]))
8204
+ if (which2("bun") && tryRun("bun", ["run", "gen"]))
8123
8205
  return true;
8124
- if (which("npm") && tryRun("npm", ["run", "gen"]))
8206
+ if (which2("npm") && tryRun("npm", ["run", "gen"]))
8125
8207
  return true;
8126
8208
  console.warn('\n! Could not run "gen" automatically. Run `bun run gen` (or `npm run gen`) manually to wire up the new panel.\n');
8127
8209
  return false;
8128
8210
  }
8129
- function which(cmd) {
8130
- const r = import_child_process.spawnSync(process.platform === "win32" ? "where" : "which", [cmd], { stdio: "ignore" });
8211
+ function which2(cmd) {
8212
+ const r = import_child_process2.spawnSync(process.platform === "win32" ? "where" : "which", [cmd], { stdio: "ignore" });
8131
8213
  return r.status === 0;
8132
8214
  }
8133
8215
  function normalizeCamel(s) {
@@ -8136,14 +8218,14 @@ function normalizeCamel(s) {
8136
8218
  return "";
8137
8219
  return cleaned.charAt(0).toLowerCase() + cleaned.slice(1);
8138
8220
  }
8139
- var fs6, path7, import_child_process, PANEL_TEMPLATES, API_BODY, TEMPLATE_VARS;
8221
+ var fs6, path7, import_child_process2, PANEL_TEMPLATES, API_BODY, TEMPLATE_VARS;
8140
8222
  var init_add2 = __esm(() => {
8141
8223
  init_scaffold();
8142
8224
  init_validate();
8143
8225
  init_add();
8144
8226
  fs6 = __toESM(require("fs"));
8145
8227
  path7 = __toESM(require("path"));
8146
- import_child_process = require("child_process");
8228
+ import_child_process2 = require("child_process");
8147
8229
  PANEL_TEMPLATES = ["blank", "form", "list", "dashboard"];
8148
8230
  API_BODY = {
8149
8231
  blank: "",
@@ -8210,18 +8292,18 @@ function editMenu(opts) {
8210
8292
  }
8211
8293
  function runGen2(cwd) {
8212
8294
  const tryRun = (cmd, args) => {
8213
- const r = import_child_process2.spawnSync(cmd, args, { cwd, stdio: "inherit" });
8295
+ const r = import_child_process3.spawnSync(cmd, args, { cwd, stdio: "inherit" });
8214
8296
  return r.status === 0;
8215
8297
  };
8216
- if (which2("bun") && tryRun("bun", ["run", "gen"]))
8298
+ if (which3("bun") && tryRun("bun", ["run", "gen"]))
8217
8299
  return true;
8218
- if (which2("npm") && tryRun("npm", ["run", "gen"]))
8300
+ if (which3("npm") && tryRun("npm", ["run", "gen"]))
8219
8301
  return true;
8220
8302
  console.warn('\n! Could not run "gen" automatically. Run `bun run gen` manually.\n');
8221
8303
  return false;
8222
8304
  }
8223
- function which2(cmd) {
8224
- const r = import_child_process2.spawnSync(process.platform === "win32" ? "where" : "which", [cmd], { stdio: "ignore" });
8305
+ function which3(cmd) {
8306
+ const r = import_child_process3.spawnSync(process.platform === "win32" ? "where" : "which", [cmd], { stdio: "ignore" });
8225
8307
  return r.status === 0;
8226
8308
  }
8227
8309
  function insertItem(src, item) {
@@ -8393,11 +8475,11 @@ function findMatching(src, openIdx) {
8393
8475
  }
8394
8476
  return -1;
8395
8477
  }
8396
- var fs7, path8, import_child_process2;
8478
+ var fs7, path8, import_child_process3;
8397
8479
  var init_edit = __esm(() => {
8398
8480
  fs7 = __toESM(require("fs"));
8399
8481
  path8 = __toESM(require("path"));
8400
- import_child_process2 = require("child_process");
8482
+ import_child_process3 = require("child_process");
8401
8483
  });
8402
8484
 
8403
8485
  // src/lib/command/add.ts
@@ -8453,18 +8535,18 @@ function addCommand(opts) {
8453
8535
  }
8454
8536
  function runGen3(cwd) {
8455
8537
  const tryRun = (cmd, args) => {
8456
- const r = import_child_process3.spawnSync(cmd, args, { cwd, stdio: "inherit" });
8538
+ const r = import_child_process4.spawnSync(cmd, args, { cwd, stdio: "inherit" });
8457
8539
  return r.status === 0;
8458
8540
  };
8459
- if (which3("bun") && tryRun("bun", ["run", "gen"]))
8541
+ if (which4("bun") && tryRun("bun", ["run", "gen"]))
8460
8542
  return true;
8461
- if (which3("npm") && tryRun("npm", ["run", "gen"]))
8543
+ if (which4("npm") && tryRun("npm", ["run", "gen"]))
8462
8544
  return true;
8463
8545
  console.warn('\n! Could not run "gen" automatically. Run `bun run gen` manually.\n');
8464
8546
  return false;
8465
8547
  }
8466
- function which3(cmd) {
8467
- const r = import_child_process3.spawnSync(process.platform === "win32" ? "where" : "which", [cmd], { stdio: "ignore" });
8548
+ function which4(cmd) {
8549
+ const r = import_child_process4.spawnSync(process.platform === "win32" ? "where" : "which", [cmd], { stdio: "ignore" });
8468
8550
  return r.status === 0;
8469
8551
  }
8470
8552
  function escapeQuotes(s) {
@@ -8476,7 +8558,7 @@ function normalizeCamel2(s) {
8476
8558
  return "";
8477
8559
  return cleaned.charAt(0).toLowerCase() + cleaned.slice(1);
8478
8560
  }
8479
- var fs8, path9, import_child_process3;
8561
+ var fs8, path9, import_child_process4;
8480
8562
  var init_add3 = __esm(() => {
8481
8563
  init_scaffold();
8482
8564
  init_edit();
@@ -8484,7 +8566,7 @@ var init_add3 = __esm(() => {
8484
8566
  init_config();
8485
8567
  fs8 = __toESM(require("fs"));
8486
8568
  path9 = __toESM(require("path"));
8487
- import_child_process3 = require("child_process");
8569
+ import_child_process4 = require("child_process");
8488
8570
  });
8489
8571
 
8490
8572
  // src/lib/helper/add.ts
@@ -10013,24 +10095,24 @@ function filesEqual(a, b) {
10013
10095
  }
10014
10096
  function runGen4(cwd) {
10015
10097
  const tryRun = (cmd, args) => {
10016
- const r = import_child_process4.spawnSync(cmd, args, { cwd, stdio: "inherit" });
10098
+ const r = import_child_process5.spawnSync(cmd, args, { cwd, stdio: "inherit" });
10017
10099
  return r.status === 0;
10018
10100
  };
10019
- if (which4("bun") && tryRun("bun", ["run", "gen"]))
10101
+ if (which5("bun") && tryRun("bun", ["run", "gen"]))
10020
10102
  return true;
10021
- if (which4("npm") && tryRun("npm", ["run", "gen"]))
10103
+ if (which5("npm") && tryRun("npm", ["run", "gen"]))
10022
10104
  return true;
10023
10105
  return false;
10024
10106
  }
10025
- function which4(cmd) {
10026
- const r = import_child_process4.spawnSync(process.platform === "win32" ? "where" : "which", [cmd], { stdio: "ignore" });
10107
+ function which5(cmd) {
10108
+ const r = import_child_process5.spawnSync(process.platform === "win32" ? "where" : "which", [cmd], { stdio: "ignore" });
10027
10109
  return r.status === 0;
10028
10110
  }
10029
- var fs13, path15, import_child_process4, SYNC_PATHS;
10111
+ var fs13, path15, import_child_process5, SYNC_PATHS;
10030
10112
  var init_upgrade = __esm(() => {
10031
10113
  fs13 = __toESM(require("fs"));
10032
10114
  path15 = __toESM(require("path"));
10033
- import_child_process4 = require("child_process");
10115
+ import_child_process5 = require("child_process");
10034
10116
  SYNC_PATHS = [
10035
10117
  "src/shared/vsceasy/define.ts",
10036
10118
  "src/shared/vsceasy/bootstrap.ts",
@@ -10257,18 +10339,18 @@ function addMenu(opts) {
10257
10339
  }
10258
10340
  function runGen5(cwd) {
10259
10341
  const tryRun = (cmd, args) => {
10260
- const r = import_child_process5.spawnSync(cmd, args, { cwd, stdio: "inherit" });
10342
+ const r = import_child_process6.spawnSync(cmd, args, { cwd, stdio: "inherit" });
10261
10343
  return r.status === 0;
10262
10344
  };
10263
- if (which5("bun") && tryRun("bun", ["run", "gen"]))
10345
+ if (which6("bun") && tryRun("bun", ["run", "gen"]))
10264
10346
  return true;
10265
- if (which5("npm") && tryRun("npm", ["run", "gen"]))
10347
+ if (which6("npm") && tryRun("npm", ["run", "gen"]))
10266
10348
  return true;
10267
10349
  console.warn('\n! Could not run "gen" automatically. Run `bun run gen` manually.\n');
10268
10350
  return false;
10269
10351
  }
10270
- function which5(cmd) {
10271
- const r = import_child_process5.spawnSync(process.platform === "win32" ? "where" : "which", [cmd], { stdio: "ignore" });
10352
+ function which6(cmd) {
10353
+ const r = import_child_process6.spawnSync(process.platform === "win32" ? "where" : "which", [cmd], { stdio: "ignore" });
10272
10354
  return r.status === 0;
10273
10355
  }
10274
10356
  function normalizeCamel4(s) {
@@ -10277,14 +10359,14 @@ function normalizeCamel4(s) {
10277
10359
  return "";
10278
10360
  return cleaned.charAt(0).toLowerCase() + cleaned.slice(1);
10279
10361
  }
10280
- var fs14, path17, import_child_process5;
10362
+ var fs14, path17, import_child_process6;
10281
10363
  var init_add7 = __esm(() => {
10282
10364
  init_scaffold();
10283
10365
  init_validate();
10284
10366
  init_config();
10285
10367
  fs14 = __toESM(require("fs"));
10286
10368
  path17 = __toESM(require("path"));
10287
- import_child_process5 = require("child_process");
10369
+ import_child_process6 = require("child_process");
10288
10370
  });
10289
10371
 
10290
10372
  // src/commands/menu/add.ts
@@ -10828,25 +10910,25 @@ function buildSnippet(method, argNames) {
10828
10910
  }
10829
10911
  function runGen6(cwd) {
10830
10912
  const tryRun = (cmd, args) => {
10831
- const r = import_child_process6.spawnSync(cmd, args, { cwd, stdio: "inherit" });
10913
+ const r = import_child_process7.spawnSync(cmd, args, { cwd, stdio: "inherit" });
10832
10914
  return r.status === 0;
10833
10915
  };
10834
- if (which6("bun") && tryRun("bun", ["run", "gen"]))
10916
+ if (which7("bun") && tryRun("bun", ["run", "gen"]))
10835
10917
  return true;
10836
- if (which6("npm") && tryRun("npm", ["run", "gen"]))
10918
+ if (which7("npm") && tryRun("npm", ["run", "gen"]))
10837
10919
  return true;
10838
10920
  return false;
10839
10921
  }
10840
- function which6(cmd) {
10841
- const r = import_child_process6.spawnSync(process.platform === "win32" ? "where" : "which", [cmd], { stdio: "ignore" });
10922
+ function which7(cmd) {
10923
+ const r = import_child_process7.spawnSync(process.platform === "win32" ? "where" : "which", [cmd], { stdio: "ignore" });
10842
10924
  return r.status === 0;
10843
10925
  }
10844
- var fs17, path21, import_child_process6;
10926
+ var fs17, path21, import_child_process7;
10845
10927
  var init_add10 = __esm(() => {
10846
10928
  init_validate();
10847
10929
  fs17 = __toESM(require("fs"));
10848
10930
  path21 = __toESM(require("path"));
10849
- import_child_process6 = require("child_process");
10931
+ import_child_process7 = require("child_process");
10850
10932
  });
10851
10933
 
10852
10934
  // src/commands/rpc/add.ts
@@ -11001,17 +11083,17 @@ function asBacktickString(s) {
11001
11083
  }
11002
11084
  function runGen7(cwd) {
11003
11085
  const tryRun = (cmd, args) => {
11004
- const r = import_child_process7.spawnSync(cmd, args, { cwd, stdio: "inherit" });
11086
+ const r = import_child_process8.spawnSync(cmd, args, { cwd, stdio: "inherit" });
11005
11087
  return r.status === 0;
11006
11088
  };
11007
- if (which7("bun") && tryRun("bun", ["run", "gen"]))
11089
+ if (which8("bun") && tryRun("bun", ["run", "gen"]))
11008
11090
  return true;
11009
- if (which7("npm") && tryRun("npm", ["run", "gen"]))
11091
+ if (which8("npm") && tryRun("npm", ["run", "gen"]))
11010
11092
  return true;
11011
11093
  return false;
11012
11094
  }
11013
- function which7(cmd) {
11014
- const r = import_child_process7.spawnSync(process.platform === "win32" ? "where" : "which", [cmd], { stdio: "ignore" });
11095
+ function which8(cmd) {
11096
+ const r = import_child_process8.spawnSync(process.platform === "win32" ? "where" : "which", [cmd], { stdio: "ignore" });
11015
11097
  return r.status === 0;
11016
11098
  }
11017
11099
  function normalizeCamel5(s) {
@@ -11020,14 +11102,14 @@ function normalizeCamel5(s) {
11020
11102
  return "";
11021
11103
  return cleaned.charAt(0).toLowerCase() + cleaned.slice(1);
11022
11104
  }
11023
- var fs18, path23, import_child_process7;
11105
+ var fs18, path23, import_child_process8;
11024
11106
  var init_add12 = __esm(() => {
11025
11107
  init_scaffold();
11026
11108
  init_add3();
11027
11109
  init_validate();
11028
11110
  fs18 = __toESM(require("fs"));
11029
11111
  path23 = __toESM(require("path"));
11030
- import_child_process7 = require("child_process");
11112
+ import_child_process8 = require("child_process");
11031
11113
  });
11032
11114
 
11033
11115
  // src/commands/statusBar/add.ts
@@ -11304,18 +11386,18 @@ function appendApi2(apiPath, apiName, created, modified, skipped) {
11304
11386
  }
11305
11387
  function runGen8(cwd) {
11306
11388
  const tryRun = (cmd, args) => {
11307
- const r = import_child_process8.spawnSync(cmd, args, { cwd, stdio: "inherit" });
11389
+ const r = import_child_process9.spawnSync(cmd, args, { cwd, stdio: "inherit" });
11308
11390
  return r.status === 0;
11309
11391
  };
11310
- if (which8("bun") && tryRun("bun", ["run", "gen"]))
11392
+ if (which9("bun") && tryRun("bun", ["run", "gen"]))
11311
11393
  return true;
11312
- if (which8("npm") && tryRun("npm", ["run", "gen"]))
11394
+ if (which9("npm") && tryRun("npm", ["run", "gen"]))
11313
11395
  return true;
11314
11396
  console.warn('\n! Could not run "gen" automatically. Run `bun run gen` manually.\n');
11315
11397
  return false;
11316
11398
  }
11317
- function which8(cmd) {
11318
- const r = import_child_process8.spawnSync(process.platform === "win32" ? "where" : "which", [cmd], { stdio: "ignore" });
11399
+ function which9(cmd) {
11400
+ const r = import_child_process9.spawnSync(process.platform === "win32" ? "where" : "which", [cmd], { stdio: "ignore" });
11319
11401
  return r.status === 0;
11320
11402
  }
11321
11403
  function normalizeCamel6(s) {
@@ -11324,13 +11406,13 @@ function normalizeCamel6(s) {
11324
11406
  return "";
11325
11407
  return cleaned.charAt(0).toLowerCase() + cleaned.slice(1);
11326
11408
  }
11327
- var fs19, path25, import_child_process8;
11409
+ var fs19, path25, import_child_process9;
11328
11410
  var init_add14 = __esm(() => {
11329
11411
  init_scaffold();
11330
11412
  init_validate();
11331
11413
  fs19 = __toESM(require("fs"));
11332
11414
  path25 = __toESM(require("path"));
11333
- import_child_process8 = require("child_process");
11415
+ import_child_process9 = require("child_process");
11334
11416
  });
11335
11417
 
11336
11418
  // src/commands/subpanel/add.ts
@@ -11436,16 +11518,16 @@ function addTreeView(opts) {
11436
11518
  return { created: [target], genRan };
11437
11519
  }
11438
11520
  function runGen9(cwd) {
11439
- const tryRun = (cmd, args) => import_child_process9.spawnSync(cmd, args, { cwd, stdio: "inherit" }).status === 0;
11440
- if (which9("bun") && tryRun("bun", ["run", "gen"]))
11521
+ const tryRun = (cmd, args) => import_child_process10.spawnSync(cmd, args, { cwd, stdio: "inherit" }).status === 0;
11522
+ if (which10("bun") && tryRun("bun", ["run", "gen"]))
11441
11523
  return true;
11442
- if (which9("npm") && tryRun("npm", ["run", "gen"]))
11524
+ if (which10("npm") && tryRun("npm", ["run", "gen"]))
11443
11525
  return true;
11444
11526
  console.warn('\n! Could not run "gen" automatically. Run `bun run gen` manually.\n');
11445
11527
  return false;
11446
11528
  }
11447
- function which9(cmd) {
11448
- return import_child_process9.spawnSync(process.platform === "win32" ? "where" : "which", [cmd], { stdio: "ignore" }).status === 0;
11529
+ function which10(cmd) {
11530
+ return import_child_process10.spawnSync(process.platform === "win32" ? "where" : "which", [cmd], { stdio: "ignore" }).status === 0;
11449
11531
  }
11450
11532
  function normalizeCamel7(s) {
11451
11533
  const cleaned = s.trim().replace(/[^a-zA-Z0-9]+(.)/g, (_m, c) => c.toUpperCase()).replace(/[^a-zA-Z0-9]/g, "");
@@ -11456,13 +11538,13 @@ function normalizeCamel7(s) {
11456
11538
  function pascal3(s) {
11457
11539
  return s.charAt(0).toUpperCase() + s.slice(1);
11458
11540
  }
11459
- var fs20, path27, import_child_process9;
11541
+ var fs20, path27, import_child_process10;
11460
11542
  var init_add16 = __esm(() => {
11461
11543
  init_scaffold();
11462
11544
  init_validate();
11463
11545
  fs20 = __toESM(require("fs"));
11464
11546
  path27 = __toESM(require("path"));
11465
- import_child_process9 = require("child_process");
11547
+ import_child_process10 = require("child_process");
11466
11548
  });
11467
11549
 
11468
11550
  // src/commands/treeView/add.ts
@@ -11674,19 +11756,19 @@ function publishInit(opts) {
11674
11756
  `);
11675
11757
  let dryPackOk = null;
11676
11758
  if (opts.runDryPack !== false) {
11677
- const r = import_child_process10.spawnSync("npx", ["--yes", "@vscode/vsce", "ls"], { cwd: opts.projectRoot, stdio: "inherit" });
11759
+ const r = import_child_process11.spawnSync("npx", ["--yes", "@vscode/vsce", "ls"], { cwd: opts.projectRoot, stdio: "inherit" });
11678
11760
  dryPackOk = r.status === 0;
11679
11761
  if (!dryPackOk)
11680
11762
  warnings.push("`vsce ls` exited non-zero — inspect output above.");
11681
11763
  }
11682
11764
  return { created, pkgUpdated, warnings, dryPackOk };
11683
11765
  }
11684
- var fs22, path31, import_child_process10;
11766
+ var fs22, path31, import_child_process11;
11685
11767
  var init_init2 = __esm(() => {
11686
11768
  init_scaffold();
11687
11769
  fs22 = __toESM(require("fs"));
11688
11770
  path31 = __toESM(require("path"));
11689
- import_child_process10 = require("child_process");
11771
+ import_child_process11 = require("child_process");
11690
11772
  });
11691
11773
 
11692
11774
  // src/commands/publish/init.ts
@@ -11850,16 +11932,16 @@ function escape(s) {
11850
11932
  return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
11851
11933
  }
11852
11934
  function runGen10(cwd) {
11853
- const tryRun = (cmd, args) => import_child_process11.spawnSync(cmd, args, { cwd, stdio: "inherit" }).status === 0;
11854
- if (which10("bun") && tryRun("bun", ["run", "gen"]))
11935
+ const tryRun = (cmd, args) => import_child_process12.spawnSync(cmd, args, { cwd, stdio: "inherit" }).status === 0;
11936
+ if (which11("bun") && tryRun("bun", ["run", "gen"]))
11855
11937
  return true;
11856
- if (which10("npm") && tryRun("npm", ["run", "gen"]))
11938
+ if (which11("npm") && tryRun("npm", ["run", "gen"]))
11857
11939
  return true;
11858
11940
  console.warn('\n! Could not run "gen" automatically. Run `bun run gen` manually.\n');
11859
11941
  return false;
11860
11942
  }
11861
- function which10(cmd) {
11862
- return import_child_process11.spawnSync(process.platform === "win32" ? "where" : "which", [cmd], { stdio: "ignore" }).status === 0;
11943
+ function which11(cmd) {
11944
+ return import_child_process12.spawnSync(process.platform === "win32" ? "where" : "which", [cmd], { stdio: "ignore" }).status === 0;
11863
11945
  }
11864
11946
  function normalizeCamel8(s) {
11865
11947
  const cleaned = s.trim().replace(/[^a-zA-Z0-9]+(.)/g, (_m, c) => c.toUpperCase()).replace(/[^a-zA-Z0-9]/g, "");
@@ -11867,13 +11949,13 @@ function normalizeCamel8(s) {
11867
11949
  return "";
11868
11950
  return cleaned.charAt(0).toLowerCase() + cleaned.slice(1);
11869
11951
  }
11870
- var fs23, path34, import_child_process11;
11952
+ var fs23, path34, import_child_process12;
11871
11953
  var init_add19 = __esm(() => {
11872
11954
  init_scaffold();
11873
11955
  init_validate();
11874
11956
  fs23 = __toESM(require("fs"));
11875
11957
  path34 = __toESM(require("path"));
11876
- import_child_process11 = require("child_process");
11958
+ import_child_process12 = require("child_process");
11877
11959
  });
11878
11960
 
11879
11961
  // src/commands/job/add.ts
@@ -12475,7 +12557,7 @@ function renderInput(field, override) {
12475
12557
  case "boolean":
12476
12558
  return wrap(` <input type="checkbox" checked={!!form.${name}} onChange={(e) => onChange('${name}', e.target.checked as any)} />`);
12477
12559
  case "date":
12478
- return wrap(` <input type="date"${required} value={(form.${name} as any) ?? ''} onChange={(e) => onChange('${name}', e.target.value as any)} />`);
12560
+ return wrap(` <input type="date"${required} value={toDateInput(form.${name})} onChange={(e) => onChange('${name}', e.target.value as any)} />`);
12479
12561
  case "select": {
12480
12562
  const opts = (spec.options ?? []).map((o) => ` <option value=${JSON.stringify(o)}>${escapeJsx(o)}</option>`).join(`
12481
12563
  `);
@@ -12602,18 +12684,18 @@ function camelLower(s) {
12602
12684
  return s.charAt(0).toLowerCase() + s.slice(1);
12603
12685
  }
12604
12686
  function runGen11(cwd) {
12605
- const tryRun = (cmd, args) => import_child_process12.spawnSync(cmd, args, { cwd, stdio: "inherit" }).status === 0;
12606
- if (which11("bun") && tryRun("bun", ["run", "gen"]))
12687
+ const tryRun = (cmd, args) => import_child_process13.spawnSync(cmd, args, { cwd, stdio: "inherit" }).status === 0;
12688
+ if (which12("bun") && tryRun("bun", ["run", "gen"]))
12607
12689
  return true;
12608
- if (which11("npm") && tryRun("npm", ["run", "gen"]))
12690
+ if (which12("npm") && tryRun("npm", ["run", "gen"]))
12609
12691
  return true;
12610
12692
  console.warn('\n! Could not run "gen" automatically. Run `bun run gen` manually.\n');
12611
12693
  return false;
12612
12694
  }
12613
- function which11(cmd) {
12614
- return import_child_process12.spawnSync(process.platform === "win32" ? "where" : "which", [cmd], { stdio: "ignore" }).status === 0;
12695
+ function which12(cmd) {
12696
+ return import_child_process13.spawnSync(process.platform === "win32" ? "where" : "which", [cmd], { stdio: "ignore" }).status === 0;
12615
12697
  }
12616
- var fs27, path41, import_child_process12;
12698
+ var fs27, path41, import_child_process13;
12617
12699
  var init_add22 = __esm(() => {
12618
12700
  init_scaffold();
12619
12701
  init_validate();
@@ -12623,7 +12705,7 @@ var init_add22 = __esm(() => {
12623
12705
  init_edit();
12624
12706
  fs27 = __toESM(require("fs"));
12625
12707
  path41 = __toESM(require("path"));
12626
- import_child_process12 = require("child_process");
12708
+ import_child_process13 = require("child_process");
12627
12709
  });
12628
12710
 
12629
12711
  // src/commands/crud/add.ts
@@ -12680,14 +12762,16 @@ var init_add23 = __esm(() => {
12680
12762
  const projectRoot = findProjectRoot();
12681
12763
  const templatesRoot = findTemplatesRoot();
12682
12764
  let menuSpec;
12683
- const choice = String(args.menu ?? NONE_SENTINEL2);
12684
- if (choice === NONE_SENTINEL2) {
12765
+ const choice = String(args.menu ?? NONE_SENTINEL2).trim();
12766
+ if (choice === NONE_SENTINEL2 || choice === "none") {
12685
12767
  menuSpec = "none";
12686
12768
  } else if (choice === NEW_SENTINEL) {
12687
12769
  const id = args.newMenuId ? String(args.newMenuId).trim() : await import_cli_maker18.prompt(" new menu id: ");
12688
12770
  if (!id)
12689
12771
  throw new Error("New menu id required.");
12690
12772
  menuSpec = `new:${id}`;
12773
+ } else if (choice.startsWith("new:") || choice.startsWith("existing:")) {
12774
+ menuSpec = choice;
12691
12775
  } else {
12692
12776
  menuSpec = `existing:${choice}`;
12693
12777
  }
@@ -12835,12 +12919,21 @@ var init_cli = __esm(() => {
12835
12919
  import_cli_maker20 = __toESM(require_dist(), 1);
12836
12920
  cli = new import_cli_maker20.CLI("vsceasy", "Build VS Code extensions fast — React UI + typed RPC bridge + zero-config build.", {
12837
12921
  interactive: true,
12838
- version: "0.1.5",
12922
+ version: "0.1.7",
12839
12923
  introAnimation: {
12840
12924
  enabled: true,
12841
12925
  preset: "retro-space",
12842
12926
  title: "vsceasy",
12843
- subtitle: "VS Code Extension Framework"
12927
+ subtitle: "VS Code Extension Framework",
12928
+ asciiArt: [
12929
+ ' .-""""-.',
12930
+ " / o o \\",
12931
+ " | .. |",
12932
+ " \\ '--' /",
12933
+ " /`-.__.-`\\",
12934
+ " _/ /|/||\\|\\ \\_",
12935
+ " `--`-`-``-`-`--`"
12936
+ ]
12844
12937
  },
12845
12938
  defaultCommands: {
12846
12939
  rotatePassphrase: false,
package/dist/index.js CHANGED
@@ -2238,7 +2238,7 @@ var init_upgrade = __esm(() => {
2238
2238
  });
2239
2239
 
2240
2240
  // src/lib/templatesData.ts
2241
- var TEMPLATES_VERSION = "0.1.5", TEMPLATE_FILES;
2241
+ var TEMPLATES_VERSION = "0.1.7", TEMPLATE_FILES;
2242
2242
  var init_templatesData = __esm(() => {
2243
2243
  TEMPLATE_FILES = {
2244
2244
  "_generators/command/command.ts.tpl": `import { defineCommand } from '../shared/vsceasy';
@@ -2437,21 +2437,35 @@ type FormState = Partial<{{Name}}>;
2437
2437
 
2438
2438
  const emptyForm: FormState = {{emptyFormLiteral}};
2439
2439
 
2440
+ // \`<input type="date">\` only accepts a \`yyyy-MM-dd\` value. Stored dates may be
2441
+ // ISO strings or Date objects, so normalize before binding to the input.
2442
+ function toDateInput(v: unknown): string {
2443
+ if (v == null || v === '') return '';
2444
+ const d = v instanceof Date ? v : new Date(v as string);
2445
+ return Number.isNaN(d.getTime()) ? '' : d.toISOString().slice(0, 10);
2446
+ }
2447
+
2440
2448
  export function App() {
2441
2449
  const [form, setForm] = useState<FormState>(emptyForm);
2442
2450
  const [editingId, setEditingId] = useState<{{Name}}['{{primaryKey}}'] | null>(null);
2443
2451
  const [error, setError] = useState<string | null>(null);
2444
2452
  const [saving, setSaving] = useState(false);
2445
2453
 
2446
- const load = useCallback(async () => {
2447
- // The list stashes the row id before revealing this panel. Pull it (the host
2448
- // clears it after handing it over) and pre-fill the form for editing.
2454
+ const load = useCallback(async (initial: boolean) => {
2455
+ // The list stashes a row id before revealing this panel. Pull it (the host
2456
+ // clears it after handing it over).
2449
2457
  const id = await api.pendingId();
2450
2458
  if (id == null || id === '') {
2451
- setForm(emptyForm);
2452
- setEditingId(null);
2459
+ // No row was requested. On the first mount, start with an empty "new" form.
2460
+ // On later reveals (focus/visibility), DON'T reset — that would wipe a form
2461
+ // the user is busy filling in. Just leave the current state as-is.
2462
+ if (initial) {
2463
+ setForm(emptyForm);
2464
+ setEditingId(null);
2465
+ }
2453
2466
  return;
2454
2467
  }
2468
+ // The list asked to edit a specific row — load it, replacing the current form.
2455
2469
  const row = await api.get(id);
2456
2470
  if (row) {
2457
2471
  setForm(row);
@@ -2460,11 +2474,12 @@ export function App() {
2460
2474
  }, []);
2461
2475
 
2462
2476
  useEffect(() => {
2463
- void load();
2464
- // Webviews retain state when hidden, so re-load whenever the panel is
2465
- // revealed — the list may have asked to edit a different row.
2466
- const onFocus = () => { void load(); };
2467
- const onVisible = () => { if (document.visibilityState === 'visible') void load(); };
2477
+ void load(true);
2478
+ // Webviews retain state when hidden, so re-check on reveal: the list may have
2479
+ // asked to edit a different row. When nothing is pending, \`load\` leaves the
2480
+ // in-progress form untouched (see above).
2481
+ const onFocus = () => { void load(false); };
2482
+ const onVisible = () => { if (document.visibilityState === 'visible') void load(false); };
2468
2483
  window.addEventListener('focus', onFocus);
2469
2484
  document.addEventListener('visibilitychange', onVisible);
2470
2485
  return () => {
@@ -6723,7 +6738,7 @@ function renderInput(field, override) {
6723
6738
  case "boolean":
6724
6739
  return wrap(` <input type="checkbox" checked={!!form.${name}} onChange={(e) => onChange('${name}', e.target.checked as any)} />`);
6725
6740
  case "date":
6726
- return wrap(` <input type="date"${required} value={(form.${name} as any) ?? ''} onChange={(e) => onChange('${name}', e.target.value as any)} />`);
6741
+ return wrap(` <input type="date"${required} value={toDateInput(form.${name})} onChange={(e) => onChange('${name}', e.target.value as any)} />`);
6727
6742
  case "select": {
6728
6743
  const opts = (spec.options ?? []).map((o) => ` <option value=${JSON.stringify(o)}>${escapeJsx(o)}</option>`).join(`
6729
6744
  `);
@@ -1,2 +1,2 @@
1
- export declare const TEMPLATES_VERSION = "0.1.5";
1
+ export declare const TEMPLATES_VERSION = "0.1.7";
2
2
  export declare const TEMPLATE_FILES: Record<string, string>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vsceasy/cli",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Build VS Code extensions fast — React UI + typed RPC bridge between extension and webview + file-based routing for panels, commands, menus, tree views, and subpanels.",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -9,21 +9,35 @@ type FormState = Partial<{{Name}}>;
9
9
 
10
10
  const emptyForm: FormState = {{emptyFormLiteral}};
11
11
 
12
+ // `<input type="date">` only accepts a `yyyy-MM-dd` value. Stored dates may be
13
+ // ISO strings or Date objects, so normalize before binding to the input.
14
+ function toDateInput(v: unknown): string {
15
+ if (v == null || v === '') return '';
16
+ const d = v instanceof Date ? v : new Date(v as string);
17
+ return Number.isNaN(d.getTime()) ? '' : d.toISOString().slice(0, 10);
18
+ }
19
+
12
20
  export function App() {
13
21
  const [form, setForm] = useState<FormState>(emptyForm);
14
22
  const [editingId, setEditingId] = useState<{{Name}}['{{primaryKey}}'] | null>(null);
15
23
  const [error, setError] = useState<string | null>(null);
16
24
  const [saving, setSaving] = useState(false);
17
25
 
18
- const load = useCallback(async () => {
19
- // The list stashes the row id before revealing this panel. Pull it (the host
20
- // clears it after handing it over) and pre-fill the form for editing.
26
+ const load = useCallback(async (initial: boolean) => {
27
+ // The list stashes a row id before revealing this panel. Pull it (the host
28
+ // clears it after handing it over).
21
29
  const id = await api.pendingId();
22
30
  if (id == null || id === '') {
23
- setForm(emptyForm);
24
- setEditingId(null);
31
+ // No row was requested. On the first mount, start with an empty "new" form.
32
+ // On later reveals (focus/visibility), DON'T reset — that would wipe a form
33
+ // the user is busy filling in. Just leave the current state as-is.
34
+ if (initial) {
35
+ setForm(emptyForm);
36
+ setEditingId(null);
37
+ }
25
38
  return;
26
39
  }
40
+ // The list asked to edit a specific row — load it, replacing the current form.
27
41
  const row = await api.get(id);
28
42
  if (row) {
29
43
  setForm(row);
@@ -32,11 +46,12 @@ export function App() {
32
46
  }, []);
33
47
 
34
48
  useEffect(() => {
35
- void load();
36
- // Webviews retain state when hidden, so re-load whenever the panel is
37
- // revealed — the list may have asked to edit a different row.
38
- const onFocus = () => { void load(); };
39
- const onVisible = () => { if (document.visibilityState === 'visible') void load(); };
49
+ void load(true);
50
+ // Webviews retain state when hidden, so re-check on reveal: the list may have
51
+ // asked to edit a different row. When nothing is pending, `load` leaves the
52
+ // in-progress form untouched (see above).
53
+ const onFocus = () => { void load(false); };
54
+ const onVisible = () => { if (document.visibilityState === 'visible') void load(false); };
40
55
  window.addEventListener('focus', onFocus);
41
56
  document.addEventListener('visibilitychange', onVisible);
42
57
  return () => {