forgecad 0.1.3 → 0.1.5

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/index.html CHANGED
@@ -8,7 +8,7 @@
8
8
  * { margin: 0; padding: 0; box-sizing: border-box; }
9
9
  html, body, #root { width: 100%; height: 100%; overflow: hidden; background: var(--fc-bg); color: var(--fc-text); font-family: system-ui, -apple-system, sans-serif; }
10
10
  </style>
11
- <script type="module" crossorigin src="/assets/index-Dvz3nSDc.js"></script>
11
+ <script type="module" crossorigin src="/assets/index-DFa4fntx.js"></script>
12
12
  </head>
13
13
  <body>
14
14
  <div id="root"></div>
@@ -14746,6 +14746,42 @@ var Assembly = class {
14746
14746
  function assembly(name) {
14747
14747
  return new Assembly(name);
14748
14748
  }
14749
+ var ImportedAssembly = class {
14750
+ constructor(_assembly) {
14751
+ this._assembly = _assembly;
14752
+ }
14753
+ /** The underlying Assembly — use for sweepJoint, addPart into parent, etc. */
14754
+ get assembly() {
14755
+ return this._assembly;
14756
+ }
14757
+ /** Solve the assembly at the given joint state (defaults to each joint's default value). */
14758
+ solve(state) {
14759
+ return this._assembly.solve(state);
14760
+ }
14761
+ /**
14762
+ * Return a specific named part positioned at the given joint state.
14763
+ * Result type mirrors SolvedAssembly.getPart(): Shape, TrackedShape, or ShapeGroup.
14764
+ */
14765
+ part(name, state) {
14766
+ return this._assembly.solve(state).getPart(name);
14767
+ }
14768
+ /**
14769
+ * Convert all assembly parts to a ShapeGroup with named children.
14770
+ * Child names match the part names used in the assembly.
14771
+ * Useful for embedding a solved sub-assembly in a parent group or assembly.
14772
+ */
14773
+ toGroup(state) {
14774
+ const solved = this._assembly.solve(state);
14775
+ const def = this._assembly.describe();
14776
+ const children = [];
14777
+ const childNames = [];
14778
+ for (const p of def.parts) {
14779
+ children.push(solved.getPart(p.name));
14780
+ childNames.push(p.name);
14781
+ }
14782
+ return new ShapeGroup(children, childNames);
14783
+ }
14784
+ };
14749
14785
 
14750
14786
  // src/forge/robotExport.ts
14751
14787
  var _collectedRobotExport = null;
@@ -17869,6 +17905,8 @@ function describeScriptResultType(value) {
17869
17905
  if (value instanceof Sketch) return "Sketch";
17870
17906
  if (value instanceof TrackedShape) return "TrackedShape";
17871
17907
  if (value instanceof ShapeGroup) return "ShapeGroup";
17908
+ if (value instanceof Assembly) return "Assembly";
17909
+ if (value instanceof ImportedAssembly) return "ImportedAssembly";
17872
17910
  if (Array.isArray(value)) return "Array";
17873
17911
  if (typeof value === "object" && typeof value.toShape === "function") {
17874
17912
  try {
@@ -18288,6 +18326,26 @@ function executeFile(code, fileName, allFiles, visited, scope = {}, options, exe
18288
18326
  logImportTrace(fileName, scope, options, "importGroup", resolvedPath, "error", { requested: name, got });
18289
18327
  throw new Error(`"${resolvedPath}" did not return a ShapeGroup (got ${got}). Use group(...) as the return value, or use importPart() for single-shape files.`);
18290
18328
  };
18329
+ const importAssembly = (name, paramOverrides) => {
18330
+ const { source: src, lookupKey, resolvedPath } = resolveImportSource(fileName, name, allFiles, options);
18331
+ const localOverrides = parseImportParamArgs("importAssembly", name, paramOverrides);
18332
+ const childScope = { namePrefix: makeChildScopePrefix(resolvedPath), localOverrides };
18333
+ logImportTrace(fileName, scope, options, "importAssembly", resolvedPath, "start", { requested: name, overrides: localOverrides });
18334
+ let result;
18335
+ try {
18336
+ result = executeFile(src, lookupKey, allFiles, visited, childScope, options);
18337
+ } catch (error) {
18338
+ logImportTrace(fileName, scope, options, "importAssembly", resolvedPath, "error", { requested: name, error: formatLogError(error) });
18339
+ throw error;
18340
+ }
18341
+ if (result instanceof Assembly) {
18342
+ logImportTrace(fileName, scope, options, "importAssembly", resolvedPath, "success", { requested: name, got: "Assembly" });
18343
+ return new ImportedAssembly(result);
18344
+ }
18345
+ const got = describeScriptResultType(result);
18346
+ logImportTrace(fileName, scope, options, "importAssembly", resolvedPath, "error", { requested: name, got });
18347
+ throw new Error(`"${resolvedPath}" did not return an Assembly (got ${got}). Return the assembly() instance directly (before calling .solve()).`);
18348
+ };
18291
18349
  const unwrap2 = (s) => s instanceof TrackedShape ? s.toShape() : s;
18292
18350
  const wrappedUnion = (...shapes) => union(...shapes.map(unwrap2));
18293
18351
  const wrappedDifference = (...shapes) => difference(...shapes.map(unwrap2));
@@ -18375,6 +18433,7 @@ function executeFile(code, fileName, allFiles, visited, scope = {}, options, exe
18375
18433
  importSketch,
18376
18434
  importPart,
18377
18435
  importGroup,
18436
+ importAssembly,
18378
18437
  importSvgSketch,
18379
18438
  dim,
18380
18439
  dimLine,
@@ -29242,8 +29301,8 @@ var Loader = class {
29242
29301
  */
29243
29302
  loadAsync(url, onProgress) {
29244
29303
  const scope = this;
29245
- return new Promise(function(resolve22, reject) {
29246
- scope.load(url, resolve22, onProgress, reject);
29304
+ return new Promise(function(resolve23, reject) {
29305
+ scope.load(url, resolve23, onProgress, reject);
29247
29306
  });
29248
29307
  }
29249
29308
  /**
@@ -40514,7 +40573,7 @@ async function waitForRenderHtml(port, timeoutMs) {
40514
40573
  const deadline = Date.now() + timeoutMs;
40515
40574
  while (Date.now() < deadline) {
40516
40575
  if (await fetchRenderHtml(port)) return true;
40517
- await new Promise((resolve22) => setTimeout(resolve22, 250));
40576
+ await new Promise((resolve23) => setTimeout(resolve23, 250));
40518
40577
  }
40519
40578
  return false;
40520
40579
  }
@@ -40564,7 +40623,7 @@ ${detail}` : `Timed out waiting for Vite on port ${port}.`
40564
40623
  async function stopDevServer(proc) {
40565
40624
  if (!proc || proc.killed) return;
40566
40625
  proc.kill("SIGTERM");
40567
- await new Promise((resolve22) => setTimeout(resolve22, 150));
40626
+ await new Promise((resolve23) => setTimeout(resolve23, 150));
40568
40627
  if (!proc.killed) {
40569
40628
  proc.kill("SIGKILL");
40570
40629
  }
@@ -41160,13 +41219,13 @@ function parseCli2(argv) {
41160
41219
  };
41161
41220
  }
41162
41221
  function readStdin() {
41163
- return new Promise((resolve22, reject) => {
41222
+ return new Promise((resolve23, reject) => {
41164
41223
  let data = "";
41165
41224
  input.setEncoding("utf8");
41166
41225
  input.on("data", (chunk) => {
41167
41226
  data += chunk;
41168
41227
  });
41169
- input.on("end", () => resolve22(data));
41228
+ input.on("end", () => resolve23(data));
41170
41229
  input.on("error", reject);
41171
41230
  });
41172
41231
  }
@@ -41594,12 +41653,12 @@ function findProjectRoot2(sp) {
41594
41653
  return root;
41595
41654
  }
41596
41655
  async function isPortOpen(port) {
41597
- return new Promise((resolve22) => {
41656
+ return new Promise((resolve23) => {
41598
41657
  const server = createServer3();
41599
- server.once("error", () => resolve22(false));
41658
+ server.once("error", () => resolve23(false));
41600
41659
  server.once("listening", () => {
41601
41660
  server.close();
41602
- resolve22(true);
41661
+ resolve23(true);
41603
41662
  });
41604
41663
  server.listen(port, "127.0.0.1");
41605
41664
  });
@@ -41614,12 +41673,12 @@ async function ensureDevServer2(port) {
41614
41673
  stdio: "pipe",
41615
41674
  detached: false
41616
41675
  });
41617
- await new Promise((resolve22, reject) => {
41676
+ await new Promise((resolve23, reject) => {
41618
41677
  const timeout = setTimeout(() => reject(new Error("Vite startup timeout")), 15e3);
41619
41678
  viteProcess.stdout.on("data", (data) => {
41620
41679
  if (data.toString().includes("ready")) {
41621
41680
  clearTimeout(timeout);
41622
- resolve22();
41681
+ resolve23();
41623
41682
  }
41624
41683
  });
41625
41684
  viteProcess.on("error", (e) => {
@@ -42777,14 +42836,14 @@ function sendJson(res, status, payload) {
42777
42836
  res.end(JSON.stringify(payload));
42778
42837
  }
42779
42838
  function readJsonBody(req) {
42780
- return new Promise((resolve22, reject) => {
42839
+ return new Promise((resolve23, reject) => {
42781
42840
  let body = "";
42782
42841
  req.on("data", (chunk) => {
42783
42842
  body += chunk;
42784
42843
  });
42785
42844
  req.on("end", () => {
42786
42845
  try {
42787
- resolve22(body ? JSON.parse(body) : {});
42846
+ resolve23(body ? JSON.parse(body) : {});
42788
42847
  } catch (e) {
42789
42848
  reject(e);
42790
42849
  }
@@ -42897,10 +42956,10 @@ function serveStatic(distDir, req, res) {
42897
42956
  return true;
42898
42957
  }
42899
42958
  function isPortAvailable(port, host) {
42900
- return new Promise((resolve22) => {
42959
+ return new Promise((resolve23) => {
42901
42960
  const s = http.createServer();
42902
- s.listen(port, host, () => s.close(() => resolve22(true)));
42903
- s.on("error", () => resolve22(false));
42961
+ s.listen(port, host, () => s.close(() => resolve23(true)));
42962
+ s.on("error", () => resolve23(false));
42904
42963
  });
42905
42964
  }
42906
42965
  async function pickPort(preferred, host, strict) {
@@ -43034,11 +43093,11 @@ data: ${JSON.stringify(entries)}
43034
43093
  res.end("Not found");
43035
43094
  }
43036
43095
  });
43037
- await new Promise((resolve22) => server.listen(port, host, resolve22));
43096
+ await new Promise((resolve23) => server.listen(port, host, resolve23));
43038
43097
  const displayHost = host === "0.0.0.0" ? "localhost" : host;
43039
43098
  const url = `http://${displayHost}:${port}`;
43040
43099
  if (open) openBrowser(url);
43041
- const close4 = () => new Promise((resolve22) => {
43100
+ const close4 = () => new Promise((resolve23) => {
43042
43101
  watcher?.close();
43043
43102
  sseClients.forEach((c) => {
43044
43103
  try {
@@ -43047,7 +43106,7 @@ data: ${JSON.stringify(entries)}
43047
43106
  }
43048
43107
  });
43049
43108
  sseClients.clear();
43050
- server.close(() => resolve22());
43109
+ server.close(() => resolve23());
43051
43110
  });
43052
43111
  return { url, close: close4 };
43053
43112
  }
@@ -43130,9 +43189,9 @@ function toViteArgs(options) {
43130
43189
  return args;
43131
43190
  }
43132
43191
  function waitForExit(child) {
43133
- return new Promise((resolve22, reject) => {
43192
+ return new Promise((resolve23, reject) => {
43134
43193
  child.once("error", reject);
43135
- child.once("exit", (code) => resolve22(code ?? 0));
43194
+ child.once("exit", (code) => resolve23(code ?? 0));
43136
43195
  });
43137
43196
  }
43138
43197
  async function runStudioCli(argv = process.argv.slice(2)) {
@@ -43860,7 +43919,7 @@ Dimension list${showAll ? "" : " (first 20)"}:`);
43860
43919
  // cli/forge-skill.ts
43861
43920
  import { cpSync, existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "fs";
43862
43921
  import { homedir as homedir2 } from "os";
43863
- import { join as join7 } from "path";
43922
+ import { join as join7, resolve as resolve22 } from "path";
43864
43923
  async function runSkillInstallCli(_argv = []) {
43865
43924
  const srcSkill = resolvePackagePath(import.meta.url, "dist-skill", "SKILL.md");
43866
43925
  const srcDocs = resolvePackagePath(import.meta.url, "dist-skill", "docs");
@@ -43881,6 +43940,26 @@ If you are running from a source checkout, run: npm run build:skill:forgecad`
43881
43940
  console.log(`ForgeCAD skill installed to ${dest}`);
43882
43941
  console.log(`Reload your agent (Claude Code, Codex, OpenCode, \u2026) to activate.`);
43883
43942
  }
43943
+ async function runSkillOneFileCli(argv = []) {
43944
+ const outputArg = argv.find((a) => !a.startsWith("-"));
43945
+ if (!outputArg) {
43946
+ throw new Error(
43947
+ `Usage: forgecad skill one-file <output-path>
43948
+ Example: forgecad skill one-file ~/Desktop/forgecad-context.md`
43949
+ );
43950
+ }
43951
+ const src = resolvePackagePath(import.meta.url, "dist-skill", "CONTEXT.md");
43952
+ if (!existsSync6(src)) {
43953
+ throw new Error(
43954
+ `Built context file not found at ${src}.
43955
+ If you are running from a source checkout, run: npm run build:skill:forgecad`
43956
+ );
43957
+ }
43958
+ const dest = resolve22(outputArg);
43959
+ writeFileSync8(dest, readFileSync15(src));
43960
+ console.log(`ForgeCAD context written to ${dest}`);
43961
+ console.log(`Paste the contents into any AI chat UI (Claude.ai, ChatGPT, Gemini, \u2026) to get full ForgeCAD API knowledge.`);
43962
+ }
43884
43963
 
43885
43964
  // cli/forgecad.ts
43886
43965
  var SHELL_VALUES = [
@@ -44040,6 +44119,22 @@ var commands = [
44040
44119
  examples: ["forgecad skill install"],
44041
44120
  run: runSkillInstallCli
44042
44121
  },
44122
+ {
44123
+ group: "Shell",
44124
+ path: ["skill", "one-file"],
44125
+ summary: "Write a single self-contained context file with all ForgeCAD docs for pasting into a chat UI (Claude.ai, ChatGPT, \u2026).",
44126
+ usage: ["forgecad skill one-file <output-path>"],
44127
+ examples: [
44128
+ "forgecad skill one-file ~/Desktop/forgecad-context.md",
44129
+ "forgecad skill one-file ./forgecad-context.md"
44130
+ ],
44131
+ completion: {
44132
+ positionals: [
44133
+ { description: "output path for the context file", valueKind: "path" }
44134
+ ]
44135
+ },
44136
+ run: runSkillOneFileCli
44137
+ },
44043
44138
  {
44044
44139
  group: "Shell",
44045
44140
  path: ["__complete"],