@mica-js/schematic-editor 0.0.0-stage → 0.0.1
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/LICENSE +21 -0
- package/README.md +147 -2
- package/dist/index.d.ts +354 -0
- package/dist/index.js +3293 -0
- package/dist/index.js.map +1 -0
- package/package.json +32 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 DigitalEcho
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,148 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @mica-js/schematic-editor
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
原理图编辑器:把 `@mica-js/core/schematic` 的 `.sch` 文档渲染成可编辑的 Canvas 画布。只负责画布本身,属性面板、器件库、文件操作等 UI 由宿主实现。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @mica-js/schematic-editor @mica-js/core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 快速上手
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { mountSchematicEditor } from '@mica-js/schematic-editor';
|
|
15
|
+
|
|
16
|
+
const editor = mountSchematicEditor({
|
|
17
|
+
container: document.querySelector('#host')!, // 编辑器只往里 append 自己的 root
|
|
18
|
+
schematic, // SchematicDocument(.sch)
|
|
19
|
+
devices, // 可选:DeviceSymbolProvider,画布按它合成器件外形
|
|
20
|
+
onChange: (state) => renderStatus(state),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
editor.placeDevice('stm32-package:stm32g431rbt6-lqfp64'); // 抓到光标上,下一次点击落地
|
|
24
|
+
editor.fitToContent();
|
|
25
|
+
// 卸载:editor.dispose()
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 挂载选项
|
|
29
|
+
|
|
30
|
+
| 选项 | 说明 |
|
|
31
|
+
| ------------------ | ---------------------------------------------------------------------------------- |
|
|
32
|
+
| `container` | 必填;宿主提供的容器元素 |
|
|
33
|
+
| `schematic` | 必填;`SchematicDocument` |
|
|
34
|
+
| `devices` | `DeviceSymbolProvider`,缺失时画布无法合成器件外形 |
|
|
35
|
+
| `theme` | `Partial<SchematicTheme>`,默认深色 |
|
|
36
|
+
| `display` | `Partial<DisplayOptions>`:网格、吸附、引脚标签、网格尺寸与细分 |
|
|
37
|
+
| `tolerance` | 命中/几何容差(页面单位) |
|
|
38
|
+
| `historyLimit` | 撤销栈上限 |
|
|
39
|
+
| `createDesignator` | `(device, document) => string`,接管新器件位号;默认为器件的前缀策略 |
|
|
40
|
+
| `zoomLimits` | `{ minScale, maxScale }`,默认 `0.25`–`4` |
|
|
41
|
+
| `readOnly` | 禁用内建交互,`controller` 仍可写 |
|
|
42
|
+
| `toolbar` | 是否渲染画布右上角的悬浮工具条,默认 `true` |
|
|
43
|
+
| 回调 | `onChange` / `onSelectionChange` / `onToolChange` / `onError` / `onViewportChange` |
|
|
44
|
+
|
|
45
|
+
回调时序:控制器发状态 → 工具条与光标更新 → `onSelectionChange` / `onToolChange` / `onError`(仅变化时)→ `onChange(state)` → 重绘 → `onViewportChange`(仅视口变化时)。
|
|
46
|
+
|
|
47
|
+
编辑错误:被拒绝的编辑写入 `state.lastError` 并触发 `onError`,内建交互不会中断页面;直接调用 `controller.*` 时错误仍会抛出(`onError` 先触发)。
|
|
48
|
+
|
|
49
|
+
## 返回值
|
|
50
|
+
|
|
51
|
+
| 成员 | 说明 |
|
|
52
|
+
| ------------------------------------------- | ---------------------------------------------------------------------- |
|
|
53
|
+
| `controller` | 无 DOM 的 `SchematicEditorController` |
|
|
54
|
+
| `root` / `canvas` | 编辑器创建的两个 DOM 节点 |
|
|
55
|
+
| `setSchematic(document, { keepHistory? })` | 换文档;默认清空历史并重新适应内容,`keepHistory` 时作为一次可撤销编辑 |
|
|
56
|
+
| `setTheme(partial)` / `getTheme()` | 增量换主题 / 读回当前主题 |
|
|
57
|
+
| `setDisplayOptions(patch)` | 网格、吸附、引脚标签、网格尺寸与细分 |
|
|
58
|
+
| `setTool(tool)` | `select` / `wire` / `power` / `ground` / `label` / `text` |
|
|
59
|
+
| `placeDevice(device)` / `cancelPlacement()` | 抓取一个器件 / 取消抓取 |
|
|
60
|
+
| `fitToContent()` / `zoomTo(scale)` | 适应内容 / 按比例缩放(夹到 `zoomLimits`) |
|
|
61
|
+
| `getViewport()` / `setViewport(state)` | 读回/恢复缩放与平移 |
|
|
62
|
+
| `toPage(point)` / `toScreen(point)` | 客户端坐标 ↔ 页面坐标,宿主定位浮层时用 |
|
|
63
|
+
| `toJSON()` | 序列化为 `.sch` JSON |
|
|
64
|
+
| `focus()` | 把键盘焦点交还画布 |
|
|
65
|
+
| `resize()` / `invalidate()` | 容器显隐后重新测量 / 只重绘不改布局 |
|
|
66
|
+
| `undo()` / `redo()` / `dispose()` | 历史 / 解绑监听并移除 root |
|
|
67
|
+
|
|
68
|
+
## 宿主 UI 合同
|
|
69
|
+
|
|
70
|
+
编辑器只内建**画布**与**悬浮工具条**;其余 UI 由宿主读状态、写方法:
|
|
71
|
+
|
|
72
|
+
| 宿主 UI | 读 | 写 |
|
|
73
|
+
| ----------- | ----------------------------------- | ------------------------------------------------------------------------------------------ |
|
|
74
|
+
| 工具条 | `state.tool`、`state.placingDevice` | `setTool()` / `placeDevice()` / `cancelPlacement()`(`toolbar: false` 时自建) |
|
|
75
|
+
| 撤销 / 重做 | `state.canUndo` / `state.canRedo` | `undo()` / `redo()` |
|
|
76
|
+
| 缩放 / 适应 | `onViewportChange`、`getViewport()` | `zoomTo()` / `fitToContent()` / `setViewport()` |
|
|
77
|
+
| 网格与吸附 | `state.display` | `setDisplayOptions()` |
|
|
78
|
+
| 属性面板 | `state.inspector` | `controller.setSymbolProperty` / `removeSymbolProperty` / `renameDesignator` / `renameNet` |
|
|
79
|
+
| 错误提示 | `state.lastError`、`onError` | — |
|
|
80
|
+
|
|
81
|
+
`state.inspector` 的渲染约定(参考实现见 [apps/playground](../../apps/playground/src/components/property-panel.tsx)):
|
|
82
|
+
|
|
83
|
+
- `kind === 'wire'`:只读信息(`id`、`netName`、`points`);选中交点、多选或空选时为 `undefined`。
|
|
84
|
+
- `SymbolInspector`:`role` 决定标题,`designator`(器件)与 `net`(网标/电源/地)可编辑,`x` / `y` / `rotation` 只读。
|
|
85
|
+
- `properties` 是 `PropertyField[]`,直接对应器件声明(#7):`type` 为 `string` / `integer` / `number` / `boolean` / `color` / `enum` / `image` / `binary`,并带 `title`、`description`、`options`、`min` / `max` / `exclusiveMin` / `step` / `unit`、`maxLength`、`alpha`、`maxBytes` / `maxPixels` 等约束。`value` 缺省表示继承 `defaultValue`,`null` 表示显式空值(仅 `nullable` 属性)。
|
|
86
|
+
- `image` / `binary` 的值是资源引用 `{ src, mode }`;编辑器只传递引用,读取与预览由宿主负责。
|
|
87
|
+
|
|
88
|
+
### 网格与吸附
|
|
89
|
+
|
|
90
|
+
| 选项 | 默认 | 作用 |
|
|
91
|
+
| ------------------ | ---- | -------------------------------------- |
|
|
92
|
+
| `gridSize` | `10` | 吸附步长(页面单位),也决定每格大小 |
|
|
93
|
+
| `gridSubdivisions` | `2` | 每格画几条可见网格线,`1` 只画吸附网格 |
|
|
94
|
+
|
|
95
|
+
强调线每 5 格一条;缩得太小时渲染器会自动丢掉细分,避免糊成一片。
|
|
96
|
+
|
|
97
|
+
### 器件放置
|
|
98
|
+
|
|
99
|
+
编辑器不内建器件库:
|
|
100
|
+
|
|
101
|
+
- 抓取后光标变为十字,幽灵随指针移动(只吸附网格;`builtin:label` / `power` / `ground` 仍吸附引脚并引出一段导线)。
|
|
102
|
+
- 空格旋转幽灵,`Esc` 或 `cancelPlacement()` 取消;放下后自动切回 `select`,一次抓取只放一个。
|
|
103
|
+
- 位号来自器件定义的 `designatorPrefix`(取该前缀下最小的未占用编号,未声明时回退 `U`),也可用 `createDesignator` 完全接管。
|
|
104
|
+
- 非法器件引用直接抛 `SchematicError`,不进入抓取态。
|
|
105
|
+
|
|
106
|
+
### 指针与键盘
|
|
107
|
+
|
|
108
|
+
- 单击引脚(整条引脚线)以引脚端起笔;画导线时吸附到附近导线并垂直落下,可直接点出 T 型连接(落在导线内部会拆线建交点)。
|
|
109
|
+
- 单击器件、网标、电源/地、文本会选中实体,不会误起笔;拖动网标、电源/地时,相连导线按「沿导线方向只改长度、有垂直位移则折一段」跟随。
|
|
110
|
+
- 拖动导线段沿法线移动并吸附网格,平行连续段一起移动,端点连着引脚/锚点/交点/导线时保持不动并折出 dogleg;一次拖动一步历史。
|
|
111
|
+
- 拖动器件按落点吸附网格,单轴拖动时另一轴不动;位号可拖动。
|
|
112
|
+
- `Delete` 删除、空格或 `R` 旋转、`F` 适应内容、`Enter`/双击结束导线、`Esc` 取消、`Ctrl/Cmd+Z` 撤销、`Ctrl/Cmd+Shift+Z` 重做、滚轮缩放、中键或空格拖拽平移。
|
|
113
|
+
- 宿主在自己的 UI 里抓取器件时(焦点不在编辑器内),编辑器会在 document 上兜底监听 `Esc`。
|
|
114
|
+
|
|
115
|
+
## 控制器
|
|
116
|
+
|
|
117
|
+
`SchematicEditorController` 不依赖 DOM,可单独用于批量编辑或 Node 环境:
|
|
118
|
+
|
|
119
|
+
- 读状态:`state`、`document`、`selection`、`tool`、`display`、`canUndo`、`canRedo`。
|
|
120
|
+
- 工具:`setTool`、`placeDevice`、`cancelPlacement`、`nextDesignator`、`setSelection`、`setDisplayOptions`、`reset`、`replace`。
|
|
121
|
+
- 编辑:`dispatch`、`transaction`、`undo`、`redo`、`moveSelectionBy`、`rotateSelection`、`deleteSelection`、`addWire`、`connectWireAt`、`moveWireSegment`、`previewWireSegment`、`moveReferenceBy`、`setSymbolProperty`、`removeSymbolProperty`、`renameDesignator`、`renameNet`。
|
|
122
|
+
- 查询:`inspect`、`topology`、`hitTest`、`subscribe`。
|
|
123
|
+
|
|
124
|
+
## 内部结构
|
|
125
|
+
|
|
126
|
+
| 目录 | 职责 |
|
|
127
|
+
| ---------------------- | ---------------------------------------------------------------------------- |
|
|
128
|
+
| `controller/` | 会话状态与命令桥接:选择、工具、历史、inspector |
|
|
129
|
+
| `editor/interactions/` | 指针与键盘状态机:`input.ts` 分发,`move` / `segment` / `wire` / `placement` |
|
|
130
|
+
| `editor/snapping/` | 吸附与放置辅助(节点、导线本体、网格、label stub),无 DOM 的纯函数 |
|
|
131
|
+
| `render/` | 场景构建(`scene.ts`)与 canvas 绘制(`canvas-renderer.ts`) |
|
|
132
|
+
| `styles/` | 编辑器自带的极少量 CSS(工具条与画布容器) |
|
|
133
|
+
|
|
134
|
+
## 开发
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
pnpm --filter @mica-js/playground dev # 浏览器 playground
|
|
138
|
+
pnpm --filter @mica-js/schematic-editor run test
|
|
139
|
+
pnpm --filter @mica-js/schematic-editor run typecheck
|
|
140
|
+
pnpm --filter @mica-js/schematic-editor run build
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
可运行示例、宿主侧器件列表与事件回调演示见 [apps/playground](../../apps/playground)。
|
|
144
|
+
|
|
145
|
+
## 相关提案
|
|
146
|
+
|
|
147
|
+
- 原理图格式:[#6](https://github.com/x-botlab/mica-js/issues/6)
|
|
148
|
+
- 属性定义:[#7](https://github.com/x-botlab/mica-js/issues/7)
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import { DesignatorFactory } from '@mica-js/core/schematic';
|
|
2
|
+
import { DeviceSymbolProvider } from '@mica-js/core/schematic';
|
|
3
|
+
import { ElementIdFactory } from '@mica-js/core/schematic';
|
|
4
|
+
import { HitTestOptions } from '@mica-js/core/schematic';
|
|
5
|
+
import { Point } from '@mica-js/core/schematic';
|
|
6
|
+
import { PropertyDescriptor as PropertyDescriptor_2 } from '@mica-js/core/schematic';
|
|
7
|
+
import { PropertyValue } from '@mica-js/core/schematic';
|
|
8
|
+
import { SchematicCommand } from '@mica-js/core/schematic';
|
|
9
|
+
import { SchematicDocument } from '@mica-js/core/schematic';
|
|
10
|
+
import { SchematicError } from '@mica-js/core/schematic';
|
|
11
|
+
import { SchematicHit } from '@mica-js/core/schematic';
|
|
12
|
+
import { SchematicTopology } from '@mica-js/core/schematic';
|
|
13
|
+
import { SymbolRole } from '@mica-js/core/schematic';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Default dark theme: neutral greys on near-black, matching the reference palette
|
|
17
|
+
* (background `#0a0a0a`, foreground `#fafafa`, muted `#a1a1a1`, ring `#737373`).
|
|
18
|
+
*/
|
|
19
|
+
export declare const DARK_SCHEMATIC_THEME: SchematicTheme;
|
|
20
|
+
|
|
21
|
+
export declare const DEFAULT_DISPLAY_OPTIONS: DisplayOptions;
|
|
22
|
+
|
|
23
|
+
export declare const DEFAULT_VIEWPORT_LIMITS: ViewportLimits;
|
|
24
|
+
|
|
25
|
+
export declare interface DisplayOptions {
|
|
26
|
+
readonly grid: boolean;
|
|
27
|
+
readonly snap: boolean;
|
|
28
|
+
readonly labels: boolean;
|
|
29
|
+
/** Snap step in page units. */
|
|
30
|
+
readonly gridSize: number;
|
|
31
|
+
/** Visible grid lines per snap cell; `1` shows only the snap grid. */
|
|
32
|
+
readonly gridSubdivisions: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Field subset the canvas needs. Reading it never builds the inspector, so a repaint (pointer
|
|
37
|
+
* hover, drag preview) stays cheap.
|
|
38
|
+
*/
|
|
39
|
+
declare interface EditorRenderState {
|
|
40
|
+
readonly document: SchematicDocument;
|
|
41
|
+
readonly selection: readonly EditorSelection[];
|
|
42
|
+
readonly tool: EditorTool;
|
|
43
|
+
readonly display: DisplayOptions;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export declare type EditorSelection = {
|
|
47
|
+
readonly kind: 'symbol';
|
|
48
|
+
readonly id: string;
|
|
49
|
+
} | {
|
|
50
|
+
readonly kind: 'reference';
|
|
51
|
+
readonly symbolId: string;
|
|
52
|
+
} | {
|
|
53
|
+
readonly kind: 'wire';
|
|
54
|
+
readonly id: string;
|
|
55
|
+
} | {
|
|
56
|
+
readonly kind: 'junction';
|
|
57
|
+
readonly id: string;
|
|
58
|
+
} | {
|
|
59
|
+
readonly kind: 'vertex';
|
|
60
|
+
readonly wireId: string;
|
|
61
|
+
readonly index: number;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
declare interface EditorSessionOptions {
|
|
65
|
+
readonly document: SchematicDocument;
|
|
66
|
+
readonly devices?: DeviceSymbolProvider;
|
|
67
|
+
readonly tolerance?: number;
|
|
68
|
+
readonly historyLimit?: number;
|
|
69
|
+
readonly display?: Partial<DisplayOptions>;
|
|
70
|
+
readonly createId?: ElementIdFactory;
|
|
71
|
+
/** Designator policy; defaults to the device's `designatorPrefix` (`U` when it declares none). */
|
|
72
|
+
readonly createDesignator?: DesignatorFactory;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export declare interface EditorState {
|
|
76
|
+
readonly document: SchematicDocument;
|
|
77
|
+
readonly selection: readonly EditorSelection[];
|
|
78
|
+
readonly tool: EditorTool;
|
|
79
|
+
/** Device armed for placement by the host's device list; `undefined` when idle. */
|
|
80
|
+
readonly placingDevice: string | undefined;
|
|
81
|
+
readonly display: DisplayOptions;
|
|
82
|
+
readonly canUndo: boolean;
|
|
83
|
+
readonly canRedo: boolean;
|
|
84
|
+
readonly inspector: Inspector;
|
|
85
|
+
/** Last captured edit error; cleared by the next successful edit. */
|
|
86
|
+
readonly lastError: SchematicError | undefined;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export declare type EditorTool = 'select' | 'wire' | 'power' | 'ground' | 'label' | 'text';
|
|
90
|
+
|
|
91
|
+
export declare type Inspector = SymbolInspector | WireInspector | undefined;
|
|
92
|
+
|
|
93
|
+
export declare const LIGHT_SCHEMATIC_THEME: SchematicTheme;
|
|
94
|
+
|
|
95
|
+
export declare interface MountedSchematicEditor {
|
|
96
|
+
readonly controller: SchematicEditorController;
|
|
97
|
+
readonly root: HTMLElement;
|
|
98
|
+
readonly canvas: HTMLCanvasElement;
|
|
99
|
+
/**
|
|
100
|
+
* Loads another document. By default history and selection start over; `keepHistory` records
|
|
101
|
+
* the swap as one undoable edit instead.
|
|
102
|
+
*/
|
|
103
|
+
setSchematic(document: SchematicDocument, options?: {
|
|
104
|
+
readonly keepHistory?: boolean;
|
|
105
|
+
}): void;
|
|
106
|
+
setTheme(theme: Partial<SchematicTheme>): void;
|
|
107
|
+
getTheme(): SchematicTheme;
|
|
108
|
+
setDisplayOptions(patch: Partial<DisplayOptions>): void;
|
|
109
|
+
setTool(tool: EditorTool): void;
|
|
110
|
+
/**
|
|
111
|
+
* Arms placement of `device`: the next canvas click inserts it. Hosts drive this from their own
|
|
112
|
+
* device list, so the editor never needs to know where the parts come from.
|
|
113
|
+
*
|
|
114
|
+
* @throws {SchematicError} when the device reference is malformed.
|
|
115
|
+
*/
|
|
116
|
+
placeDevice(device: string): void;
|
|
117
|
+
/** Disarms a pending placement without touching the document. */
|
|
118
|
+
cancelPlacement(): void;
|
|
119
|
+
fitToContent(): void;
|
|
120
|
+
zoomTo(scale: number): void;
|
|
121
|
+
/** Current zoom/pan; safe to persist and hand back to {@link setViewport}. */
|
|
122
|
+
getViewport(): ViewportState;
|
|
123
|
+
/** Restores a saved viewport; the scale is clamped to `zoomLimits` around the canvas centre. */
|
|
124
|
+
setViewport(viewport: ViewportState): void;
|
|
125
|
+
/** Serializes the current document as `.sch` JSON. */
|
|
126
|
+
toJSON(): string;
|
|
127
|
+
/** Moves keyboard focus to the canvas surface; call after host toolbar interactions. */
|
|
128
|
+
focus(): void;
|
|
129
|
+
/** Canvas-relative client coordinates → page coordinates (for host overlays). */
|
|
130
|
+
toPage(clientPoint: Point): Point;
|
|
131
|
+
/** Page coordinates → canvas-relative CSS pixels (for host overlays). */
|
|
132
|
+
toScreen(pagePoint: Point): Point;
|
|
133
|
+
/** Re-measures the container; call after showing a previously hidden editor. */
|
|
134
|
+
resize(): void;
|
|
135
|
+
/** Repaints without touching layout (e.g. after a device-pixel-ratio change). */
|
|
136
|
+
invalidate(): void;
|
|
137
|
+
undo(): void;
|
|
138
|
+
redo(): void;
|
|
139
|
+
/** Removes the DOM and listeners created by the editor, keeping the container contents. */
|
|
140
|
+
dispose(): void;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Mounts a canvas schematic editor inside `options.container`. */
|
|
144
|
+
export declare function mountSchematicEditor(options: MountSchematicEditorOptions): MountedSchematicEditor;
|
|
145
|
+
|
|
146
|
+
export declare interface MountSchematicEditorOptions {
|
|
147
|
+
/** Caller-provided container; the editor only appends and removes its own root. */
|
|
148
|
+
readonly container: HTMLElement;
|
|
149
|
+
readonly schematic: SchematicDocument;
|
|
150
|
+
readonly devices?: DeviceSymbolProvider;
|
|
151
|
+
readonly theme?: Partial<SchematicTheme>;
|
|
152
|
+
readonly display?: Partial<DisplayOptions>;
|
|
153
|
+
readonly tolerance?: number;
|
|
154
|
+
readonly historyLimit?: number;
|
|
155
|
+
/** Reference designator policy; defaults to the device's `designatorPrefix`, then `U`. */
|
|
156
|
+
readonly createDesignator?: DesignatorFactory;
|
|
157
|
+
/** Zoom range for wheel, fit and `zoomTo`; defaults to 0.25×–4×. */
|
|
158
|
+
readonly zoomLimits?: Partial<ViewportLimits>;
|
|
159
|
+
/** Disables the built-in interactions; the controller stays writable. */
|
|
160
|
+
readonly readOnly?: boolean;
|
|
161
|
+
/** Renders the floating tool strip in the canvas corner; defaults to true. */
|
|
162
|
+
readonly toolbar?: boolean;
|
|
163
|
+
readonly onChange?: (state: EditorState) => void;
|
|
164
|
+
readonly onSelectionChange?: (selection: readonly EditorSelection[]) => void;
|
|
165
|
+
readonly onToolChange?: (tool: EditorTool) => void;
|
|
166
|
+
/** Fires whenever the last edit error changes; `undefined` once an edit succeeds. */
|
|
167
|
+
readonly onError?: (error: SchematicError | undefined) => void;
|
|
168
|
+
/** Fires on wheel/pan/fit/`zoomTo`/`setViewport` and when auto fit re-runs. */
|
|
169
|
+
readonly onViewportChange?: (viewport: ViewportState) => void;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* One editable property of the selected symbol, described by its device (#7).
|
|
174
|
+
*
|
|
175
|
+
* `type` selects the control a host renders; the remaining constraint fields (`options`, `min`,
|
|
176
|
+
* `max`, `step`, `unit`, `maxLength`, `alpha`, `maxBytes`, `maxPixels`) come from the declaration.
|
|
177
|
+
*/
|
|
178
|
+
export declare type PropertyField = Omit<PropertyDescriptor_2, 'default'> & {
|
|
179
|
+
/** Declared default; absent when the device declares none. */
|
|
180
|
+
readonly defaultValue?: PropertyValue;
|
|
181
|
+
/** Instance override; absent when the instance inherits the default, `null` when cleared. */
|
|
182
|
+
readonly value?: PropertyValue;
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
/** Merges overrides onto the dark baseline, so partial themes stay readable. */
|
|
186
|
+
export declare function resolveSchematicTheme(overrides?: Partial<SchematicTheme>): SchematicTheme;
|
|
187
|
+
|
|
188
|
+
/** Merges partial overrides onto the defaults; rejects non-positive and inverted ranges. */
|
|
189
|
+
export declare function resolveViewportLimits(limits?: Partial<ViewportLimits>): ViewportLimits;
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Editing state without any DOM dependency: selection, tool, undo history and the derived data
|
|
193
|
+
* the UI needs. It is a façade over {@link EditorSession} (state + mutation plumbing) and the
|
|
194
|
+
* action modules `symbols.ts` / `wires.ts`; every mutation flows through `applySchematicCommand`.
|
|
195
|
+
*/
|
|
196
|
+
export declare class SchematicEditorController {
|
|
197
|
+
#private;
|
|
198
|
+
constructor(options: SchematicEditorControllerOptions);
|
|
199
|
+
get state(): EditorState;
|
|
200
|
+
/** Everything the canvas draws from; never builds the inspector. */
|
|
201
|
+
get renderState(): EditorRenderState;
|
|
202
|
+
get document(): SchematicDocument;
|
|
203
|
+
get selection(): readonly EditorSelection[];
|
|
204
|
+
get tool(): EditorTool;
|
|
205
|
+
get display(): DisplayOptions;
|
|
206
|
+
get canUndo(): boolean;
|
|
207
|
+
get canRedo(): boolean;
|
|
208
|
+
/** Device armed for placement by the host's device list; `undefined` when idle. */
|
|
209
|
+
get placingDevice(): string | undefined;
|
|
210
|
+
setTool(tool: EditorTool): void;
|
|
211
|
+
setSelection(selection: readonly EditorSelection[]): void;
|
|
212
|
+
setDisplayOptions(patch: Partial<DisplayOptions>): void;
|
|
213
|
+
/** Swaps the edited document and clears history, selection and errors. */
|
|
214
|
+
reset(document: SchematicDocument): void;
|
|
215
|
+
/** @throws {SchematicError} leaving document, history and selection untouched. */
|
|
216
|
+
dispatch(command: SchematicCommand): void;
|
|
217
|
+
/**
|
|
218
|
+
* Swaps the whole document as one undoable edit, so a host can reload a file without losing
|
|
219
|
+
* the history it already has.
|
|
220
|
+
*/
|
|
221
|
+
replace(document: SchematicDocument): void;
|
|
222
|
+
/** Runs several edits as one history step; a failure rolls all of them back. */
|
|
223
|
+
transaction(run: () => void): void;
|
|
224
|
+
undo(): void;
|
|
225
|
+
redo(): void;
|
|
226
|
+
/** Arms placement of `device`: the next canvas click inserts it and disarms again. */
|
|
227
|
+
placeDevice(device: string): void;
|
|
228
|
+
cancelPlacement(): void;
|
|
229
|
+
/** Reference designator the next placed component gets, per the host's policy. */
|
|
230
|
+
nextDesignator(device: string): string;
|
|
231
|
+
moveSelectionBy(dx: number, dy: number): void;
|
|
232
|
+
rotateSelection(degrees: number): void;
|
|
233
|
+
deleteSelection(): void;
|
|
234
|
+
moveReferenceBy(symbolId: string, dx: number, dy: number): void;
|
|
235
|
+
setSymbolProperty(symbolId: string, key: string, value: PropertyValue): void;
|
|
236
|
+
removeSymbolProperty(symbolId: string, key: string): void;
|
|
237
|
+
renameDesignator(symbolId: string, nextDesignator: string): void;
|
|
238
|
+
renameNet(symbolId: string, nextNet: string): void;
|
|
239
|
+
addWire(points: readonly {
|
|
240
|
+
readonly x: number;
|
|
241
|
+
readonly y: number;
|
|
242
|
+
}[]): void;
|
|
243
|
+
connectWireAt(wireId: string, point: {
|
|
244
|
+
readonly x: number;
|
|
245
|
+
readonly y: number;
|
|
246
|
+
}, otherWireId?: string): string;
|
|
247
|
+
moveWireSegment(wireId: string, segment: number, delta: {
|
|
248
|
+
readonly x: number;
|
|
249
|
+
readonly y: number;
|
|
250
|
+
}): void;
|
|
251
|
+
previewWireSegment(wireId: string, segment: number, delta: {
|
|
252
|
+
readonly x: number;
|
|
253
|
+
readonly y: number;
|
|
254
|
+
}): readonly {
|
|
255
|
+
readonly x: number;
|
|
256
|
+
readonly y: number;
|
|
257
|
+
}[] | undefined;
|
|
258
|
+
inspect(): Inspector;
|
|
259
|
+
topology(): SchematicTopology;
|
|
260
|
+
hitTest(point: {
|
|
261
|
+
readonly x: number;
|
|
262
|
+
readonly y: number;
|
|
263
|
+
}, options?: HitTestOptions): SchematicHit | undefined;
|
|
264
|
+
subscribe(listener: (state: EditorState) => void): () => void;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export declare type SchematicEditorControllerOptions = EditorSessionOptions;
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Rendering and chrome tokens. The renderer never hard-codes colors: every visible
|
|
271
|
+
* value comes from a theme.
|
|
272
|
+
*/
|
|
273
|
+
export declare interface SchematicTheme {
|
|
274
|
+
background: ThemeColor;
|
|
275
|
+
grid: ThemeColor;
|
|
276
|
+
gridMajor: ThemeColor;
|
|
277
|
+
wire: ThemeColor;
|
|
278
|
+
wireSelected: ThemeColor;
|
|
279
|
+
junction: ThemeColor;
|
|
280
|
+
junctionSelected: ThemeColor;
|
|
281
|
+
pendingWire: ThemeColor;
|
|
282
|
+
symbolFill: ThemeColor;
|
|
283
|
+
symbolStroke: ThemeColor;
|
|
284
|
+
symbolText: ThemeColor;
|
|
285
|
+
symbolPlaceholder: ThemeColor;
|
|
286
|
+
pin: ThemeColor;
|
|
287
|
+
pinText: ThemeColor;
|
|
288
|
+
pinOpen: ThemeColor;
|
|
289
|
+
labelAnchor: ThemeColor;
|
|
290
|
+
labelText: ThemeColor;
|
|
291
|
+
selection: ThemeColor;
|
|
292
|
+
hover: ThemeColor;
|
|
293
|
+
uiBackground: ThemeColor;
|
|
294
|
+
uiSurface: ThemeColor;
|
|
295
|
+
uiBorder: ThemeColor;
|
|
296
|
+
uiText: ThemeColor;
|
|
297
|
+
uiMutedText: ThemeColor;
|
|
298
|
+
uiAccent: ThemeColor;
|
|
299
|
+
uiAccentText: ThemeColor;
|
|
300
|
+
focusRing: ThemeColor;
|
|
301
|
+
fontFamily: string;
|
|
302
|
+
fontSize: number;
|
|
303
|
+
/** Pin name text; smaller than `fontSize` so labels stay inside the body. */
|
|
304
|
+
pinFontSize: number;
|
|
305
|
+
/** Device name drawn in the symbol centre. */
|
|
306
|
+
symbolFontSize: number;
|
|
307
|
+
gridSize: number;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export declare interface SymbolInspector {
|
|
311
|
+
readonly kind: 'symbol';
|
|
312
|
+
readonly id: string;
|
|
313
|
+
/** What the symbol is for; drives which fields the panel shows. */
|
|
314
|
+
readonly role: SymbolRole;
|
|
315
|
+
readonly device: string;
|
|
316
|
+
readonly name: string | undefined;
|
|
317
|
+
/** True for the builtin symbols (`label` / `power` / `ground` / `text`). */
|
|
318
|
+
readonly builtin: boolean;
|
|
319
|
+
/** Reference designator; absent for symbols that carry none. */
|
|
320
|
+
readonly designator?: string;
|
|
321
|
+
/** Net name; absent for symbols that name no net. */
|
|
322
|
+
readonly net?: string;
|
|
323
|
+
readonly x: number;
|
|
324
|
+
readonly y: number;
|
|
325
|
+
readonly rotation: number;
|
|
326
|
+
readonly properties: readonly PropertyField[];
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/** Any CSS color string; Canvas and CSS parse it. */
|
|
330
|
+
export declare type ThemeColor = string;
|
|
331
|
+
|
|
332
|
+
/** Maps the theme onto `--mica-schematic-*` CSS variables for light-DOM chrome. */
|
|
333
|
+
export declare function themeToCssVariables(theme: SchematicTheme): Readonly<Record<string, string>>;
|
|
334
|
+
|
|
335
|
+
export declare interface ViewportLimits {
|
|
336
|
+
readonly minScale: number;
|
|
337
|
+
readonly maxScale: number;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export declare interface ViewportState {
|
|
341
|
+
readonly scale: number;
|
|
342
|
+
readonly offsetX: number;
|
|
343
|
+
readonly offsetY: number;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export declare interface WireInspector {
|
|
347
|
+
readonly kind: 'wire';
|
|
348
|
+
readonly id: string;
|
|
349
|
+
/** Net name derived from the topology; `null` for anonymous nets. */
|
|
350
|
+
readonly netName: string | null;
|
|
351
|
+
readonly points: readonly Point[];
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export { }
|