@osamash/floor-planner 1.2.3 → 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.
Files changed (86) hide show
  1. package/dist/floor-planner.css +1 -1
  2. package/dist/floor-planner.es.js +44841 -38060
  3. package/dist/floor-planner.umd.js +65 -51
  4. package/dist/types/index.d.ts +3 -1
  5. package/dist/types/react-planner/ReactPlanner.d.ts +3 -1
  6. package/dist/types/react-planner/ReactPlannerLoader.d.ts +20 -1
  7. package/dist/types/react-planner/ai-chat/AIChatBubble.d.ts +6 -0
  8. package/dist/types/react-planner/ai-chat/AIChatPanel.d.ts +8 -0
  9. package/dist/types/react-planner/ai-chat/SelectionChips.d.ts +2 -0
  10. package/dist/types/react-planner/ai-chat/ThinkingIndicator.d.ts +5 -0
  11. package/dist/types/react-planner/ai-chat/aiService.d.ts +340 -0
  12. package/dist/types/react-planner/ai-chat/imageUpload.d.ts +6 -0
  13. package/dist/types/react-planner/ai-chat/plannerActions.d.ts +20 -0
  14. package/dist/types/react-planner/ai-chat/streamingClient.d.ts +31 -0
  15. package/dist/types/react-planner/ai-chat/useTypewriter.d.ts +19 -0
  16. package/dist/types/react-planner/catalog/catalog.d.ts +24 -0
  17. package/dist/types/react-planner/catalog/factories/AreaFactory.d.ts +1 -24
  18. package/dist/types/react-planner/catalog/factories/ItemFactory.d.ts +2 -0
  19. package/dist/types/react-planner/catalog/factories/WallFactory.d.ts +1 -26
  20. package/dist/types/react-planner/catalog/factories/area-factory-3d.d.ts +2 -2
  21. package/dist/types/react-planner/catalog/factories/area-patterns.d.ts +42 -0
  22. package/dist/types/react-planner/catalog/factories/room-types.d.ts +22 -0
  23. package/dist/types/react-planner/catalog/factories/wall-factory-3d.d.ts +1 -1
  24. package/dist/types/react-planner/catalog/unknown-element.d.ts +10 -0
  25. package/dist/types/react-planner/class/area.d.ts +28 -1
  26. package/dist/types/react-planner/class/hole.d.ts +13 -0
  27. package/dist/types/react-planner/class/index.d.ts +3 -1
  28. package/dist/types/react-planner/class/item.d.ts +22 -0
  29. package/dist/types/react-planner/class/line.d.ts +12 -3
  30. package/dist/types/react-planner/class/room.d.ts +18 -0
  31. package/dist/types/react-planner/class/surface.d.ts +34 -0
  32. package/dist/types/react-planner/class/vertex.d.ts +6 -1
  33. package/dist/types/react-planner/components/ErrorBoundary.d.ts +19 -0
  34. package/dist/types/react-planner/components/SearchInput.d.ts +2 -1
  35. package/dist/types/react-planner/constants.d.ts +3 -0
  36. package/dist/types/react-planner/footer-bar/Footerbar.d.ts +1 -1
  37. package/dist/types/react-planner/hooks/usePlanThumbnail.d.ts +13 -0
  38. package/dist/types/react-planner/models.d.ts +19 -0
  39. package/dist/types/react-planner/plugins/autosave.d.ts +1 -1
  40. package/dist/types/react-planner/plugins/keyboard.d.ts +1 -1
  41. package/dist/types/react-planner/sidebar/Panel.d.ts +2 -1
  42. package/dist/types/react-planner/sidebar/panel-element-editor/AreaAttributesEditor.d.ts +8 -0
  43. package/dist/types/react-planner/sidebar/panel-element-editor/AttributesEditor.d.ts +4 -1
  44. package/dist/types/react-planner/sidebar/panel-element-editor/HoleAttributesEditor.d.ts +3 -4
  45. package/dist/types/react-planner/sidebar/panel-element-editor/ItemAttributesEditor.d.ts +3 -4
  46. package/dist/types/react-planner/sidebar/panel-element-editor/SimilarElements.d.ts +15 -0
  47. package/dist/types/react-planner/sidebar/panel-element-editor/WallAttributesEditor.d.ts +8 -0
  48. package/dist/types/react-planner/sidebar/panel-element-editor/editor-ui.d.ts +93 -0
  49. package/dist/types/react-planner/sidebar/panel-element-editor/paint-palette.d.ts +6 -0
  50. package/dist/types/react-planner/toolbar/CatalogPicker.d.ts +8 -0
  51. package/dist/types/react-planner/toolbar/NumberStepper.d.ts +11 -0
  52. package/dist/types/react-planner/toolbar/ProjectMiniMap.d.ts +11 -0
  53. package/dist/types/react-planner/toolbar/ProjectPanel.d.ts +2 -0
  54. package/dist/types/react-planner/toolbar/ToolbarRail.d.ts +15 -0
  55. package/dist/types/react-planner/toolbar/ToolbarSubPanel.d.ts +23 -0
  56. package/dist/types/react-planner/toolbar/ZoomControls.d.ts +5 -0
  57. package/dist/types/react-planner/utils/angle.d.ts +9 -0
  58. package/dist/types/react-planner/utils/geometry.d.ts +15 -0
  59. package/dist/types/react-planner/utils/image-trim.d.ts +25 -0
  60. package/dist/types/react-planner/utils/index.d.ts +3 -1
  61. package/dist/types/react-planner/utils/item-footprint.d.ts +23 -0
  62. package/dist/types/react-planner/utils/item-snap.d.ts +56 -0
  63. package/dist/types/react-planner/utils/item-transform.d.ts +9 -0
  64. package/dist/types/react-planner/utils/line-family.d.ts +29 -0
  65. package/dist/types/react-planner/utils/name-generator.d.ts +1 -1
  66. package/dist/types/react-planner/utils/screenshot.d.ts +22 -1
  67. package/dist/types/react-planner/utils/three-cache.d.ts +17 -0
  68. package/dist/types/react-planner/utils/three-materials.d.ts +33 -0
  69. package/dist/types/react-planner/utils/threeCSG.es6.d.ts +1 -1
  70. package/dist/types/react-planner/utils/wall-miter.d.ts +53 -0
  71. package/dist/types/react-planner/utils/zoom.d.ts +2 -2
  72. package/dist/types/react-planner/viewer2d/Area.d.ts +7 -1
  73. package/dist/types/react-planner/viewer2d/Grids.d.ts +1 -1
  74. package/dist/types/react-planner/viewer2d/Ruler.d.ts +6 -1
  75. package/dist/types/react-planner/viewer2d/SelectionToolbar.d.ts +12 -0
  76. package/dist/types/react-planner/viewer2d/WallHoverHandles.d.ts +13 -0
  77. package/dist/types/react-planner/viewer3d/selection-gizmo.d.ts +57 -0
  78. package/dist/types/react-planner/viewer3d/viewer3d.d.ts +71 -0
  79. package/dist/types/shared-style.d.ts +6 -0
  80. package/dist/types/store/ReactPlannerStore.d.ts +102 -1
  81. package/package.json +1 -1
  82. package/dist/types/react-planner/components/PropertyString.d.ts +0 -10
  83. package/dist/types/react-planner/hooks/useScreenshotBeforeUnload.d.ts +0 -4
  84. package/dist/types/react-planner/toolbar/Popup.d.ts +0 -11
  85. package/dist/types/react-planner/viewer2d/GridHorizontalStreak.d.ts +0 -7
  86. package/dist/types/react-planner/viewer2d/GridVerticalStreak.d.ts +0 -7
