@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.
- package/dist/floor-planner.css +1 -1
- package/dist/floor-planner.es.js +44841 -38060
- package/dist/floor-planner.umd.js +65 -51
- package/dist/types/index.d.ts +3 -1
- package/dist/types/react-planner/ReactPlanner.d.ts +3 -1
- package/dist/types/react-planner/ReactPlannerLoader.d.ts +20 -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 +340 -0
- package/dist/types/react-planner/ai-chat/imageUpload.d.ts +6 -0
- package/dist/types/react-planner/ai-chat/plannerActions.d.ts +20 -0
- package/dist/types/react-planner/ai-chat/streamingClient.d.ts +31 -0
- package/dist/types/react-planner/ai-chat/useTypewriter.d.ts +19 -0
- package/dist/types/react-planner/catalog/catalog.d.ts +24 -0
- package/dist/types/react-planner/catalog/factories/AreaFactory.d.ts +1 -24
- package/dist/types/react-planner/catalog/factories/ItemFactory.d.ts +2 -0
- 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/catalog/unknown-element.d.ts +10 -0
- package/dist/types/react-planner/class/area.d.ts +28 -1
- 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/class/surface.d.ts +34 -0
- package/dist/types/react-planner/class/vertex.d.ts +6 -1
- 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/hooks/usePlanThumbnail.d.ts +13 -0
- package/dist/types/react-planner/models.d.ts +19 -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 +93 -0
- package/dist/types/react-planner/sidebar/panel-element-editor/paint-palette.d.ts +6 -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/index.d.ts +3 -1
- 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/line-family.d.ts +29 -0
- package/dist/types/react-planner/utils/name-generator.d.ts +1 -1
- package/dist/types/react-planner/utils/screenshot.d.ts +22 -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/Area.d.ts +7 -1
- 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 +71 -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/hooks/useScreenshotBeforeUnload.d.ts +0 -4
- 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;
|
|
@@ -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;
|
|
@@ -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,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,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;
|
|
@@ -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
|
+
};
|