Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,25 @@ user-facing notes live in [`docs/updates/`](docs/updates/) (English and

## Unreleased

## 2.2.2 — 2026-10-14

### Added

- Editor: code completion from the SDK itself. `scad.` (and `scad.ql.`, `scad.std.gear.`, `ql.`)
lists the real functions with their signatures and docstrings; accepting one, or typing `(`
after a known name, writes the call with every required parameter as `name=` and Tab moves
between them; inside a call the parameters not given yet complete as `name=` (required first).
The names come from the running package (`GET /api/sdk`), so they cannot drift from the code.
- Editor: a **new** action in the workspace tab starts a notebook from the SDK's template (a part,
or an assembly that uses the first two parts of the workspace), with the name checked as you
type. An empty workspace opens on that form, so `sca edit` on an empty directory is usable.

### Fixed

- Editor: the cell editors rebuilt their configuration on every render (a new `onChange` and
`basicSetup` each time), which dropped any snippet in progress and could disturb typing; both
are stable now.

## 2.2.1 — 2026-10-13

The first stable release of the 2.2 line (2.2.0b1 was a pre-release): everything
Expand Down
44 changes: 16 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,37 +149,24 @@ the full crank cycle sweeps clean in the same sim ·

---

## Update Notes (2.2.1)
## Update Notes (2.2.2)

> Validate generated definitions, assembly constraints, and manufacturing
> geometry before production use.

- **New editor.** `sca edit` is part of the SDK now (`pip install
"simplecadapi[editor]"`). It runs a workspace's notebooks in a marimo kernel
with the SDK's own cell executor, previews the live model straight from the
solids, writes your viewport picks as selection code, and lets an agent see
what you see (`sca snapshot`). **Re-Studio is removed**: the reverse-
engineering studio (the `Re-mode` workspace of the standalone sca-web-editor)
is gone, and the standalone sca-web-editor is replaced by `sca edit`. STEP
reconstruction continues through the inspection API and the skill's
step-reconstruction workflow.
- **One runtime, one cache.** The editor, `sca run` and
`simplecadapi.runtime.run_notebook()` share the per-cell cache, so what one
computed the others restore. Notebook parameters come from `# ---- params
----` cells with an optional `# ui:` comment; local modules (shared
dimensions) get their own view in the editor.
- **Landing path.** `docs/landing.md` is the script an agent follows to install
the SDK and its skill and open an example; `sca example u_link` copies the
bundled workspace and `sca skill where` shows where the skill goes.
- Earlier: 2.1.3b1 shipped the addon ecosystem (`sca addon ...`, the
`sca-addon.toml` descriptor, the `.scadpkg` format spec); see its
[update notes](docs/updates/2.1.3b1.md).

Every example model is a notebook; its export scripts load the product with
`run_notebook`, capture one `.scadpkg`, and translate AP242 `.step` and
editable `.FCStd` from that same package. The AP242/Gmsh example under
`examples/ap242_gmsh_volume_mesh/` exposes each export and FEM stage as a
separate directly runnable script.
- **Code completion from the SDK.** In the editor, `scad.` (and `scad.ql.`,
`scad.std.gear.`) lists the real functions with their signatures and
docstrings. Accepting one, or typing `(` after its name, writes the call with
every required parameter as `name=`, and Tab moves between them; inside a call
the parameters not given yet complete as `name=`.
- **New notebooks from the workspace tab.** A **new** action starts a part, or
an assembly that uses the first two parts, from the SDK's template; an empty
workspace opens on that form.
- **Fix.** The cell editors no longer rebuild their configuration on every
render, which dropped a snippet in progress.

Full notes for this version: [`docs/updates/2.2.2.md`](docs/updates/2.2.2.md).
Earlier versions: the [changelog](CHANGELOG.md) and [`docs/updates/`](docs/updates/).

---

Expand All @@ -199,7 +186,7 @@ in a compact public API for creating solids, applying features, tagging semantic
intent, querying topology, exporting manufacturing files, and translating recorded
models into FreeCAD workflows.