@@ -2,5 +2,7 @@ import ReactPlanner from "./react-planner/ReactPlanner";
2
2
  import ReactPlannerLoader from "./react-planner/ReactPlannerLoader";
3
3
  import convertUnits, { type Unit } from "./react-planner/utils/convert-units";
4
4
  import Catalog, { AreaFactory, ItemFactory, WallFactory, SurfaceFactory } from "./react-planner/catalog";
5
- export { Catalog, AreaFactory, ItemFactory, WallFactory, SurfaceFactory, convertUnits, type Unit, ReactPlannerLoader, };
5
+ import type { AIRequestHandler, AIResponse, AIStreamContext, ChatMessage, PlanSummary, PlannerAction } from "./react-planner/ai-chat/aiService";
6
+ import { createStreamingAIHandler, type StreamingAIHandlerOptions } from "./react-planner/ai-chat/streamingClient";
7
+ export { Catalog, AreaFactory, ItemFactory, WallFactory, SurfaceFactory, convertUnits, type Unit, ReactPlannerLoader, createStreamingAIHandler, type StreamingAIHandlerOptions, type AIRequestHandler, type AIResponse, type AIStreamContext, type ChatMessage, type PlanSummary, type PlannerAction, };
6
8
  export default ReactPlanner;
@@ -1,11 +1,13 @@
1
1
  import { type Plan as PlanType } from "../store/ReactPlannerStore";
