@osamash/floor-planner 1.2.4 → 1.2.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/floor-planner.css +1 -1
- package/dist/floor-planner.es.js +7866 -7257
- package/dist/floor-planner.umd.js +44 -40
- package/dist/types/react-planner/ReactPlannerLoader.d.ts +20 -1
- package/dist/types/react-planner/ai-chat/aiService.d.ts +147 -134
- package/dist/types/react-planner/ai-chat/plannerActions.d.ts +18 -1
- package/dist/types/react-planner/ai-chat/streamingClient.d.ts +0 -1
- package/dist/types/react-planner/catalog/catalog.d.ts +20 -0
- package/dist/types/react-planner/catalog/factories/ItemFactory.d.ts +2 -0
- package/dist/types/react-planner/catalog/unknown-element.d.ts +10 -0
- package/dist/types/react-planner/class/area.d.ts +5 -1
- package/dist/types/react-planner/class/surface.d.ts +34 -0
- package/dist/types/react-planner/class/vertex.d.ts +6 -1
- package/dist/types/react-planner/hooks/usePlanThumbnail.d.ts +13 -0
- package/dist/types/react-planner/models.d.ts +12 -0
- package/dist/types/react-planner/sidebar/panel-element-editor/editor-ui.d.ts +51 -0
- package/dist/types/react-planner/sidebar/panel-element-editor/paint-palette.d.ts +6 -0
- package/dist/types/react-planner/utils/index.d.ts +3 -1
- package/dist/types/react-planner/utils/line-family.d.ts +29 -0
- package/dist/types/react-planner/utils/screenshot.d.ts +22 -1
- package/dist/types/react-planner/viewer2d/Area.d.ts +7 -1
- package/dist/types/react-planner/viewer3d/viewer3d.d.ts +10 -0
- package/package.json +1 -1
- package/dist/types/react-planner/hooks/useScreenshotBeforeUnload.d.ts +0 -4
|
@@ -1,2 +1,21 @@
|
|
|
1
|
-
|
|
1
|
+
import "./react-planner.css";
|
|
2
|
+
interface ReactPlannerLoaderProps {
|
|
3
|
+
/** Defaults to the theme the planner itself is about to open in, so the
|
|
4
|
+
* hand-off from skeleton to real UI doesn't flash. */
|
|
5
|
+
theme?: "light" | "dark";
|
|
6
|
+
}
|
|
7
|
+
/** Skeleton of the planner's chrome, shown while the plan and catalog load.
|
|
8
|
+
*
|
|
9
|
+
* It reuses the real layout classes — `.floor_planner-headerbar`,
|
|
10
|
+
* `.fp-toolbar-rail`, `.fp-toolbar-subpanel`, `.fp-toolbar-utility`,
|
|
11
|
+
* `.fp-zoom-controls` — instead of restating their geometry, so the
|
|
12
|
+
* placeholder keeps lining up with the real UI when that chrome moves. Only
|
|
13
|
+
* the grey blocks and the few `.fp-loader__*` rules are its own.
|
|
14
|
+
*
|
|
15
|
+
* What it mirrors is the planner's *opening* state: header, the three-item
|
|
16
|
+
* tool rail, the Build sub-panel the toolbar starts on, zoom controls
|
|
17
|
+
* shifted clear of that panel, the utility bar and the AI bubble. No
|
|
18
|
+
* properties sidebar — that only appears once something is selected — and no
|
|
19
|
+
* footer, which renders nothing today. */
|
|
20
|
+
declare const ReactPlannerLoader: ({ theme }: ReactPlannerLoaderProps) => import("react/jsx-runtime").JSX.Element;
|
|
2
21
|
export default ReactPlannerLoader;
|
|
@@ -35,6 +35,12 @@ export interface WallInfo {
|
|
|
35
35
|
length: number;
|
|
36
36
|
orientation: "horizontal" | "vertical" | "diagonal";
|
|
37
37
|
holeCount: number;
|
|
38
|
+
/** cm. */
|
|
39
|
+
thickness: number | null;
|
|
40
|
+
height: number | null;
|
|
41
|
+
/** The wall's two faces. Which one looks into a given room is geometric. */
|
|
42
|
+
textureA: string | null;
|
|
43
|
+
textureB: string | null;
|
|
38
44
|
}
|
|
39
45
|
export interface HoleInfo {
|
|
40
46
|
id: string;
|
|
@@ -49,8 +55,8 @@ export interface HoleInfo {
|
|
|
49
55
|
export interface AreaInfo {
|
|
50
56
|
id: string;
|
|
51
57
|
/** "room" = enclosed by walls, "surface" = a free-standing polygon (grass,
|
|
52
|
-
* patio, driveway…).
|
|
53
|
-
*
|
|
58
|
+
* patio, driveway…). A room's corners are shared with the walls around it,
|
|
59
|
+
* so the two are not interchangeable targets for anything that reshapes. */
|
|
54
60
|
kind: "room" | "surface" | "area";
|
|
55
61
|
vertices: {
|
|
56
62
|
id: string;
|
|
@@ -58,6 +64,9 @@ export interface AreaInfo {
|
|
|
58
64
|
y: number;
|
|
59
65
|
}[];
|
|
60
66
|
color: string | null;
|
|
67
|
+
/** Floor fill/texture. Mutually exclusive; "none" means unset. */
|
|
68
|
+
pattern: string | null;
|
|
69
|
+
texture: string | null;
|
|
61
70
|
customName: string | null;
|
|
62
71
|
roomCategory: string | null;
|
|
63
72
|
roomType: string | null;
|
|
@@ -125,150 +134,145 @@ export interface PlanSummary {
|
|
|
125
134
|
roomTypes: RoomCategoryOption[];
|
|
126
135
|
selection: PlanSelection;
|
|
127
136
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
137
|
+
/**
|
|
138
|
+
* Which side of an existing room a new one goes on. Screen-relative, matching
|
|
139
|
+
* how a user says it; note the scene's +y is UP, so "below" is smaller y.
|
|
140
|
+
*/
|
|
141
|
+
export type RoomSide = "left" | "right" | "above" | "below";
|
|
142
|
+
/**
|
|
143
|
+
* An action the agent may emit for the planner to apply.
|
|
144
|
+
*
|
|
145
|
+
* **No action carries coordinates, and none ever should.** The agent says what
|
|
146
|
+
* to build and which existing room it relates to; every number describing a
|
|
147
|
+
* POSITION is computed by `applyPlannerActions`. Rooms live at 5-digit scene
|
|
148
|
+
* coordinates, and a room whose edge is a few centimetres off its neighbour
|
|
149
|
+
* draws as an unreachable void in 2D and a hole in the floor in 3D — so that
|
|
150
|
+
* arithmetic belongs where it can be exact.
|
|
151
|
+
*
|
|
152
|
+
* The set is being rebuilt one action at a time; the previous 23-variant
|
|
153
|
+
* union is on the `pre-rewrite-backup` branch. Each new variant needs three
|
|
154
|
+
* things moving together: this type (what may arrive), the backend's
|
|
155
|
+
* `ACTION_SPECS` (what the model may say), and a branch in
|
|
156
|
+
* `applyPlannerActions` (what actually happens).
|
|
157
|
+
*/
|
|
158
|
+
export type PlannerAction = AddRoomAction | SetRoomPropertiesAction | AddHoleAction | AddItemAction | SetWallPropertiesAction | SetItemPropertiesAction | AddSurfaceAction | DeleteAction | SetSurfacePropertiesAction;
|
|
159
|
+
export type AddRoomAction = {
|
|
136
160
|
type: "add-room";
|
|
137
|
-
|
|
138
|
-
|
|
161
|
+
/**
|
|
162
|
+
* Shown on the plan, and how a later `of` refers to this room — so it has to
|
|
163
|
+
* be unique within the scene ("Bedroom 1", "Bedroom 2").
|
|
164
|
+
*/
|
|
165
|
+
name: string;
|
|
166
|
+
/** cm. Defaults are applied backend-side, so these arrive populated. */
|
|
139
167
|
width: number;
|
|
140
168
|
height: number;
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
id?: string;
|
|
150
|
-
} | {
|
|
151
|
-
type: "add-item";
|
|
152
|
-
itemType: string;
|
|
153
|
-
x: number;
|
|
154
|
-
y: number;
|
|
155
|
-
width?: number;
|
|
156
|
-
height?: number;
|
|
157
|
-
rotation?: number;
|
|
158
|
-
id?: string;
|
|
159
|
-
} | {
|
|
160
|
-
type: "add-hole";
|
|
161
|
-
/** Real line ID, or a "$tag" / "$tag.side" reference — see note above. */
|
|
162
|
-
lineID: string;
|
|
163
|
-
holeType: string;
|
|
164
|
-
offset: number;
|
|
165
|
-
id?: string;
|
|
166
|
-
} | {
|
|
167
|
-
type: "set-area-color";
|
|
168
|
-
areaID: string;
|
|
169
|
-
color: string;
|
|
170
|
-
} | {
|
|
171
|
-
type: "set-area-properties";
|
|
172
|
-
areaID: string;
|
|
173
|
-
customName?: string;
|
|
169
|
+
/**
|
|
170
|
+
* The NAME of the room this one sits against — either already in the scene
|
|
171
|
+
* or created earlier in this same batch. Omitted together with `side` for
|
|
172
|
+
* the first room of a plan, which is centered in the scene instead.
|
|
173
|
+
*/
|
|
174
|
+
of?: string;
|
|
175
|
+
side?: RoomSide;
|
|
176
|
+
/** Catalog room type; also sets the floor color to that type's own color. */
|
|
174
177
|
roomCategory?: string;
|
|
175
178
|
roomType?: string;
|
|
176
|
-
|
|
177
|
-
|
|
179
|
+
/** Explicit floor color. Wins over the room type's default when both are set. */
|
|
180
|
+
color?: string;
|
|
181
|
+
};
|
|
182
|
+
export type SetRoomPropertiesAction = {
|
|
183
|
+
type: "set-room-properties";
|
|
184
|
+
/** A room name, or the literal "selected" for the room highlighted in the canvas. */
|
|
185
|
+
room: string;
|
|
186
|
+
/** Renames the room. */
|
|
187
|
+
name?: string;
|
|
188
|
+
roomCategory?: string;
|
|
189
|
+
roomType?: string;
|
|
190
|
+
color?: string;
|
|
178
191
|
pattern?: string;
|
|
179
|
-
/** One of PlanSummary.catalogAreaTextures keys (or "none"). */
|
|
180
192
|
texture?: string;
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
193
|
+
};
|
|
194
|
+
export type AddHoleAction = {
|
|
195
|
+
type: "add-hole";
|
|
196
|
+
/** Verbatim from PlanSummary.catalogHoleTypes. */
|
|
197
|
+
holeType: string;
|
|
198
|
+
/**
|
|
199
|
+
* The two rooms this opening connects; the shared wall is found here. One
|
|
200
|
+
* opening has one representation, so it cannot be described from both sides
|
|
201
|
+
* and land as two doors. Mutually exclusive with room/side.
|
|
202
|
+
*/
|
|
203
|
+
between?: [string, string];
|
|
204
|
+
/** A particular wall — the only way to reach an exterior one. */
|
|
205
|
+
room?: string;
|
|
206
|
+
side?: RoomSide;
|
|
207
|
+
};
|
|
208
|
+
export type AddItemAction = {
|
|
209
|
+
type: "add-item";
|
|
210
|
+
/** Verbatim from PlanSummary.catalogItemTypes. */
|
|
211
|
+
itemType: string;
|
|
212
|
+
/** A room name, or "selected". */
|
|
213
|
+
room: string;
|
|
214
|
+
/** 1-8. col 1 is the left eighth of the room, row 1 the top. */
|
|
215
|
+
col: number;
|
|
216
|
+
row: number;
|
|
217
|
+
/** 0, 90, 180 or 270. */
|
|
218
|
+
rotation?: number;
|
|
219
|
+
};
|
|
220
|
+
export type SetWallPropertiesAction = {
|
|
184
221
|
type: "set-wall-properties";
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
/** cm */
|
|
222
|
+
/** A room name, or "selected". All four of its walls unless `side` is given. */
|
|
223
|
+
room: string;
|
|
224
|
+
side?: RoomSide;
|
|
225
|
+
/** Applied to the wall face looking into `room`. */
|
|
226
|
+
color?: string;
|
|
227
|
+
texture?: string;
|
|
228
|
+
/** cm. Whole-wall, so a shared wall changes for both rooms. */
|
|
192
229
|
thickness?: number;
|
|
193
|
-
/** cm */
|
|
194
230
|
height?: number;
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
} | {
|
|
198
|
-
/** Edit an existing item in place — the same set of changes the
|
|
199
|
-
* selection toolbar offers in the 2D view. Omitted fields are left
|
|
200
|
-
* untouched. */
|
|
231
|
+
};
|
|
232
|
+
export type SetItemPropertiesAction = {
|
|
201
233
|
type: "set-item-properties";
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
234
|
+
/** A room name, or "selected". */
|
|
235
|
+
room: string;
|
|
236
|
+
/** The furniture's catalog name, e.g. "macheroni sofa". */
|
|
237
|
+
item: string;
|
|
238
|
+
/** Move it to this cell of the room's 8x8 grid. Both or neither. */
|
|
239
|
+
col?: number;
|
|
240
|
+
row?: number;
|
|
241
|
+
/** The new angle, not a turn to add. */
|
|
206
242
|
rotation?: number;
|
|
207
|
-
/**
|
|
243
|
+
/** Multiplier on the catalog size; the only sizing an item has. */
|
|
208
244
|
scale?: number;
|
|
209
245
|
flipHorizontal?: boolean;
|
|
210
246
|
flipVertical?: boolean;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
type: "
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
type: "
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
itemID: string;
|
|
241
|
-
} | {
|
|
242
|
-
/** Copy a door / window along the same wall. */
|
|
243
|
-
type: "duplicate-hole";
|
|
244
|
-
holeID: string;
|
|
245
|
-
} | {
|
|
246
|
-
type: "move-vertex";
|
|
247
|
-
vertexID: string;
|
|
248
|
-
x: number;
|
|
249
|
-
y: number;
|
|
250
|
-
} | {
|
|
251
|
-
type: "delete-wall";
|
|
252
|
-
wallID: string;
|
|
253
|
-
} | {
|
|
254
|
-
type: "delete-item";
|
|
255
|
-
itemID: string;
|
|
256
|
-
} | {
|
|
257
|
-
type: "delete-hole";
|
|
258
|
-
holeID: string;
|
|
259
|
-
} | {
|
|
260
|
-
type: "remove-room";
|
|
261
|
-
areaID: string;
|
|
262
|
-
} | {
|
|
263
|
-
type: "clear";
|
|
264
|
-
} | {
|
|
265
|
-
type: "switch-view";
|
|
266
|
-
view: "2d" | "3d";
|
|
267
|
-
} | {
|
|
268
|
-
type: "toggle-dimensions";
|
|
269
|
-
show: boolean;
|
|
270
|
-
} | {
|
|
271
|
-
type: "undo";
|
|
247
|
+
};
|
|
248
|
+
export type AddSurfaceAction = {
|
|
249
|
+
type: "add-surface";
|
|
250
|
+
/** The room whose wall it lies against, and which side. Must face outdoors. */
|
|
251
|
+
of: string;
|
|
252
|
+
side: RoomSide;
|
|
253
|
+
name?: string;
|
|
254
|
+
/** cm away from the wall; it runs that wall's full width. */
|
|
255
|
+
depth?: number;
|
|
256
|
+
texture?: string;
|
|
257
|
+
color?: string;
|
|
258
|
+
};
|
|
259
|
+
export type DeleteAction = {
|
|
260
|
+
type: "delete";
|
|
261
|
+
/** A room name, a surface name, or "selected". */
|
|
262
|
+
room: string;
|
|
263
|
+
/** Present to delete one piece of furniture inside it instead of the room. */
|
|
264
|
+
item?: string;
|
|
265
|
+
};
|
|
266
|
+
export type SetSurfacePropertiesAction = {
|
|
267
|
+
type: "set-surface-properties";
|
|
268
|
+
/** Its name from the Outdoor surfaces list. */
|
|
269
|
+
surface: string;
|
|
270
|
+
name?: string;
|
|
271
|
+
/** cm out from the wall it lies against; same wall, same width. */
|
|
272
|
+
depth?: number;
|
|
273
|
+
texture?: string;
|
|
274
|
+
pattern?: string;
|
|
275
|
+
color?: string;
|
|
272
276
|
};
|
|
273
277
|
export interface AIResponse {
|
|
274
278
|
reply: string;
|
|
@@ -277,9 +281,9 @@ export interface AIResponse {
|
|
|
277
281
|
* Multi-step continuation. Building something big (a whole multi-room
|
|
278
282
|
* floor plan) can need more actions than fit in one well-grounded
|
|
279
283
|
* response — e.g. you want to see a room's real wall IDs (from a fresh
|
|
280
|
-
* `PlanSummary`) before placing doors on it
|
|
281
|
-
*
|
|
282
|
-
*
|
|
284
|
+
* `PlanSummary`) before placing doors on it. Real IDs are random and only
|
|
285
|
+
* exist once the library has created the element, so anything that needs one
|
|
286
|
+
* has to wait for the next call.
|
|
283
287
|
*
|
|
284
288
|
* Set `done: false` to have the library apply this response's `actions`,
|
|
285
289
|
* take a fresh `summarizePlan()` of the result, and call your handler
|
|
@@ -297,6 +301,15 @@ export interface AIResponse {
|
|
|
297
301
|
* more than that for one user request.
|
|
298
302
|
*/
|
|
299
303
|
done?: boolean;
|
|
304
|
+
/**
|
|
305
|
+
* Actions the backend rejected, with the reason. Fed back to the agent on the
|
|
306
|
+
* next continuation so it can correct itself instead of repeating the
|
|
307
|
+
* mistake — see the "Continue." turn in AIChatPanel.
|
|
308
|
+
*/
|
|
309
|
+
dropped?: {
|
|
310
|
+
reason: string;
|
|
311
|
+
action: unknown;
|
|
312
|
+
}[];
|
|
300
313
|
}
|
|
301
314
|
/**
|
|
302
315
|
* Extras the library hands a handler on every call. Optional on purpose: a
|
|
@@ -1,3 +1,20 @@
|
|
|
1
1
|
import type { PlanSummary, PlannerAction } from "./aiService";
|
|
2
2
|
export declare function summarizePlan(planner: any): PlanSummary;
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Applies a batch of actions from the agent to the planner.
|
|
5
|
+
*
|
|
6
|
+
* The agent never sends coordinates. It says how big a room is, what to call
|
|
7
|
+
* it, and which existing room it sits next to — every number describing a
|
|
8
|
+
* POSITION is worked out here, where it can be exact. A room with no `of` /
|
|
9
|
+
* `side` is centered in the scene, which is only ever right for the first room.
|
|
10
|
+
*
|
|
11
|
+
* Rooms created earlier in this same batch are valid anchors: each one is
|
|
12
|
+
* looked up by name from live state after it is created, so a five-room plan
|
|
13
|
+
* arrives as five actions that chain off each other.
|
|
14
|
+
*
|
|
15
|
+
* @returns `skipped` — human-readable reasons anything was not applied, shown
|
|
16
|
+
* to the user by the caller.
|
|
17
|
+
*/
|
|
18
|
+
export declare function applyPlannerActions(actions: PlannerAction[]): {
|
|
19
|
+
skipped: string[];
|
|
20
|
+
};
|
|
@@ -7,7 +7,6 @@ import type { AIRequestHandler } from "./aiService";
|
|
|
7
7
|
*
|
|
8
8
|
* Wire format (Server-Sent Events, POST — so NOT `EventSource`-compatible):
|
|
9
9
|
*
|
|
10
|
-
* event: meta data: { provider, model } once, before any output
|
|
11
10
|
* event: reply data: { delta } zero or more, in order
|
|
12
11
|
* event: final data: { reply, actions, done, … } authoritative, terminal
|
|
13
12
|
* event: error data: { error, type } terminal
|
|
@@ -47,8 +47,28 @@ export default class Catalog {
|
|
|
47
47
|
private elements;
|
|
48
48
|
categories: Record<string, CatalogCategory>;
|
|
49
49
|
private propertyTypes;
|
|
50
|
+
/**
|
|
51
|
+
* Placeholders handed out for types the plan references and this catalog
|
|
52
|
+
* doesn't have. Cached so a type resolves to the same object every render,
|
|
53
|
+
* and so the warning about it is printed once.
|
|
54
|
+
*/
|
|
55
|
+
private missingElements;
|
|
50
56
|
unit: Unit;
|
|
51
57
|
constructor(unit?: Unit);
|
|
58
|
+
/**
|
|
59
|
+
* The element `type` names, or a placeholder if the catalog has no such
|
|
60
|
+
* element.
|
|
61
|
+
*
|
|
62
|
+
* Deliberately total. Every renderer and editor reaches an element through
|
|
63
|
+
* here, and a plan can legitimately outlive the catalog it was drawn with —
|
|
64
|
+
* an item renamed, dropped, or opened by an app carrying a different subset.
|
|
65
|
+
* Throwing meant one stale `type` in a saved plan took down the render of
|
|
66
|
+
* everything around it; the placeholder keeps the rest of the plan on screen
|
|
67
|
+
* and keeps the offending element selectable, movable and — because nothing
|
|
68
|
+
* rewrites its `type` — able to come back intact against the right catalog.
|
|
69
|
+
* Callers that need to know whether something is really registered ask
|
|
70
|
+
* `hasElement`.
|
|
71
|
+
*/
|
|
52
72
|
getElement(type: string): CatalogElement;
|
|
53
73
|
/** Every registered element of a prototype ("items", "holes", "lines"),
|
|
54
74
|
* regardless of the category it was filed under. Backs the sidebar's
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CatalogElement } from "./catalog";
|
|
2
|
+
/**
|
|
3
|
+
* Build the stand-in for `type`.
|
|
4
|
+
*
|
|
5
|
+
* `prototype` is left empty on purpose: the element's own record already
|
|
6
|
+
* carries the right one, and claiming one here would file the placeholder among
|
|
7
|
+
* real elements — `getElementsOfPrototype` backs the "similar elements" list,
|
|
8
|
+
* which should never offer a missing type as something to place.
|
|
9
|
+
*/
|
|
10
|
+
export default function unknownElement(type: string): CatalogElement;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Set } from "immutable";
|
|
2
|
+
import { type LineFamily } from "../utils/line-family";
|
|
2
3
|
declare class Area {
|
|
3
4
|
static getSurfaceLines(state: any, layerID: string, areaID: string): Set<string>;
|
|
4
5
|
static center(state: any, layerID: string, areaID: string): {
|
|
@@ -24,7 +25,10 @@ declare class Area {
|
|
|
24
25
|
static mirror(state: any, layerID: string, areaID: string, axis: "x" | "y"): {
|
|
25
26
|
updatedState: any;
|
|
26
27
|
};
|
|
27
|
-
|
|
28
|
+
/** `family` says which graph the boundary belongs to. Walls and surfaces can
|
|
29
|
+
* now have distinct vertices at identical coordinates, so without it the
|
|
30
|
+
* lookup below could bind a surface polygon to a wall's corner. */
|
|
31
|
+
static add(state: any, layerID: any, type: any, verticesCoords: any, catalog: any, family?: LineFamily): {
|
|
28
32
|
updatedState: any;
|
|
29
33
|
area: undefined;
|
|
30
34
|
};
|
|
@@ -1,7 +1,27 @@
|
|
|
1
|
+
/** Free-standing polygons — patios, lawns, decking. Drawn corner by corner
|
|
2
|
+
* with the same lines/vertices as walls, but kept out of the wall graph by
|
|
3
|
+
* `utils/line-family`, so laying one over a room neither welds it to the
|
|
4
|
+
* walls it crosses nor takes the room's floor over.
|
|
5
|
+
*
|
|
6
|
+
* Snapping is the deliberate exception: a surface *should* be able to land
|
|
7
|
+
* exactly on a wall corner or run flush along a wall — it just must not
|
|
8
|
+
* become attached to it. So the whole scene is offered as snap targets, the
|
|
9
|
+
* same set the wall tool uses.
|
|
10
|
+
*
|
|
11
|
+
* Drawing: click each corner. Click back on the first corner to close the
|
|
12
|
+
* polygon, or leave the tool (Esc, double-click) once three corners are down
|
|
13
|
+
* and `finishDrawingSurface` closes it for you.
|
|
14
|
+
*/
|
|
1
15
|
declare class Surface {
|
|
2
16
|
static selectToolDrawingSurface(state: any, sceneComponentType: string): {
|
|
3
17
|
updatedState: any;
|
|
4
18
|
};
|
|
19
|
+
/** Snap targets for the whole scene, plus the horizontal/vertical guides
|
|
20
|
+
* through `(x, y)` — exactly what `Line.beginDrawingLine` builds. This was
|
|
21
|
+
* simply missing before: `snapElements` stayed empty for the surface tool,
|
|
22
|
+
* so every `nearestSnap` downstream had nothing to find and the tool
|
|
23
|
+
* behaved as if snapping were off. */
|
|
24
|
+
private static anchorSnaps;
|
|
5
25
|
static beginDrawingSurface(state: any, layerID: string, x: number, y: number): {
|
|
6
26
|
updatedState: any;
|
|
7
27
|
};
|
|
@@ -11,5 +31,19 @@ declare class Surface {
|
|
|
11
31
|
static endDrawingSurface(state: any, x: number, y: number): {
|
|
12
32
|
updatedState: any;
|
|
13
33
|
};
|
|
34
|
+
/** Leave the tool keeping what has been drawn, by committing the edge from
|
|
35
|
+
* the last clicked corner back to the first.
|
|
36
|
+
*
|
|
37
|
+
* This is what Esc and the double-click go through. The outline on screen
|
|
38
|
+
* is already drawn closed while the tool is live, so throwing it away is
|
|
39
|
+
* never what leaving the tool means — but only clicked corners count, not
|
|
40
|
+
* wherever the cursor happens to be resting.
|
|
41
|
+
*
|
|
42
|
+
* Under three corners there is no polygon to keep; `finished: false` says
|
|
43
|
+
* so, and the caller falls back to a real rollback. */
|
|
44
|
+
static finishDrawingSurface(state: any): {
|
|
45
|
+
updatedState: any;
|
|
46
|
+
finished: boolean;
|
|
47
|
+
};
|
|
14
48
|
}
|
|
15
49
|
export default Surface;
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
+
import { type LineFamily } from "../utils/line-family";
|
|
1
2
|
declare class Vertex {
|
|
2
|
-
|
|
3
|
+
/** `family` is what the caller is about to attach here. A coincident vertex
|
|
4
|
+
* is only reused when it belongs to the same family, so a surface corner
|
|
5
|
+
* dropped on a wall corner gets its own vertex and the two graphs stay
|
|
6
|
+
* separate. Omit it to reuse any coincident vertex (the old behaviour). */
|
|
7
|
+
static add(state: any, layerID: any, x: any, y: any, relatedPrototype: any, relatedID: any, family?: LineFamily): {
|
|
3
8
|
updatedState: any;
|
|
4
9
|
vertex: any;
|
|
5
10
|
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keeps a plan thumbnail up to date, handing each new one to `onReady`.
|
|
3
|
+
*
|
|
4
|
+
* Capture is deliberately *ahead* of unload, not during it. `beforeunload`
|
|
5
|
+
* can't await anything, and a multi-hundred-KB data URI is far past what
|
|
6
|
+
* sendBeacon or fetch({ keepalive: true }) will carry (~64KB), so there is no
|
|
7
|
+
* way to both build and ship an image once the page is going away. Instead we
|
|
8
|
+
* capture on an idle timer and again when the page is hidden — by the time the
|
|
9
|
+
* tab actually closes, the server already has a current image.
|
|
10
|
+
*/
|
|
11
|
+
export declare function usePlanThumbnail(onReady?: (img: string) => void): {
|
|
12
|
+
ensureScreenshot: (force?: boolean) => Promise<void>;
|
|
13
|
+
};
|
|
@@ -173,6 +173,18 @@ declare const Catalog_base: Record.Factory<{
|
|
|
173
173
|
}>;
|
|
174
174
|
export declare class Catalog extends Catalog_base {
|
|
175
175
|
constructor(json?: any);
|
|
176
|
+
/**
|
|
177
|
+
* Build a record of `type` with its catalog defaults filled in.
|
|
178
|
+
*
|
|
179
|
+
* A type the catalog doesn't have is normally a caller's mistake, but not
|
|
180
|
+
* always: duplicating something already in the plan goes through here, and a
|
|
181
|
+
* plan can outlive the catalog it was drawn with (see
|
|
182
|
+
* `catalog/unknown-element.tsx`). So when `options.prototype` says which kind
|
|
183
|
+
* of record to build, one gets built — carrying whatever properties came in,
|
|
184
|
+
* since there are no defaults to merge — rather than taking the planner down
|
|
185
|
+
* over a copy of something already on screen. Without that hint there is
|
|
186
|
+
* nothing to construct, and it still throws.
|
|
187
|
+
*/
|
|
176
188
|
factoryElement(type: string, options: any, initialProperties?: Map<string, any>): Line | Hole | Area | Item;
|
|
177
189
|
}
|
|
178
190
|
declare const HistoryStructure_base: Record.Factory<{
|
|
@@ -34,6 +34,57 @@ export interface EditorAction {
|
|
|
34
34
|
export declare const ActionRow: ({ actions }: {
|
|
35
35
|
actions: EditorAction[];
|
|
36
36
|
}) => import("react/jsx-runtime").JSX.Element;
|
|
37
|
+
/**
|
|
38
|
+
* One entry of a catalog element's `textures` map, as far as the picker cares.
|
|
39
|
+
* Structural on purpose — the repeat scales matter to the renderers, not here.
|
|
40
|
+
*/
|
|
41
|
+
export interface MaterialTexture {
|
|
42
|
+
name: string;
|
|
43
|
+
uri: string;
|
|
44
|
+
tag?: string[];
|
|
45
|
+
}
|
|
46
|
+
interface MaterialPickerProps {
|
|
47
|
+
textures: Record<string, MaterialTexture>;
|
|
48
|
+
/** Currently applied key, or `"none"`. */
|
|
49
|
+
value: string;
|
|
50
|
+
onChange: (key: string) => void;
|
|
51
|
+
/** Heading, rendered only alongside the picker so it can't be left stranded
|
|
52
|
+
* above nothing when the element has no materials. */
|
|
53
|
+
label?: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Browse a catalog element's materials.
|
|
57
|
+
*
|
|
58
|
+
* Shared by the wall and area editors because they browse the same library —
|
|
59
|
+
* the catalog hands floors and walls one `textures` map, so whatever makes it
|
|
60
|
+
* navigable for one has to be there for the other. Categories come from the
|
|
61
|
+
* materials' own tags in the order the catalog lists them, so no vocabulary is
|
|
62
|
+
* baked in here.
|
|
63
|
+
*/
|
|
64
|
+
export declare const MaterialPicker: ({ textures, value, onChange, label, }: MaterialPickerProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
65
|
+
interface FinishPickerProps {
|
|
66
|
+
/** Paint colour, as `#RRGGBB`. */
|
|
67
|
+
color: string;
|
|
68
|
+
onColorChange: (color: string) => void;
|
|
69
|
+
textures: Record<string, MaterialTexture>;
|
|
70
|
+
/** Currently applied texture key, or `"none"`. */
|
|
71
|
+
texture: string;
|
|
72
|
+
onTextureChange: (key: string) => void;
|
|
73
|
+
/**
|
|
74
|
+
* Rendered under the palette on the Colors tab. Areas put their hatch
|
|
75
|
+
* patterns here; walls have none, and pass nothing.
|
|
76
|
+
*/
|
|
77
|
+
patterns?: React.ReactNode;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* How a surface's finish is chosen: paint on one tab, materials on the other.
|
|
81
|
+
*
|
|
82
|
+
* Walls and areas both answer the same question here — what is this surface
|
|
83
|
+
* covered in — so they get the same two tabs over the same palette and the same
|
|
84
|
+
* material library. What differs is what else lives under the palette, which
|
|
85
|
+
* comes in as `patterns`.
|
|
86
|
+
*/
|
|
87
|
+
export declare const FinishPicker: ({ color, onColorChange, textures, texture, onTextureChange, patterns, }: FinishPickerProps) => import("react/jsx-runtime").JSX.Element;
|
|
37
88
|
/** Catalog thumbnail, falling back to a neutral tile when there is no image. */
|
|
38
89
|
export declare const ElementPreview: ({ imageUri, alt, }: {
|
|
39
90
|
imageUri?: string;
|
|
@@ -5,9 +5,10 @@ import * as SnapUtils from "./snap";
|
|
|
5
5
|
import * as SnapSceneUtils from "./snap-scene";
|
|
6
6
|
import * as history from "./history";
|
|
7
7
|
import * as ObjectUtils from "./objects-utils";
|
|
8
|
+
import * as LineFamilyUtils from "./line-family";
|
|
8
9
|
import IDBroker from "./id-broker";
|
|
9
10
|
import NameGenerator from "./name-generator";
|
|
10
|
-
export { GeometryUtils, GraphInnerCycles, MathUtils, SnapUtils, SnapSceneUtils, history, IDBroker, NameGenerator, ObjectUtils, };
|
|
11
|
+
export { GeometryUtils, GraphInnerCycles, MathUtils, SnapUtils, SnapSceneUtils, history, IDBroker, NameGenerator, ObjectUtils, LineFamilyUtils, };
|
|
11
12
|
declare const _default: {
|
|
12
13
|
GeometryUtils: typeof GeometryUtils;
|
|
13
14
|
GraphInnerCycles: typeof GraphInnerCycles;
|
|
@@ -18,5 +19,6 @@ declare const _default: {
|
|
|
18
19
|
IDBroker: typeof IDBroker;
|
|
19
20
|
NameGenerator: typeof NameGenerator;
|
|
20
21
|
ObjectUtils: typeof ObjectUtils;
|
|
22
|
+
LineFamilyUtils: typeof LineFamilyUtils;
|
|
21
23
|
};
|
|
22
24
|
export default _default;
|