@osamash/floor-planner 1.2.2 → 1.2.4
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 +43937 -37765
- package/dist/floor-planner.umd.js +59 -49
- package/dist/types/index.d.ts +3 -1
- package/dist/types/react-planner/ReactPlanner.d.ts +3 -1
- package/dist/types/react-planner/ai-chat/AIChatBubble.d.ts +6 -0
- package/dist/types/react-planner/ai-chat/AIChatPanel.d.ts +8 -0
- package/dist/types/react-planner/ai-chat/SelectionChips.d.ts +2 -0
- package/dist/types/react-planner/ai-chat/ThinkingIndicator.d.ts +5 -0
- package/dist/types/react-planner/ai-chat/aiService.d.ts +327 -0
- package/dist/types/react-planner/ai-chat/imageUpload.d.ts +6 -0
- package/dist/types/react-planner/ai-chat/plannerActions.d.ts +3 -0
- package/dist/types/react-planner/ai-chat/streamingClient.d.ts +32 -0
- package/dist/types/react-planner/ai-chat/useTypewriter.d.ts +19 -0
- package/dist/types/react-planner/catalog/catalog.d.ts +4 -0
- package/dist/types/react-planner/catalog/factories/AreaFactory.d.ts +1 -24
- package/dist/types/react-planner/catalog/factories/WallFactory.d.ts +1 -26
- package/dist/types/react-planner/catalog/factories/area-factory-3d.d.ts +2 -2
- package/dist/types/react-planner/catalog/factories/area-patterns.d.ts +42 -0
- package/dist/types/react-planner/catalog/factories/room-types.d.ts +22 -0
- package/dist/types/react-planner/catalog/factories/wall-factory-3d.d.ts +1 -1
- package/dist/types/react-planner/class/area.d.ts +23 -0
- package/dist/types/react-planner/class/hole.d.ts +13 -0
- package/dist/types/react-planner/class/index.d.ts +3 -1
- package/dist/types/react-planner/class/item.d.ts +22 -0
- package/dist/types/react-planner/class/line.d.ts +12 -3
- package/dist/types/react-planner/class/room.d.ts +18 -0
- package/dist/types/react-planner/components/ErrorBoundary.d.ts +19 -0
- package/dist/types/react-planner/components/SearchInput.d.ts +2 -1
- package/dist/types/react-planner/constants.d.ts +3 -0
- package/dist/types/react-planner/footer-bar/Footerbar.d.ts +1 -1
- package/dist/types/react-planner/models.d.ts +7 -0
- package/dist/types/react-planner/plugins/autosave.d.ts +1 -1
- package/dist/types/react-planner/plugins/keyboard.d.ts +1 -1
- package/dist/types/react-planner/sidebar/Panel.d.ts +2 -1
- package/dist/types/react-planner/sidebar/panel-element-editor/AreaAttributesEditor.d.ts +8 -0
- package/dist/types/react-planner/sidebar/panel-element-editor/AttributesEditor.d.ts +4 -1
- package/dist/types/react-planner/sidebar/panel-element-editor/HoleAttributesEditor.d.ts +3 -4
- package/dist/types/react-planner/sidebar/panel-element-editor/ItemAttributesEditor.d.ts +3 -4
- package/dist/types/react-planner/sidebar/panel-element-editor/SimilarElements.d.ts +15 -0
- package/dist/types/react-planner/sidebar/panel-element-editor/WallAttributesEditor.d.ts +8 -0
- package/dist/types/react-planner/sidebar/panel-element-editor/editor-ui.d.ts +42 -0
- package/dist/types/react-planner/toolbar/CatalogPicker.d.ts +8 -0
- package/dist/types/react-planner/toolbar/NumberStepper.d.ts +11 -0
- package/dist/types/react-planner/toolbar/ProjectMiniMap.d.ts +11 -0
- package/dist/types/react-planner/toolbar/ProjectPanel.d.ts +2 -0
- package/dist/types/react-planner/toolbar/ToolbarRail.d.ts +15 -0
- package/dist/types/react-planner/toolbar/ToolbarSubPanel.d.ts +23 -0
- package/dist/types/react-planner/toolbar/ZoomControls.d.ts +5 -0
- package/dist/types/react-planner/utils/angle.d.ts +9 -0
- package/dist/types/react-planner/utils/geometry.d.ts +15 -0
- package/dist/types/react-planner/utils/image-trim.d.ts +25 -0
- package/dist/types/react-planner/utils/item-footprint.d.ts +23 -0
- package/dist/types/react-planner/utils/item-snap.d.ts +56 -0
- package/dist/types/react-planner/utils/item-transform.d.ts +9 -0
- package/dist/types/react-planner/utils/name-generator.d.ts +1 -1
- package/dist/types/react-planner/utils/three-cache.d.ts +17 -0
- package/dist/types/react-planner/utils/three-materials.d.ts +33 -0
- package/dist/types/react-planner/utils/threeCSG.es6.d.ts +1 -1
- package/dist/types/react-planner/utils/wall-miter.d.ts +53 -0
- package/dist/types/react-planner/utils/zoom.d.ts +2 -2
- package/dist/types/react-planner/viewer2d/Grids.d.ts +1 -1
- package/dist/types/react-planner/viewer2d/Ruler.d.ts +6 -1
- package/dist/types/react-planner/viewer2d/SelectionToolbar.d.ts +12 -0
- package/dist/types/react-planner/viewer2d/WallHoverHandles.d.ts +13 -0
- package/dist/types/react-planner/viewer3d/selection-gizmo.d.ts +57 -0
- package/dist/types/react-planner/viewer3d/viewer3d.d.ts +61 -0
- package/dist/types/shared-style.d.ts +6 -0
- package/dist/types/store/ReactPlannerStore.d.ts +102 -1
- package/package.json +1 -1
- package/dist/types/react-planner/components/PropertyString.d.ts +0 -10
- package/dist/types/react-planner/toolbar/Popup.d.ts +0 -11
- package/dist/types/react-planner/viewer2d/GridHorizontalStreak.d.ts +0 -7
- package/dist/types/react-planner/viewer2d/GridVerticalStreak.d.ts +0 -7
package/dist/types/index.d.ts
CHANGED
|
@@ -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
|
-
|
|
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;
|
|
@@ -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,327 @@
|
|
|
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
|
+
}
|
|
39
|
+
export interface HoleInfo {
|
|
40
|
+
id: string;
|
|
41
|
+
type: string;
|
|
42
|
+
lineID: string;
|
|
43
|
+
offset: number;
|
|
44
|
+
/** Mirrored ALONG its wall — for a door, which end the hinge is at. */
|
|
45
|
+
flipAlongWall: boolean;
|
|
46
|
+
/** Mirrored ACROSS its wall — for a door, which side it opens into. */
|
|
47
|
+
flipAcrossWall: boolean;
|
|
48
|
+
}
|
|
49
|
+
export interface AreaInfo {
|
|
50
|
+
id: string;
|
|
51
|
+
/** "room" = enclosed by walls, "surface" = a free-standing polygon (grass,
|
|
52
|
+
* patio, driveway…). Only surfaces can be transformed with
|
|
53
|
+
* `set-surface-transform` / `duplicate-surface`. */
|
|
54
|
+
kind: "room" | "surface" | "area";
|
|
55
|
+
vertices: {
|
|
56
|
+
id: string;
|
|
57
|
+
x: number;
|
|
58
|
+
y: number;
|
|
59
|
+
}[];
|
|
60
|
+
color: string | null;
|
|
61
|
+
customName: string | null;
|
|
62
|
+
roomCategory: string | null;
|
|
63
|
+
roomType: string | null;
|
|
64
|
+
showName: boolean;
|
|
65
|
+
showSurfaceArea: boolean;
|
|
66
|
+
}
|
|
67
|
+
export interface RoomTypeOption {
|
|
68
|
+
value: string;
|
|
69
|
+
label: string;
|
|
70
|
+
color?: string;
|
|
71
|
+
}
|
|
72
|
+
export interface RoomCategoryOption {
|
|
73
|
+
value: string;
|
|
74
|
+
label: string;
|
|
75
|
+
types: RoomTypeOption[];
|
|
76
|
+
}
|
|
77
|
+
export interface PlanSelection {
|
|
78
|
+
walls: string[];
|
|
79
|
+
holes: string[];
|
|
80
|
+
items: string[];
|
|
81
|
+
areas: string[];
|
|
82
|
+
vertices: string[];
|
|
83
|
+
}
|
|
84
|
+
export interface ItemInfo {
|
|
85
|
+
id: string;
|
|
86
|
+
name: string;
|
|
87
|
+
x: number;
|
|
88
|
+
y: number;
|
|
89
|
+
/** Degrees. Positive reads counter-clockwise in the 2D view. */
|
|
90
|
+
rotation: number;
|
|
91
|
+
/** Uniform size multiplier on the catalog element's own dimensions. */
|
|
92
|
+
scale: number;
|
|
93
|
+
flipHorizontal: boolean;
|
|
94
|
+
flipVertical: boolean;
|
|
95
|
+
}
|
|
96
|
+
export interface PlanSummary {
|
|
97
|
+
layerName: string;
|
|
98
|
+
wallCount: number;
|
|
99
|
+
itemCount: number;
|
|
100
|
+
holeCount: number;
|
|
101
|
+
areaCount: number;
|
|
102
|
+
walls: WallInfo[];
|
|
103
|
+
holes: HoleInfo[];
|
|
104
|
+
areas: AreaInfo[];
|
|
105
|
+
items: ItemInfo[];
|
|
106
|
+
mode: string;
|
|
107
|
+
is3D: boolean;
|
|
108
|
+
showDimensions: boolean;
|
|
109
|
+
sceneWidth: number;
|
|
110
|
+
sceneHeight: number;
|
|
111
|
+
unit: string;
|
|
112
|
+
catalogItemTypes: string[];
|
|
113
|
+
catalogHoleTypes: string[];
|
|
114
|
+
catalogSurfaceTypes: string[];
|
|
115
|
+
/** Fill patterns (hatch styles) available for areas — pass in
|
|
116
|
+
* `set-area-properties.pattern`. Also "none" is always accepted. */
|
|
117
|
+
catalogAreaPatterns: string[];
|
|
118
|
+
/** Textures registered on the current area catalog element — pass in
|
|
119
|
+
* `set-area-properties.texture`. Also "none" is always accepted. */
|
|
120
|
+
catalogAreaTextures: string[];
|
|
121
|
+
/** Textures registered on the current wall catalog element — pass in
|
|
122
|
+
* `set-wall-properties.textureA` / `textureB`. Also "none" is always
|
|
123
|
+
* accepted. */
|
|
124
|
+
catalogWallTextures: string[];
|
|
125
|
+
roomTypes: RoomCategoryOption[];
|
|
126
|
+
selection: PlanSelection;
|
|
127
|
+
}
|
|
128
|
+
export type PlannerAction = {
|
|
129
|
+
type: "add-wall";
|
|
130
|
+
x0: number;
|
|
131
|
+
y0: number;
|
|
132
|
+
x1: number;
|
|
133
|
+
y1: number;
|
|
134
|
+
id?: string;
|
|
135
|
+
} | {
|
|
136
|
+
type: "add-room";
|
|
137
|
+
x: number;
|
|
138
|
+
y: number;
|
|
139
|
+
width: number;
|
|
140
|
+
height: number;
|
|
141
|
+
id?: string;
|
|
142
|
+
} | {
|
|
143
|
+
type: "add-surface";
|
|
144
|
+
points: {
|
|
145
|
+
x: number;
|
|
146
|
+
y: number;
|
|
147
|
+
}[];
|
|
148
|
+
surfaceType?: string;
|
|
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;
|
|
174
|
+
roomCategory?: string;
|
|
175
|
+
roomType?: string;
|
|
176
|
+
patternColor?: string;
|
|
177
|
+
/** One of PlanSummary.catalogAreaPatterns keys (or "none"). */
|
|
178
|
+
pattern?: string;
|
|
179
|
+
/** One of PlanSummary.catalogAreaTextures keys (or "none"). */
|
|
180
|
+
texture?: string;
|
|
181
|
+
showName?: boolean;
|
|
182
|
+
showSurfaceArea?: boolean;
|
|
183
|
+
} | {
|
|
184
|
+
type: "set-wall-properties";
|
|
185
|
+
wallID: string;
|
|
186
|
+
colorA?: string;
|
|
187
|
+
colorB?: string;
|
|
188
|
+
/** One of PlanSummary.catalogWallTextures keys (or "none"). */
|
|
189
|
+
textureA?: string;
|
|
190
|
+
textureB?: string;
|
|
191
|
+
/** cm */
|
|
192
|
+
thickness?: number;
|
|
193
|
+
/** cm */
|
|
194
|
+
height?: number;
|
|
195
|
+
customName?: string;
|
|
196
|
+
showName?: boolean;
|
|
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. */
|
|
201
|
+
type: "set-item-properties";
|
|
202
|
+
itemID: string;
|
|
203
|
+
x?: number;
|
|
204
|
+
y?: number;
|
|
205
|
+
/** Degrees, absolute (not a delta). Positive = counter-clockwise. */
|
|
206
|
+
rotation?: number;
|
|
207
|
+
/** Uniform size multiplier, clamped to 0.2–8. 1 = catalog size. */
|
|
208
|
+
scale?: number;
|
|
209
|
+
flipHorizontal?: boolean;
|
|
210
|
+
flipVertical?: boolean;
|
|
211
|
+
} | {
|
|
212
|
+
/** Edit an existing door / window in place. */
|
|
213
|
+
type: "set-hole-properties";
|
|
214
|
+
holeID: string;
|
|
215
|
+
/** 0..1 along its wall. */
|
|
216
|
+
offset?: number;
|
|
217
|
+
/** Mirror along the wall — moves a door's hinge to the other end. */
|
|
218
|
+
flipAlongWall?: boolean;
|
|
219
|
+
/** Mirror across the wall — flips which side a door opens into. */
|
|
220
|
+
flipAcrossWall?: boolean;
|
|
221
|
+
} | {
|
|
222
|
+
/** Rotate / mirror a surface polygon about its own center. Rooms are
|
|
223
|
+
* rejected: their vertices are shared with the walls around them. */
|
|
224
|
+
type: "set-surface-transform";
|
|
225
|
+
areaID: string;
|
|
226
|
+
/** Degrees, relative to where the surface is now. Positive =
|
|
227
|
+
* counter-clockwise on screen, as for `set-item-properties.rotation`. */
|
|
228
|
+
rotate?: number;
|
|
229
|
+
/** "x" flips it left-right, "y" top-bottom. */
|
|
230
|
+
mirror?: "x" | "y";
|
|
231
|
+
} | {
|
|
232
|
+
/** Copy a surface polygon, keeping its look, set down clear of the
|
|
233
|
+
* original. The copy ends up selected. */
|
|
234
|
+
type: "duplicate-surface";
|
|
235
|
+
areaID: string;
|
|
236
|
+
} | {
|
|
237
|
+
/** Copy an item, keeping its rotation / scale / flips, offset slightly
|
|
238
|
+
* from the original. The copy ends up selected. */
|
|
239
|
+
type: "duplicate-item";
|
|
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";
|
|
272
|
+
};
|
|
273
|
+
export interface AIResponse {
|
|
274
|
+
reply: string;
|
|
275
|
+
actions: PlannerAction[];
|
|
276
|
+
/**
|
|
277
|
+
* Multi-step continuation. Building something big (a whole multi-room
|
|
278
|
+
* floor plan) can need more actions than fit in one well-grounded
|
|
279
|
+
* response — e.g. you want to see a room's real wall IDs (from a fresh
|
|
280
|
+
* `PlanSummary`) before placing doors on it, rather than guessing with a
|
|
281
|
+
* `$tag` (see the note on `PlannerAction` above; `$tag`s work within one
|
|
282
|
+
* response, this is for spanning several).
|
|
283
|
+
*
|
|
284
|
+
* Set `done: false` to have the library apply this response's `actions`,
|
|
285
|
+
* take a fresh `summarizePlan()` of the result, and call your handler
|
|
286
|
+
* again — same `messages` array with this response's own `reply` appended
|
|
287
|
+
* as an assistant turn, no synthetic "continue" message injected — so you
|
|
288
|
+
* see your own last reply plus the updated plan and pick up from there.
|
|
289
|
+
* Each intermediate `reply` is shown in the chat as its own message, so
|
|
290
|
+
* narrate what that step did (e.g. "Built the room shells, adding doors
|
|
291
|
+
* next…").
|
|
292
|
+
*
|
|
293
|
+
* Omit, or set `true`, once you're finished — this is also the default
|
|
294
|
+
* for backends that don't implement this field, so single-response
|
|
295
|
+
* behavior is unchanged. The library caps continuations at 8 steps
|
|
296
|
+
* regardless, in case of a backend bug that never stops; don't rely on
|
|
297
|
+
* more than that for one user request.
|
|
298
|
+
*/
|
|
299
|
+
done?: boolean;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Extras the library hands a handler on every call. Optional on purpose: a
|
|
303
|
+
* handler written against the original two-argument signature keeps working
|
|
304
|
+
* untouched — it just never streams and never gets cancelled.
|
|
305
|
+
*/
|
|
306
|
+
export interface AIStreamContext {
|
|
307
|
+
/**
|
|
308
|
+
* Aborted when the planner unmounts mid-request. Pass it to `fetch` so a
|
|
309
|
+
* closed tab stops the backend generating (and billing) into a dead socket.
|
|
310
|
+
*/
|
|
311
|
+
signal?: AbortSignal;
|
|
312
|
+
/**
|
|
313
|
+
* Call with each incremental chunk of reply text as it arrives, in order.
|
|
314
|
+
* The chat paints the accumulated text live while the request is still in
|
|
315
|
+
* flight.
|
|
316
|
+
*
|
|
317
|
+
* The deltas are a *preview*, not the result: when the promise resolves,
|
|
318
|
+
* the chat throws the accumulated text away and renders `AIResponse.reply`
|
|
319
|
+
* instead. The two legitimately differ (a backend that re-parses its own
|
|
320
|
+
* output can correct or replace what it streamed), so a handler must still
|
|
321
|
+
* resolve with the complete, authoritative reply — never with `""` on the
|
|
322
|
+
* grounds that it already streamed the text.
|
|
323
|
+
*/
|
|
324
|
+
onReplyDelta?: (delta: string) => void;
|
|
325
|
+
}
|
|
326
|
+
export type AIRequestHandler = (messages: ChatMessage[], plan: PlanSummary, ctx?: AIStreamContext) => Promise<AIResponse>;
|
|
327
|
+
export declare function getAIResponse(messages: ChatMessage[], _plan: PlanSummary): Promise<AIResponse>;
|
|
@@ -0,0 +1,32 @@
|
|
|
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: meta data: { provider, model } once, before any output
|
|
11
|
+
* event: reply data: { delta } zero or more, in order
|
|
12
|
+
* event: final data: { reply, actions, done, … } authoritative, terminal
|
|
13
|
+
* event: error data: { error, type } terminal
|
|
14
|
+
*
|
|
15
|
+
* Only the reply text streams. Actions arrive whole in `final` because a
|
|
16
|
+
* half-parsed action isn't something the planner can apply.
|
|
17
|
+
*
|
|
18
|
+
* Failures *before* the first token (bad key, rate limit, an image sent to a
|
|
19
|
+
* text-only model) come back as an ordinary JSON error with a real status
|
|
20
|
+
* code. Once the stream has started the status line is spent, so anything
|
|
21
|
+
* after that arrives as an `error` frame — hence both paths below.
|
|
22
|
+
*/
|
|
23
|
+
export interface StreamingAIHandlerOptions {
|
|
24
|
+
/** Full URL of the streaming endpoint, e.g. `http://host:3001/ai/chat/stream`. */
|
|
25
|
+
url: string;
|
|
26
|
+
/**
|
|
27
|
+
* Extra request headers (auth, tenant id…). A function is re-invoked per
|
|
28
|
+
* request, so a token that rotates doesn't go stale.
|
|
29
|
+
*/
|
|
30
|
+
headers?: Record<string, string> | (() => Record<string, string>);
|
|
31
|
+
}
|
|
32
|
+
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
|
+
};
|
|
@@ -50,6 +50,10 @@ export default class Catalog {
|
|
|
50
50
|
unit: Unit;
|
|
51
51
|
constructor(unit?: Unit);
|
|
52
52
|
getElement(type: string): CatalogElement;
|
|
53
|
+
/** Every registered element of a prototype ("items", "holes", "lines"),
|
|
54
|
+
* regardless of the category it was filed under. Backs the sidebar's
|
|
55
|
+
* "similar items" list, which matches on tags rather than category. */
|
|
56
|
+
getElementsOfPrototype(prototype: string): CatalogElement[];
|
|
53
57
|
getCategory(categoryName: string): CatalogCategory;
|
|
54
58
|
getPropertyType(type: string): any;
|
|
55
59
|
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;
|
|
@@ -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 {
|
|
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,
|
|
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
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface RoomType {
|
|
2
|
+
value: string;
|
|
3
|
+
label: string;
|
|
4
|
+
color: string;
|
|
5
|
+
}
|
|
6
|
+
export interface RoomCategory {
|
|
7
|
+
value: string;
|
|
8
|
+
label: string;
|
|
9
|
+
types: RoomType[];
|
|
10
|
+
}
|
|
11
|
+
export declare const ROOM_CATEGORIES: RoomCategory[];
|
|
12
|
+
export declare function getCategory(value: string | undefined): RoomCategory | undefined;
|
|
13
|
+
export declare function getRoomType(categoryValue: string | undefined, typeValue: string | undefined): RoomType | undefined;
|
|
14
|
+
export declare function getRoomTypeLabel(categoryValue: string | undefined, typeValue: string | undefined): string;
|
|
15
|
+
export declare function getRoomTypeColor(categoryValue: string | undefined, typeValue: string | undefined): string | undefined;
|
|
16
|
+
export declare const DEFAULT_ROOM_CATEGORY = "";
|
|
17
|
+
export declare const DEFAULT_ROOM_TYPE = "";
|
|
18
|
+
export type AreaKind = "Room" | "Surface" | "Area";
|
|
19
|
+
export declare function getAreaKind(area: any, layer: any): AreaKind;
|
|
20
|
+
export declare function deriveAreaLabel(area: any, layer: any, opts?: {
|
|
21
|
+
defaultLabel?: string;
|
|
22
|
+
}): string;
|
|
@@ -13,5 +13,5 @@ type TextureInfo = {
|
|
|
13
13
|
};
|
|
14
14
|
};
|
|
15
15
|
export declare function buildWall(element: any, layer: Layer, scene: Scene, textures: Record<string, TextureInfo>): Promise<Group>;
|
|
16
|
-
export declare function updatedWall(element: any, layer: any, scene: any, textures: any, mesh: any,
|
|
16
|
+
export declare function updatedWall(element: any, layer: any, scene: any, textures: any, mesh: any, _oldElement: any, differences: any, selfDestroy: any, selfBuild: any): any;
|
|
17
17
|
export {};
|
|
@@ -1,6 +1,29 @@
|
|
|
1
1
|
import { Set } from "immutable";
|
|
2
2
|
declare class Area {
|
|
3
3
|
static getSurfaceLines(state: any, layerID: string, areaID: string): Set<string>;
|
|
4
|
+
static center(state: any, layerID: string, areaID: string): {
|
|
5
|
+
x: number;
|
|
6
|
+
y: number;
|
|
7
|
+
} | null;
|
|
8
|
+
static transform(state: any, layerID: string, areaID: string, transform: (point: {
|
|
9
|
+
x: number;
|
|
10
|
+
y: number;
|
|
11
|
+
}) => {
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
}): {
|
|
15
|
+
updatedState: any;
|
|
16
|
+
};
|
|
17
|
+
/** Rotate about the area's center. Maths convention: in these y-up scene
|
|
18
|
+
* coords a positive angle turns counter-clockwise, which reads *clockwise*
|
|
19
|
+
* in the y-flipped 2D view. */
|
|
20
|
+
static rotate(state: any, layerID: string, areaID: string, degrees: number): {
|
|
21
|
+
updatedState: any;
|
|
22
|
+
};
|
|
23
|
+
/** Mirror about the area's center — "x" flips it left-right, "y" top-bottom. */
|
|
24
|
+
static mirror(state: any, layerID: string, areaID: string, axis: "x" | "y"): {
|
|
25
|
+
updatedState: any;
|
|
26
|
+
};
|
|
4
27
|
static add(state: any, layerID: any, type: any, verticesCoords: any, catalog: any): {
|
|
5
28
|
updatedState: any;
|
|
6
29
|
area: undefined;
|
|
@@ -28,6 +28,16 @@ declare class Hole {
|
|
|
28
28
|
static endDraggingHole(state: any, x: any, y: any): {
|
|
29
29
|
updatedState: any;
|
|
30
30
|
};
|
|
31
|
+
static flip(state: any, layerID: any, holeID: any, axis?: "x" | "y"): {
|
|
32
|
+
updatedState: any;
|
|
33
|
+
};
|
|
34
|
+
static duplicate(state: any, layerID: any, holeID: any): {
|
|
35
|
+
updatedState: any;
|
|
36
|
+
hole?: undefined;
|
|
37
|
+
} | {
|
|
38
|
+
updatedState: any;
|
|
39
|
+
hole: any;
|
|
40
|
+
};
|
|
31
41
|
static setProperties(state: any, layerID: any, holeID: any, properties: any): {
|
|
32
42
|
updatedState: any;
|
|
33
43
|
};
|
|
@@ -40,6 +50,9 @@ declare class Hole {
|
|
|
40
50
|
static updateJsProperties(state: any, layerID: any, holeID: any, properties: any): {
|
|
41
51
|
updatedState: any;
|
|
42
52
|
};
|
|
53
|
+
static mergeAttributes(state: any, layerID: any, holeID: any, attributes: any): {
|
|
54
|
+
updatedState: any;
|
|
55
|
+
};
|
|
43
56
|
static setAttributes(state: any, layerID: any, holeID: any, holesAttributes: any): {
|
|
44
57
|
updatedState: any;
|
|
45
58
|
};
|