2
2
  import "./react-planner.css";
3
+ import type { AIRequestHandler } from "./ai-chat/aiService";
3
4
  interface ReactPlannerProps {
4
5
  catalog: any;
5
6
  plan: PlanType;
6
7
  has3dView?: boolean;
7
8
  onBack?: () => void;
8
9
  onSave?: (data: any) => void;
10
+ onAIMessage?: AIRequestHandler;
9
11
  }
10
- declare const ReactPlanner: ({ plan, catalog, onSave, onBack, has3dView, }: ReactPlannerProps) => import("react/jsx-runtime").JSX.Element;
12
+ declare const ReactPlanner: ({ plan, catalog, onSave, onBack, has3dView, onAIMessage, }: ReactPlannerProps) => import("react/jsx-runtime").JSX.Element;
11
13
  export default ReactPlanner;
@@ -1,2 +1,21 @@
1
- declare const ReactPlannerLoader: () => import("react/jsx-runtime").JSX.Element;
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;
@@ -0,0 +1,6 @@
1
+ import type { AIRequestHandler } from "./aiService";
2
+ interface AIChatBubbleProps {
3
+ onAIMessage?: AIRequestHandler;
4
+ }
5
+ declare const AIChatBubble: ({ onAIMessage }: AIChatBubbleProps) => import("react/jsx-runtime").JSX.Element;
6
+ export default AIChatBubble;
@@ -0,0 +1,8 @@
1
+ import { type AIRequestHandler } from "./aiService";
2
+ interface AIChatPanelProps {
3
+ open: boolean;
4
+ onClose: () => void;
5
+ onAIMessage?: AIRequestHandler;
6
+ }
7
+ declare const AIChatPanel: ({ open, onClose, onAIMessage }: AIChatPanelProps) => import("react/jsx-runtime").JSX.Element;
8
+ export default AIChatPanel;
@@ -0,0 +1,2 @@
1
+ declare const SelectionChips: () => import("react/jsx-runtime").JSX.Element | null;
2
+ export default SelectionChips;
@@ -0,0 +1,5 @@
1
+ interface ThinkingIndicatorProps {
2
+ label?: string;
3
+ }
4
+ declare const ThinkingIndicator: ({ label }: ThinkingIndicatorProps) => import("react/jsx-runtime").JSX.Element;
5
+ export default ThinkingIndicator;
@@ -0,0 +1,340 @@
1
+ export interface ChatMessage {
2
+ role: "user" | "assistant";
3
+ content: string;
4
+ /**
5
+ * Photos attached to a user message — e.g. a scan/photo of a paper floor
6
+ * plan the AI should interpret and turn into `add-room`/`add-wall`/etc.
7
+ * actions. `data` is raw base64 (no `data:...;base64,` prefix). Assistant
8
+ * messages never carry this.
9
+ */
10
+ images?: {
11
+ mediaType: string;
12
+ data: string;
13
+ }[];
14
+ /**
15
+ * True for the library's own "keep going" turn inserted between
16
+ * `done: false` continuation calls — never something the user typed.
17
+ * Several current models (Sonnet 5 and the whole Opus 4.6+/Fable 5
18
+ * family) reject a request whose message list ends on an `assistant`
19
+ * turn ("assistant message prefill" is no longer supported), so a
20
+ * continuation can't just resend the conversation as-is after appending
21
+ * the assistant's own reply — it needs a trailing `user` turn too. This
22
+ * flag exists so the chat UI can skip rendering that turn as if the user
23
+ * had sent it, while it's still included in what's sent to the backend.
24
+ */
25
+ synthetic?: boolean;
26
+ }
27
+ export interface WallInfo {
28
+ id: string;
29
+ v0: string;
30
+ v1: string;
31
+ x0: number;
32
+ y0: number;
33
+ x1: number;
34
+ y1: number;
35
+ length: number;
36
+ orientation: "horizontal" | "vertical" | "diagonal";
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;
44
+ }
45
+ export interface HoleInfo {
46
+ id: string;
47
+ type: string;
48
+ lineID: string;
49
+ offset: number;
50
+ /** Mirrored ALONG its wall — for a door, which end the hinge is at. */
51
+ flipAlongWall: boolean;
52
+ /** Mirrored ACROSS its wall — for a door, which side it opens into. */
53
+ flipAcrossWall: boolean;
54
+ }
55
+ export interface AreaInfo {
56
+ id: string;
57
+ /** "room" = enclosed by walls, "surface" = a free-standing polygon (grass,
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. */
60
+ kind: "room" | "surface" | "area";
61
+ vertices: {
62
+ id: string;
63
+ x: number;
64
+ y: number;
65
+ }[];
66
+ color: string | null;
67
+ /** Floor fill/texture. Mutually exclusive; "none" means unset. */
68
+ pattern: string | null;
69
+ texture: string | null;
70
+ customName: string | null;
71
+ roomCategory: string | null;
72
+ roomType: string | null;
73
+ showName: boolean;
74
+ showSurfaceArea: boolean;
75
+ }
76
+ export interface RoomTypeOption {
77
+ value: string;
78
+ label: string;
79
+ color?: string;
80
+ }
81
+ export interface RoomCategoryOption {
82
+ value: string;
83
+ label: string;
84
+ types: RoomTypeOption[];
85
+ }
86
+ export interface PlanSelection {
87
+ walls: string[];
88
+ holes: string[];
89
+ items: string[];
90
+ areas: string[];
91
+ vertices: string[];
92
+ }
93
+ export interface ItemInfo {
94
+ id: string;
95
+ name: string;
96
+ x: number;
97
+ y: number;
98
+ /** Degrees. Positive reads counter-clockwise in the 2D view. */
99
+ rotation: number;
100
+ /** Uniform size multiplier on the catalog element's own dimensions. */
101
+ scale: number;
102
+ flipHorizontal: boolean;
103
+ flipVertical: boolean;
104
+ }
105
+ export interface PlanSummary {
106
+ layerName: string;
107
+ wallCount: number;
108
+ itemCount: number;
109
+ holeCount: number;
110
+ areaCount: number;
111
+ walls: WallInfo[];
112
+ holes: HoleInfo[];
113
+ areas: AreaInfo[];
114
+ items: ItemInfo[];
115
+ mode: string;
116
+ is3D: boolean;
117
+ showDimensions: boolean;
118
+ sceneWidth: number;
119
+ sceneHeight: number;
120
+ unit: string;
121
+ catalogItemTypes: string[];
122
+ catalogHoleTypes: string[];
123
+ catalogSurfaceTypes: string[];
124
+ /** Fill patterns (hatch styles) available for areas — pass in
125
+ * `set-area-properties.pattern`. Also "none" is always accepted. */
126
+ catalogAreaPatterns: string[];
127
+ /** Textures registered on the current area catalog element — pass in
128
+ * `set-area-properties.texture`. Also "none" is always accepted. */
129
+ catalogAreaTextures: string[];
130
+ /** Textures registered on the current wall catalog element — pass in
131
+ * `set-wall-properties.textureA` / `textureB`. Also "none" is always
132
+ * accepted. */
133
+ catalogWallTextures: string[];
134
+ roomTypes: RoomCategoryOption[];
135
+ selection: PlanSelection;
136
+ }
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 = {
160
+ type: "add-room";
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. */
167
+ width: number;
168
+ height: number;
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. */
177
+ roomCategory?: string;
178
+ roomType?: string;
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;
191
+ pattern?: string;
192
+ texture?: string;
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 = {
221
+ type: "set-wall-properties";
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. */
229
+ thickness?: number;
230
+ height?: number;
231
+ };
232
+ export type SetItemPropertiesAction = {
233
+ type: "set-item-properties";
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. */
242
+ rotation?: number;
243
+ /** Multiplier on the catalog size; the only sizing an item has. */
244
+ scale?: number;
245
+ flipHorizontal?: boolean;
246
+ flipVertical?: boolean;
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;
276
+ };
277
+ export interface AIResponse {
278
+ reply: string;
279
+ actions: PlannerAction[];
280
+ /**
281
+ * Multi-step continuation. Building something big (a whole multi-room
282
+ * floor plan) can need more actions than fit in one well-grounded
283
+ * response — e.g. you want to see a room's real wall IDs (from a fresh
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.
287
+ *
288
+ * Set `done: false` to have the library apply this response's `actions`,
289
+ * take a fresh `summarizePlan()` of the result, and call your handler
290
+ * again — same `messages` array with this response's own `reply` appended
291
+ * as an assistant turn, no synthetic "continue" message injected — so you
292
+ * see your own last reply plus the updated plan and pick up from there.
293
+ * Each intermediate `reply` is shown in the chat as its own message, so
294
+ * narrate what that step did (e.g. "Built the room shells, adding doors
295
+ * next…").
296
+ *
297
+ * Omit, or set `true`, once you're finished — this is also the default
298
+ * for backends that don't implement this field, so single-response
299
+ * behavior is unchanged. The library caps continuations at 8 steps
300
+ * regardless, in case of a backend bug that never stops; don't rely on
301
+ * more than that for one user request.
302
+ */
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
+ }[];
313
+ }
314
+ /**
315
+ * Extras the library hands a handler on every call. Optional on purpose: a
316
+ * handler written against the original two-argument signature keeps working
317
+ * untouched — it just never streams and never gets cancelled.
318
+ */
319
+ export interface AIStreamContext {
320
+ /**
321
+ * Aborted when the planner unmounts mid-request. Pass it to `fetch` so a
322
+ * closed tab stops the backend generating (and billing) into a dead socket.
323
+ */
324
+ signal?: AbortSignal;
325
+ /**
326
+ * Call with each incremental chunk of reply text as it arrives, in order.
327
+ * The chat paints the accumulated text live while the request is still in
328
+ * flight.
329
+ *
330
+ * The deltas are a *preview*, not the result: when the promise resolves,
331
+ * the chat throws the accumulated text away and renders `AIResponse.reply`
332
+ * instead. The two legitimately differ (a backend that re-parses its own
333
+ * output can correct or replace what it streamed), so a handler must still
334
+ * resolve with the complete, authoritative reply — never with `""` on the
335
+ * grounds that it already streamed the text.
336
+ */
337
+ onReplyDelta?: (delta: string) => void;
338
+ }
339
+ export type AIRequestHandler = (messages: ChatMessage[], plan: PlanSummary, ctx?: AIStreamContext) => Promise<AIResponse>;
340
+ export declare function getAIResponse(messages: ChatMessage[], _plan: PlanSummary): Promise<AIResponse>;
@@ -0,0 +1,6 @@
1
+ export interface PendingImage {
2
+ mediaType: string;
3
+ data: string;
4
+ previewUrl: string;
5
+ }
6
+ export declare function fileToChatImage(file: File): Promise<PendingImage>;
@@ -0,0 +1,20 @@
1
+ import type { PlanSummary, PlannerAction } from "./aiService";
2
+ export declare function summarizePlan(planner: any): PlanSummary;
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
+ };
@@ -0,0 +1,31 @@
1
+ import type { AIRequestHandler } from "./aiService";
2
+ /**
3
+ * Client for a backend that speaks the streaming variant of the chat contract
4
+ * (`floor-planner-ai-backend`'s `POST /ai/chat/stream`). Lives in the library
5
+ * so every consumer doesn't hand-roll an SSE parser; the URL and any auth
6
+ * headers still belong to the consuming app — the library never holds a key.
7
+ *
8
+ * Wire format (Server-Sent Events, POST — so NOT `EventSource`-compatible):
9
+ *
10
+ * event: reply data: { delta } zero or more, in order
11
+ * event: final data: { reply, actions, done, … } authoritative, terminal
12
+ * event: error data: { error, type } terminal
13
+ *
14
+ * Only the reply text streams. Actions arrive whole in `final` because a
15
+ * half-parsed action isn't something the planner can apply.
16
+ *
17
+ * Failures *before* the first token (bad key, rate limit, an image sent to a
18
+ * text-only model) come back as an ordinary JSON error with a real status
19
+ * code. Once the stream has started the status line is spent, so anything
20
+ * after that arrives as an `error` frame — hence both paths below.
21
+ */
22
+ export interface StreamingAIHandlerOptions {
23
+ /** Full URL of the streaming endpoint, e.g. `http://host:3001/ai/chat/stream`. */
24
+ url: string;
25
+ /**
26
+ * Extra request headers (auth, tenant id…). A function is re-invoked per
27
+ * request, so a token that rotates doesn't go stale.
28
+ */
29
+ headers?: Record<string, string> | (() => Record<string, string>);
30
+ }
31
+ export declare const createStreamingAIHandler: ({ url, headers, }: StreamingAIHandlerOptions) => AIRequestHandler;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Paces streamed text into the UI at frame rate instead of at network rate.
3
+ *
4
+ * Deltas off an SSE stream don't arrive one token at a time — TCP and any
5
+ * proxy in between coalesce them, so what lands is a few characters, then 80,
6
+ * then nothing for 300ms. Rendering each delta the moment it arrives makes the
7
+ * text jump in uneven blocks. Buffering here and draining a slice per animation
8
+ * frame turns the same bytes into steady output.
9
+ *
10
+ * The drain is proportional (a fixed chars-per-frame rate would fall further
11
+ * and further behind a fast model), so a big burst catches up in a handful of
12
+ * frames rather than trickling out for seconds after the response is done.
13
+ */
14
+ export declare const useTypewriter: () => {
15
+ text: string;
16
+ push: (delta: string) => void;
17
+ reset: () => void;
18
+ getFullText: () => string;
19
+ };
@@ -47,9 +47,33 @@ 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;
73
+ /** Every registered element of a prototype ("items", "holes", "lines"),
74
+ * regardless of the category it was filed under. Backs the sidebar's
75
+ * "similar items" list, which matches on tags rather than category. */
76
+ getElementsOfPrototype(prototype: string): CatalogElement[];
53
77
  getCategory(categoryName: string): CatalogCategory;
