@lotics/ui 4.6.0 → 4.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +15 -6
- package/examples/tpl_dieline.tsx +22 -5
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -222,8 +222,15 @@ accepts / edits / dismisses, the deterministic app applies. The agent owns judgm
|
|
|
222
222
|
(recognition, estimation, intent→parameters); the app owns geometry, math, and the write.
|
|
223
223
|
Compose the surfaces as a loop, and reach for the right one by job:
|
|
224
224
|
|
|
225
|
-
- **Command** — `PromptField`,
|
|
226
|
-
|
|
225
|
+
- **Command** — `PromptField`, a single-row pill for a TEXT-ONLY trigger (Enter sends; the field
|
|
226
|
+
locks + the send spins while it runs). The imperative "do this" surface. Its `onAttach` is a bare
|
|
227
|
+
trigger with NO review step, so it CANNOT do attach→review→submit — **never auto-run an agent on
|
|
228
|
+
attach.** When a run needs a FILE + a prompt together (attach a photo, review/remove it, add a
|
|
229
|
+
note, THEN send), reach for **`Composer`** instead: fill `actionsButton` with the attach button,
|
|
230
|
+
the `files` slot with `FileThumbnail`s (each `onRemove`-able), and set `sendDisabled` so Send fires
|
|
231
|
+
with an attachment and/or text (`onSend(text)` — the host already holds the uploaded file id). The
|
|
232
|
+
canvas (`tpl_dieline`) uses `Composer` for exactly this; `Composer` morphs into `AgentProgress`
|
|
233
|
+
while running just like the pill.
|
|
227
234
|
- **Show the work** — `AgentRun`: a live feed of the agent's steps (reasoning, tool calls) on a
|
|
228
235
|
spine, the running node a pulsing ink dot (a done step settles to a hollow ring). Transparent
|
|
229
236
|
work, NEVER a bare spinner — the
|
|
@@ -252,8 +259,9 @@ Compose the surfaces as a loop, and reach for the right one by job:
|
|
|
252
259
|
Ten shapes prove the range. The first four PRODUCE — pick by whether the work is a living artifact,
|
|
253
260
|
a conversation, a single pass, or generated prose:
|
|
254
261
|
- **Canvas** (`tpl_dieline`) — the page IS the design on a pannable/zoomable surface: it stays
|
|
255
|
-
CENTRED at every zoom (a floating zoom pill bottom-left), the FLOATING composer at the bottom
|
|
256
|
-
|
|
262
|
+
CENTRED at every zoom (a floating zoom pill bottom-left), the FLOATING composer at the bottom (a
|
|
263
|
+
`Composer`: attach a photo → review/remove it → add a note → send, with `sendDisabled` allowing a
|
|
264
|
+
file-and/or-text submit — NOT auto-run on attach) morphs into `AgentProgress` while running, and a pinned PARAMS PANEL (the `RecordReview` field-card
|
|
257
265
|
in live-edit mode, carrying the single Download; minimizes to a pill) floats centre-right. Panel, zoom pill and composer
|
|
258
266
|
float ON TOP — they never shift the design. (Floating layers use a `pointerEvents:"none"` wrapper
|
|
259
267
|
with the interactive child set `"auto"`; RN-Web ignores `"box-none"` in style, so a full-width
|
|
@@ -309,8 +317,9 @@ controls — while **status/severity/diffs use functional colour** the way the r
|
|
|
309
317
|
|
|
310
318
|
Card chrome (borders, microlabels) stays neutral — colour marks the *state*, never the container.
|
|
311
319
|
The **composer keeps its icons**: `PromptField` is a single-row pill with circular attach + send
|
|
312
|
-
`Button`s; `
|
|
313
|
-
|
|
320
|
+
`Button`s; `Composer` (the file+prompt variant) shows an attach `actionsButton` + a `FileThumbnail`
|
|
321
|
+
attachment row (each `onRemove`-able) + send; `AgentProgress` is the `WaveAvatar` pill. "No icons"
|
|
322
|
+
was only ever about the review surfaces' sparkles/severity glyphs, not functional affordances.
|
|
314
323
|
|
|
315
324
|
---
|
|
316
325
|
|
package/examples/tpl_dieline.tsx
CHANGED
|
@@ -8,9 +8,19 @@ import { PressableHighlight } from "@lotics/ui/pressable_highlight";
|
|
|
8
8
|
import { DotsIndicator } from "@lotics/ui/dots_indicator";
|
|
9
9
|
import { InlineTextInput } from "@lotics/ui/inline_text_input";
|
|
10
10
|
import { AgentProgress } from "@lotics/ui/agent_progress";
|
|
11
|
-
import {
|
|
11
|
+
import { Composer } from "@lotics/ui/composer";
|
|
12
|
+
import { FileThumbnail, type DisplayFile } from "@lotics/ui/file_thumbnail";
|
|
12
13
|
import { type AgentRunStep } from "@lotics/ui/agent_run";
|
|
13
14
|
|
|
15
|
+
// A real app fills the attachment from `useFileUpload` (the uploaded file's URL);
|
|
16
|
+
// this mock uses an inline placeholder so the example needs no network image.
|
|
17
|
+
const MOCK_PHOTO: DisplayFile = {
|
|
18
|
+
id: "att-carton",
|
|
19
|
+
filename: "carton.jpg",
|
|
20
|
+
mimeType: "image/jpeg",
|
|
21
|
+
url: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==",
|
|
22
|
+
};
|
|
23
|
+
|
|
14
24
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
15
25
|
// Template · AI design canvas (the carton "dieline" app) — the FLAGSHIP shape:
|
|
16
26
|
// the page IS the design. The dieline fills the centre, a FLOATING composer sits
|
|
@@ -56,6 +66,7 @@ export function TplDieline() {
|
|
|
56
66
|
const [pan, setPan] = useState({ x: 0, y: 0 });
|
|
57
67
|
const [dragging, setDragging] = useState(false);
|
|
58
68
|
const [prompt, setPrompt] = useState("");
|
|
69
|
+
const [attachment, setAttachment] = useState<DisplayFile | null>(null);
|
|
59
70
|
|
|
60
71
|
// Drag-to-pan — like a design tool: grab anywhere on the canvas and move it.
|
|
61
72
|
// The committed pan is read at grant via a ref (the responder is built once).
|
|
@@ -116,6 +127,7 @@ export function TplDieline() {
|
|
|
116
127
|
const submitPrompt = (text: string) => {
|
|
117
128
|
setPrompt("");
|
|
118
129
|
if (!designReady) {
|
|
130
|
+
setAttachment(null); // the attached photo is consumed into the run
|
|
119
131
|
startRecognize();
|
|
120
132
|
return;
|
|
121
133
|
}
|
|
@@ -136,6 +148,7 @@ export function TplDieline() {
|
|
|
136
148
|
|
|
137
149
|
const newSession = () => {
|
|
138
150
|
setRun(null);
|
|
151
|
+
setAttachment(null);
|
|
139
152
|
setRevealed(0);
|
|
140
153
|
setDesignReady(false);
|
|
141
154
|
setDims({ L: 320, W: 230, H: 150 });
|
|
@@ -197,12 +210,16 @@ export function TplDieline() {
|
|
|
197
210
|
{phase === "working" ? (
|
|
198
211
|
<AgentProgress steps={liveSteps} state="streaming" />
|
|
199
212
|
) : (
|
|
200
|
-
<
|
|
213
|
+
<Composer
|
|
201
214
|
value={prompt}
|
|
202
215
|
onChangeText={setPrompt}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
placeholder={designReady ? "Describe a change — “make it 5 mm taller”" : "Attach a photo
|
|
216
|
+
onSend={submitPrompt}
|
|
217
|
+
sendDisabled={prompt.trim().length === 0 && !attachment}
|
|
218
|
+
placeholder={designReady ? "Describe a change — “make it 5 mm taller”" : "Attach a photo, add a note, then send…"}
|
|
219
|
+
actionsButton={
|
|
220
|
+
<Button icon="paperclip" color="secondary" accessibilityLabel="Attach a photo" onPress={() => setAttachment(MOCK_PHOTO)} />
|
|
221
|
+
}
|
|
222
|
+
files={attachment ? <FileThumbnail file={attachment} size={56} onRemove={() => setAttachment(null)} /> : null}
|
|
206
223
|
/>
|
|
207
224
|
)}
|
|
208
225
|
</View>
|