Current release: `simplecadapi==2.2.1`.
Current release: `simplecadapi==2.2.2`.

## What It Provides

Expand Down Expand Up @@ -714,6 +701,7 @@ and the [STEP reconstruction workflow](docs/skill/references/workflows/step-reco

## Documentation

- 2.2.2 update notes: [`docs/updates/2.2.2.md`](docs/updates/2.2.2.md)
- 2.2.1 update notes: [`docs/updates/2.2.1.md`](docs/updates/2.2.1.md)
- 2.1.3b1 update notes: [`docs/updates/2.1.3b1.md`](docs/updates/2.1.3b1.md)
- 2.1.2 update notes: [`docs/updates/2.1.2.md`](docs/updates/2.1.2.md)
Expand Down
34 changes: 12 additions & 22 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,30 +133,19 @@ CalculiX 静力 FEM(边界面按保真的 `interface.*` 标签选取,仿真

---

## 更新日志(2.2.1)
## 更新日志(2.2.2)

> 投入生产前请验证生成的定义、装配约束和制造几何。

- **新编辑器。** `sca edit` 现在是 SDK 的一部分(`pip install
"simplecadapi[editor]"`)。它用 SDK 自己的 cell 执行器在 marimo 内核里运行工作
区的 notebook,直接由实体生成实时预览,把你在视口里的点选写成选择代码,并让
agent 看到你看到的(`sca snapshot`)。**Re-Studio 已移除**:逆向工程 studio
(独立 sca-web-editor 的 `Re-mode` 工作区)不再存在,独立的 sca-web-editor 由
`sca edit` 取代。STEP 重建继续通过检查 API 和 skill 的 step-reconstruction
工作流进行。
- **一个运行时,一份缓存。** 编辑器、`sca run` 和
`simplecadapi.runtime.run_notebook()` 共用逐 cell 缓存,一方算过的另一方直接
复用。notebook 参数来自 `# ---- params ----` cell,可加 `# ui:` 注释指定控件;
本地模块(共用尺寸)在编辑器里有独立视图。
- **落地路径。** `docs/landing.md` 是 agent 安装 SDK 和 skill、打开示例时遵循的
脚本;`sca example u_link` 复制内置工作区,`sca skill where` 显示 skill 的安装位置。
- 之前:2.1.3b1 带来插件生态(`sca addon ...`、`sca-addon.toml` 描述文件、
`.scadpkg` 格式规范),见[更新说明](docs/updates/2.1.3b1.zh-CN.md)。

每个示例模型都是 notebook;导出脚本用 `run_notebook` 载入产品,捕获一个
`.scadpkg`,再从同一个产品包转换出 AP242 `.step` 和可编辑 `.FCStd`。
`examples/ap242_gmsh_volume_mesh/` 下的 AP242/Gmsh 示例把每个导出和 FEM 阶段
都做成可直接运行的独立脚本。
- **来自 SDK 的代码补全。** 在编辑器里,输入 `scad.`(以及 `scad.ql.`、`scad.std.gear.`)
会列出真实存在的函数、签名和文档说明。选中一个函数,或在函数名后输入 `(`,会把每个必填
参数写成 `name=`,Tab 在它们之间跳转;在调用括号内,还没写的参数会补全成 `name=`。
- **在工作区页面创建 notebook。** 新增的 **new** 按 SDK 模板创建一个零件,或一个使用前两个
零件的装配;空工作区会直接打开这个表单。
- **修复。** cell 编辑器不再每次渲染都重建配置,这会让进行中的占位跳转丢失。

本版本的完整说明:[`docs/updates/2.2.2.zh-CN.md`](docs/updates/2.2.2.zh-CN.md)。
更早的版本:见[更新日志](CHANGELOG.md)和 [`docs/updates/`](docs/updates/)。

---

Expand All @@ -173,7 +162,7 @@ CalculiX 静力 FEM(边界面按保真的 `interface.*` 标签选取,仿真

SimpleCADAPI 是一个基于 OCP 的 Python CAD SDK,提供清晰的函数式建模操作和可重放的模型图。它在 OpenCascade 几何内核之上提供精简的公共 API,可用于创建实体、应用特征、添加语义标签、查询拓扑、导出制造文件,以及将记录的模型转换为 FreeCAD 工作流。

当前版本:`simplecadapi==2.2.1`。
当前版本:`simplecadapi==2.2.2`。

## 核心能力

Expand Down Expand Up @@ -599,6 +588,7 @@ uv run --extra fem python examples/ap242_gmsh_volume_mesh/run_calculix.py

## 文档

- 2.2.2 更新说明:[`docs/updates/2.2.2.zh-CN.md`](docs/updates/2.2.2.zh-CN.md)
- 2.2.1 更新说明:[`docs/updates/2.2.1.zh-CN.md`](docs/updates/2.2.1.zh-CN.md)
- 2.1.3b1 更新说明:[`docs/updates/2.1.3b1.zh-CN.md`](docs/updates/2.1.3b1.zh-CN.md)
- 2.1.2 更新说明:[`docs/updates/2.1.2.zh-CN.md`](docs/updates/2.1.2.zh-CN.md)
Expand Down
4 changes: 2 additions & 2 deletions docs/skill/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ description: Build, assemble, inspect, reconstruct, and export parametric CAD mo
license: Apache-2.0
metadata:
project: simplecadapi
version: 2.2.1
version: 2.2.2
package-name: simplecadapi
package-version: 2.2.1
package-version: 2.2.2
---

# SimpleCAD SDK Skill
Expand Down
27 changes: 27 additions & 0 deletions docs/updates/2.2.2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# 2.2.2 Update Notes

Two additions to the code-first editor (`sca edit`), and a fix that made one of them usable.

## Code completion from the SDK

`scad.` (and `scad.ql.`, `scad.std.gear.`, `ql.`, `Q.`) lists the real functions with their
signatures; the side panel shows every parameter with its default and the docstring.

- Accepting a function, or typing `(` after a known name, writes the call with every
**required** parameter as `name=`: `scad.make_box_rsolid(width=, height=, depth=)`.
**Tab** moves to the next parameter.
- Inside a call, the parameters not given yet complete as `name=`, required ones first.
- The names come from the running package (`GET /api/sdk`), so they cannot drift from the code.

## Create a notebook from the workspace tab

A **new** action in the notebooks header starts a notebook from the SDK's template: a part, or an
assembly that uses the first two parts of the workspace. The name is checked as you type. An empty
workspace opens on that form, so `sca edit` on an empty directory is usable.

## Fixed

The cell editors rebuilt their configuration on every render, which dropped a snippet in progress
and could disturb typing; the editor's `onChange` and options are stable now.

See the [changelog](../../CHANGELOG.md) for the complete list.
25 changes: 25 additions & 0 deletions docs/updates/2.2.2.zh-CN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# 2.2.2 更新说明

`sca edit` 编辑器的两项新增,以及一个让其中一项能正常使用的修复。

## 来自 SDK 的代码补全

输入 `scad.`(以及 `scad.ql.`、`scad.std.gear.`、`ql.`、`Q.`)会列出真实存在的函数和签名;
右侧面板显示每个参数、默认值和文档说明。

- 选中一个函数,或在已知函数名后输入 `(`,会把每个**必填**参数写成 `name=`:
`scad.make_box_rsolid(width=, height=, depth=)`。**Tab** 跳到下一个参数。
- 在调用括号内,还没写的参数会补全成 `name=`,必填的排在最前。
- 名字来自正在运行的包(`GET /api/sdk`),不会和代码不一致。

## 在工作区页面创建 notebook

notebooks 标题处新增 **new**,按 SDK 模板创建 notebook:一个零件,或一个使用工作区前两个零件的装配。
边输入边检查名字。空工作区会直接打开这个表单,所以对空目录运行 `sca edit` 也能用了。

## 修复

cell 编辑器每次渲染都会重建配置,导致进行中的占位跳转丢失,也可能干扰输入;现在编辑器的
`onChange` 和选项都是稳定的。

完整列表见[更新日志](../../CHANGELOG.md)。
2 changes: 2 additions & 0 deletions editor-web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions editor-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
"typecheck": "tsc --noEmit -p tsconfig.json"
},
"dependencies": {
"@codemirror/autocomplete": "^6.20.3",
"@codemirror/lang-python": "^6.2.1",
"@codemirror/language": "^6.13.1",
"@codemirror/state": "^6.7.6",
"@codemirror/theme-one-dark": "^6.1.3",
"@codemirror/view": "^6.43.14",
"@lezer/highlight": "^1.2.5",
Expand Down
14 changes: 12 additions & 2 deletions editor-web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { ModuleView, type ModuleState } from "@/components/ModuleView";
import { NotebookList } from "@/components/NotebookList";
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "@/components/ui/resizable";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { fetchPreview, getWorkspace, openNotebook, postViewport, type ModuleRow, type NotebookRow } from "@/lib/api";
import { fetchPreview, getWorkspace, newNotebook, openNotebook, postViewport, type ModuleRow, type NotebookRow } from "@/lib/api";
import { parsePreview } from "@/lib/preview";
import { EnvelopeSocket, type Envelope } from "@/lib/envelope";
import { useRunLog } from "@/lib/runlog";
Expand Down Expand Up @@ -118,12 +118,14 @@ export function App() {
socket.on("hello", (envelope) => {
setNotebook((envelope["notebook"] as string) ?? null);
setCurrentPath((envelope["path"] as string) ?? null);
// an empty workspace: the way forward is the workspace tab's "new"
if (!envelope["notebook"]) setLeftTab("workspace");
setKernelAlive(Boolean(envelope["kernel"]));
// No notebook: nothing to wait for. Else the model is on its way.
if (hasModel.current) return;
const stage = envelope["notebook"] ? (envelope["kernel"] ? "building the model" : "starting the kernel") : null;
setLoading(stage);
if (stage) setStatus(`${stage[0].toUpperCase()}${stage.slice(1)}…`);
setStatus(stage ? `${stage[0].toUpperCase()}${stage.slice(1)}…` : "No notebook yet: create one in the workspace tab");
setMarimoUrl((envelope["marimo_url"] as string) ?? null);
refreshWorkspace();
});
Expand Down Expand Up @@ -312,6 +314,13 @@ export function App() {
});
};

const handleCreate = async (name: string, kind: "part" | "assembly") => {
setStatus(`creating ${name}…`);
await newNotebook(name, kind);
// the server opens it: show its cells
setLeftTab("notebook");
};

return (
<div className="flex h-screen flex-col overflow-hidden">
<Header
Expand Down Expand Up @@ -356,6 +365,7 @@ export function App() {
openModule={moduleView?.path ?? null}
onOpen={handleOpen}
onOpenModule={(path) => socketRef.current?.send({ type: "module.open", path })}
onCreate={handleCreate}
/>
</TabsContent>
{moduleView && (
Expand Down
15 changes: 11 additions & 4 deletions editor-web/src/components/CellEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useMemo, useRef, useState } from "react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import CodeMirror from "@uiw/react-codemirror";
import { python } from "@codemirror/lang-python";
import { EditorView } from "@codemirror/view";
Expand All @@ -20,6 +20,7 @@ import { Badge } from "@/components/ui/badge";
import { ScrollArea } from "@/components/ui/scroll-area";
import { ParamControl, type ParamMeta, type ParamValue } from "@/components/ParamControl";
import { cadTheme } from "@/lib/cm-theme";
import { sdkCompletion } from "@/lib/sdk-complete";
import { cn } from "@/lib/utils";

export type NotebookCell = {
Expand Down Expand Up @@ -49,7 +50,9 @@ export type { ParamMeta, ParamValue };
type SocketLike = { send: (message: Record<string, unknown>) => void } | null;

const AUTOSAVE_MS = 700;
const EXTENSIONS = [python(), EditorView.lineWrapping];
const EXTENSIONS = [python(), EditorView.lineWrapping, sdkCompletion];
/** a constant: a new object each render makes the editor wrapper rebuild its configuration, which drops a snippet in progress */
const BASIC_SETUP = { lineNumbers: false, foldGutter: false, highlightActiveLine: false };

function loadCollapsed(notebook: string | null): Set<string> {
try {
Expand Down Expand Up @@ -317,6 +320,10 @@ function CellBlock({
onDelete: () => void;
onEdit: (value: string) => void;
}) {
// the editor takes this once: a new function each render would rebuild its configuration
const onEditRef = useRef(onEdit);
onEditRef.current = onEdit;
const handleChange = useCallback((value: string) => onEditRef.current(value), []);
const failed = cell.status === "exception" || cell.status === "error" || (cell.errors?.length ?? 0) > 0;
const stale = cell.status === "stale";
const dirty = draft !== undefined && draft !== cell.code;
Expand Down Expand Up @@ -411,8 +418,8 @@ function CellBlock({
extensions={EXTENSIONS}
theme={cadTheme}
editable={!cell.readonly}
basicSetup={{ lineNumbers: false, foldGutter: false, highlightActiveLine: false }}
onChange={onEdit}
basicSetup={BASIC_SETUP}
onChange={handleChange}
onKeyDown={(event) => {
if ((event.ctrlKey || event.metaKey) && event.key === "Enter") {
event.preventDefault();
Expand Down
16 changes: 12 additions & 4 deletions editor-web/src/components/ModuleView.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from "react";
import { useCallback, useEffect, useRef, useState } from "react";
import CodeMirror from "@uiw/react-codemirror";
import { python } from "@codemirror/lang-python";
import { EditorView } from "@codemirror/view";
Expand All @@ -8,6 +8,7 @@ import { Badge } from "@/components/ui/badge";
import { ScrollArea } from "@/components/ui/scroll-area";
import { ParamControl, type ParamMeta, type ParamValue } from "@/components/ParamControl";
import { cadTheme } from "@/lib/cm-theme";
import { sdkCompletion } from "@/lib/sdk-complete";

/** A local module of the workspace (shared dimensions, helpers): what the
* server sends as the `module` envelope. */
Expand All @@ -21,7 +22,9 @@ export type ModuleState = {
};

const AUTOSAVE_MS = 700;
const EXTENSIONS = [python(), EditorView.lineWrapping];
const EXTENSIONS = [python(), EditorView.lineWrapping, sdkCompletion];
/** a constant: a new object each render makes the editor wrapper rebuild its configuration, which drops a snippet in progress */
const BASIC_SETUP = { lineNumbers: true, foldGutter: false, highlightActiveLine: false };

/** A module open next to the notebooks: its dataclass fields as controls, its
* source below. Every edit is written to the file at once, and every notebook
Expand Down Expand Up @@ -60,6 +63,11 @@ export function ModuleView({
}, AUTOSAVE_MS);
};

// the editor takes this once: a new function each render would rebuild its configuration
const editRef = useRef(edit);
editRef.current = edit;
const handleChange = useCallback((value: string) => editRef.current(value), []);

const forward = (id: string, value: ParamValue) => {
pending.current.set(id, value);
if (paramTimer.current !== null) return;
Expand Down Expand Up @@ -124,8 +132,8 @@ export function ModuleView({
height="auto"
extensions={EXTENSIONS}
theme={cadTheme}
basicSetup={{ lineNumbers: true, foldGutter: false, highlightActiveLine: false }}
onChange={edit}
basicSetup={BASIC_SETUP}
onChange={handleChange}
/>
</section>
</div>
Expand Down
Loading
Loading