54
78
  getPropertyType(type: string): any;
55
79
  registerElement(json: any): void;
@@ -1,24 +1 @@
1
- export default function AreaFactory(name: string, info: any, textures: any): {
2
- name: string;
3
- prototype: string;
4
- info: any;
5
- properties: {
6
- patternColor: {
7
- label: string;
8
- type: string;
9
- defaultValue: string;
10
- };
11
- thickness: {
12
- label: string;
13
- type: string;
14
- defaultValue: {
15
- length: number;
16
- _length: number;
17
- _unit: string;
18
- };
19
- };
20
- };
21
- render2D: (element: any, layer: any, scene: any) => import("react/jsx-runtime").JSX.Element;
22
- render3D: (element: any, layer: any, scene: any) => Promise<import("three").Mesh<import("three").ShapeGeometry, import("three").MeshPhongMaterial, import("three").Object3DEventMap>>;
23
- updateRender3D: (element: any, layer: any, scene: any, mesh: any, oldElement: any, differences: any, selfDestroy: any, selfBuild: any) => Promise<import("three").Mesh<import("three").BufferGeometry<import("three").NormalBufferAttributes, import("three").BufferGeometryEventMap>, import("three").Material | import("three").Material[], import("three").Object3DEventMap>>;
24
- };
1
+ export default function AreaFactory(name: string, info: any, textures: any): any;
@@ -12,6 +12,8 @@ export default function (json: any): {
12
12
  tag: any;
13
13
  description: any;
14
14
  image: any;
15
+ width: any;
16
+ depth: any;
15
17
  };
16
18
  properties: any;
17
19
  render2D: (element: any, layer: any, scene: any) => import("react/jsx-runtime").JSX.Element;
@@ -1,26 +1 @@
1
- export default function WallFactory(name: string, info: any, textures: any): {
2
- name: string;
3
- prototype: string;
4
- info: any;
5
- properties: {
6
- height: {
7
- label: string;
8
- type: string;
9
- defaultValue: {
10
- length: number;
11
- };
12
- };
13
- thickness: {
14
- label: string;
15
- type: string;
16
- defaultValue: {
17
- length: number;
18
- _length: number;
19
- _unit: string;
20
- };
21
- };
22
- };
23
- render2D: (element: any, layer: any, scene: any, mode: string) => import("react/jsx-runtime").JSX.Element;
24
- render3D: (element: any, layer: any, scene: any) => Promise<import("three").Group<import("three").Object3DEventMap>>;
25
- updateRender3D: (element: any, layer: any, scene: any, mesh: any, oldElement: any, differences: any, selfDestroy: any, selfBuild: any) => any;
26
- };
1
+ export default function WallFactory(name: string, info: any, textures: any): any;
@@ -1,4 +1,4 @@
1
- import { MeshPhongMaterial, ShapeGeometry, Mesh } from "three";
1
+ import { MeshStandardMaterial, ShapeGeometry, Mesh } from "three";
2
2
  import * as THREE from "three";
3
3
  import type { Layer, Scene } from "../../models";
4
4
  type TextureData = {
@@ -13,6 +13,6 @@ type TextureData = {
13
13
  heightRepeatScale: number;
14
14
  };
15
15
  };
16
- export declare function createArea(element: any, layer: Layer, scene: Scene, textures: Record<string, TextureData>): Promise<Mesh<ShapeGeometry, MeshPhongMaterial, THREE.Object3DEventMap>>;
16
+ export declare function createArea(element: any, layer: Layer, scene: Scene, textures: Record<string, TextureData>): Promise<Mesh<ShapeGeometry, MeshStandardMaterial, THREE.Object3DEventMap>>;
17
17
  export declare function updatedArea(element: any, layer: Layer, scene: Scene, textures: Record<string, TextureData>, mesh: Mesh, oldElement: any, differences: string[], selfDestroy: () => void, selfBuild: () => Promise<Mesh>): Promise<Mesh>;
18
18
  export {};
@@ -0,0 +1,42 @@
1
+ export interface AreaPattern {
2
+ key: string;
3
+ name: string;
4
+ size: number;
5
+ /**
6
+ * Degrees the whole tiling lattice is turned by, SVG-style (positive =
7
+ * clockwise on screen). Only 45° is periodic under square tiling — see
8
+ * `patternTileDataURI` — so stick to that or leave it out.
9
+ */
10
+ rotation?: number;
11
+ /**
12
+ * The tile's contents as raw SVG markup rather than JSX, because the same
13
+ * string has to serve two very different consumers: React, for the 2D
14
+ * viewer, and a standalone `<svg>` document rasterized into a Three.js
15
+ * texture for the 3D viewer. Anything expressible only as React elements
16
+ * would have to be written twice and would drift.
17
+ */
18
+ marks: (color: string) => string;
19
+ }
20
+ export declare function darkenHex(hex: string, factor?: number): string;
21
+ export declare const AREA_PATTERNS: AreaPattern[];
22
+ export declare function getAreaPattern(key: string | undefined): AreaPattern | undefined;
23
+ /** `patternTransform` for the SVG `<pattern>`, or undefined when unrotated. */
24
+ export declare function patternTransform(pattern: AreaPattern): string | undefined;
25
+ /** The marks, for any React consumer that is building an SVG `<pattern>`. */
26
+ export declare function PatternMarks({ pattern, color, }: {
27
+ pattern: AreaPattern;
28
+ color: string;
29
+ }): import("react/jsx-runtime").JSX.Element;
30
+ /**
31
+ * One tile of `pattern` as an `<svg>` data URI, background included.
32
+ *
33
+ * The 3D floor can't hatch a mesh the way SVG hatches a path, so it gets a
34
+ * texture instead — and the texture is produced by handing the browser the
35
+ * *same* `<pattern>` element the 2D viewer uses. Rotation, stroke widths and
36
+ * spacing therefore come out of one renderer, not two approximations of each
37
+ * other, and the mesh only has to tile the result.
38
+ */
39
+ export declare function patternTileDataURI(pattern: AreaPattern, background: string, markColor: string): {
40
+ uri: string;
41
+ tileSize: number;
42
+ };