@lotics/ui 7.19.3 → 9.0.0
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 +298 -159
- package/examples/tpl_allocate.tsx +2 -2
- package/examples/tpl_attendance.tsx +2 -2
- package/examples/tpl_calendar.tsx +1 -1
- package/examples/tpl_dashboard.tsx +1 -1
- package/examples/tpl_dieline.tsx +3 -3
- package/examples/tpl_documents.tsx +790 -0
- package/examples/tpl_item_list.tsx +1015 -124
- package/examples/tpl_lookup.tsx +37 -10
- package/examples/tpl_pick.tsx +3 -3
- package/examples/tpl_pivot.tsx +1 -1
- package/examples/tpl_record.tsx +1354 -0
- package/examples/tpl_report.tsx +7 -7
- package/examples/tpl_rollup.tsx +6 -6
- package/examples/tpl_shifts.tsx +2 -2
- package/examples/tpl_statements.tsx +221 -0
- package/examples/tpl_stock.tsx +7 -7
- package/examples/tpl_task_board.tsx +48 -32
- package/examples/tpl_tasks.tsx +47 -47
- package/examples/tpl_tower.tsx +2 -2
- package/package.json +9 -9
- package/src/agent_run.tsx +1 -1
- package/src/capture_row.tsx +59 -0
- package/src/change_review.tsx +732 -236
- package/src/checklist.tsx +104 -0
- package/src/chip.tsx +12 -3
- package/src/composer.tsx +22 -1
- package/src/confidence.tsx +1 -1
- package/src/control_surface.ts +0 -14
- package/src/detail_row.tsx +137 -10
- package/src/finding.tsx +112 -80
- package/src/floating_action_bar.tsx +1 -1
- package/src/inline_date_picker.tsx +8 -3
- package/src/inline_edit.tsx +40 -10
- package/src/inline_member_select.tsx +3 -0
- package/src/inline_number_input.tsx +5 -2
- package/src/inline_select.tsx +8 -3
- package/src/inline_tag_select.tsx +140 -0
- package/src/inline_text_input.tsx +5 -2
- package/src/inline_time_picker.tsx +5 -2
- package/src/ledger.tsx +220 -0
- package/src/locale.tsx +43 -16
- package/src/progress_bar.tsx +32 -1
- package/src/record_summary.tsx +101 -0
- package/src/section_heading.tsx +16 -8
- package/src/sources.tsx +8 -5
- package/src/suggestion_chip.tsx +47 -0
- package/src/use_section_nav.test.ts +69 -0
- package/src/use_section_nav.ts +59 -0
- package/examples/tpl_assistant.tsx +0 -174
- package/examples/tpl_billing.tsx +0 -344
- package/examples/tpl_briefing.tsx +0 -121
- package/examples/tpl_compare.tsx +0 -133
- package/examples/tpl_crosscheck.tsx +0 -120
- package/examples/tpl_detail.tsx +0 -232
- package/examples/tpl_directory.tsx +0 -260
- package/examples/tpl_draft.tsx +0 -163
- package/examples/tpl_extract.tsx +0 -193
- package/examples/tpl_intake.tsx +0 -206
- package/examples/tpl_match.tsx +0 -134
- package/examples/tpl_order.tsx +0 -482
- package/examples/tpl_quick.tsx +0 -211
- package/examples/tpl_ratedesk.tsx +0 -386
- package/examples/tpl_record_plain.tsx +0 -259
- package/examples/tpl_settings.tsx +0 -178
- package/examples/tpl_timeline.tsx +0 -244
- package/examples/tpl_triage.tsx +0 -112
- package/examples/tpl_wizard.tsx +0 -223
- package/src/discrepancy.tsx +0 -114
- package/src/match_sides.tsx +0 -79
- package/src/record_fields.tsx +0 -68
- package/src/review_card.tsx +0 -172
- package/src/scored_option.tsx +0 -138
- package/src/spec_list.tsx +0 -81
- package/src/triage_row.tsx +0 -99
package/examples/tpl_tasks.tsx
CHANGED
|
@@ -12,13 +12,13 @@ import { InlineTextInput } from "@lotics/ui/inline_text_input";
|
|
|
12
12
|
import { InlineDatePicker } from "@lotics/ui/inline_date_picker";
|
|
13
13
|
import { InlineSelect } from "@lotics/ui/inline_select";
|
|
14
14
|
import { Select } from "@lotics/ui/select";
|
|
15
|
-
import {
|
|
15
|
+
import { CaptureRow } from "@lotics/ui/capture_row";
|
|
16
16
|
import { Button } from "@lotics/ui/button";
|
|
17
17
|
import { FilesEditor } from "@lotics/ui/files_editor";
|
|
18
18
|
import type { DisplayFile } from "@lotics/ui/file_thumbnail";
|
|
19
19
|
import { Composer } from "@lotics/ui/composer";
|
|
20
20
|
import { AgentRun, type AgentRunItem } from "@lotics/ui/agent_run";
|
|
21
|
-
import { ChangeReview, ChangeReviewActions, type
|
|
21
|
+
import { Change, ChangeReview, ChangeReviewActions, ChangeReviewHeader, ChangeSummary, type ChangeStatus } from "@lotics/ui/change_review";
|
|
22
22
|
import { Dialog, DialogHeader, DialogHeaderTitle, DialogScrollArea, DialogFooter } from "@lotics/ui/dialog";
|
|
23
23
|
import { CompletionState } from "@lotics/ui/completion_state";
|
|
24
24
|
import { Confidence, type ConfidenceLevel } from "@lotics/ui/confidence";
|
|
@@ -27,7 +27,7 @@ import { formatDate, toISODate } from "@lotics/ui/format_date";
|
|
|
27
27
|
type TagOption = { value: string; label: string };
|
|
28
28
|
|
|
29
29
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
30
|
-
// Template ·
|
|
30
|
+
// Template · Task list — the Apple-Reminders shape for PERSONAL lists.
|
|
31
31
|
// There's no Task component: a row is composed directly from primitives
|
|
32
32
|
// (CheckCircle + an inline-editable title + a quiet meta line), which is the most
|
|
33
33
|
// flexible way to build a task surface. Quiet, scannable rows you tick to finish
|
|
@@ -160,7 +160,7 @@ export function TplTasks() {
|
|
|
160
160
|
const [phase, setPhase] = useState<Phase>("idle");
|
|
161
161
|
const [revealed, setRevealed] = useState(0);
|
|
162
162
|
const [proposals, setProposals] = useState<Proposal[]>([]);
|
|
163
|
-
const [decisions, setDecisions] = useState<Record<string,
|
|
163
|
+
const [decisions, setDecisions] = useState<Record<string, ChangeStatus>>({});
|
|
164
164
|
const [addedCount, setAddedCount] = useState(0);
|
|
165
165
|
|
|
166
166
|
const patch = (id: string, next: Partial<Task>) =>
|
|
@@ -182,7 +182,7 @@ export function TplTasks() {
|
|
|
182
182
|
const closeDraft = useCallback(() => { setDraftOpen(false); resetDraft(); }, [resetDraft]);
|
|
183
183
|
const editProposal = useCallback((id: string, p: Partial<Proposal>) =>
|
|
184
184
|
setProposals((ps) => ps.map((x) => (x.id === id ? { ...x, ...p } : x))), []);
|
|
185
|
-
const decide = useCallback((id: string, status:
|
|
185
|
+
const decide = useCallback((id: string, status: ChangeStatus | null) =>
|
|
186
186
|
setDecisions((dec) => { const n = { ...dec }; if (status) n[id] = status; else delete n[id]; return n; }), []);
|
|
187
187
|
const acceptedCount = proposals.filter((p) => decisions[p.id] === "accepted").length;
|
|
188
188
|
const addAccepted = useCallback(() => {
|
|
@@ -259,15 +259,7 @@ export function TplTasks() {
|
|
|
259
259
|
|
|
260
260
|
{/* Capture — type to add; the submit button lives inside the field on the right. */}
|
|
261
261
|
<View style={styles.capture}>
|
|
262
|
-
<
|
|
263
|
-
<View style={styles.emptyRing} />
|
|
264
|
-
<View style={styles.captureField}>
|
|
265
|
-
<View style={{ flex: 1 }}>
|
|
266
|
-
<TextInputField value={draft} onChangeText={setDraft} onSubmitEditing={add} placeholder="Add a task…" style={styles.titleInput} accessibilityLabel="Add a task" />
|
|
267
|
-
</View>
|
|
268
|
-
{draft.trim() ? <Button title="Save" color="primary" onPress={add} /> : null}
|
|
269
|
-
</View>
|
|
270
|
-
</View>
|
|
262
|
+
<CaptureRow value={draft} onChangeText={setDraft} onSubmit={add} placeholder="Add a task…" accessibilityLabel="Add a task" />
|
|
271
263
|
</View>
|
|
272
264
|
|
|
273
265
|
<View>
|
|
@@ -294,7 +286,7 @@ export function TplTasks() {
|
|
|
294
286
|
<View style={styles.row}>
|
|
295
287
|
<CheckCircle checked={done} onChange={(on) => patch(t.id, { status: on ? "done" : "todo" })} accessibilityLabel={t.title} />
|
|
296
288
|
<View style={{ flex: 1 }}>
|
|
297
|
-
<InlineTextInput value={t.title} onSave={(v) => patch(t.id, { title: v })} struck={done} accessibilityLabel="Title" />
|
|
289
|
+
<InlineTextInput background="transparent" value={t.title} onSave={(v) => patch(t.id, { title: v })} struck={done} accessibilityLabel="Title" />
|
|
298
290
|
</View>
|
|
299
291
|
<Pressable
|
|
300
292
|
onPress={() => toggleExpand(t.id, !open)}
|
|
@@ -320,16 +312,16 @@ export function TplTasks() {
|
|
|
320
312
|
{open ? (
|
|
321
313
|
<View style={styles.detail}>
|
|
322
314
|
<Field label="Due">
|
|
323
|
-
<InlineDatePicker value={t.due} optionalTime onSave={(v) => patch(t.id, { due: v })} placeholder="Set a due date" accessibilityLabel="Due date" />
|
|
315
|
+
<InlineDatePicker background="transparent" value={t.due} optionalTime onSave={(v) => patch(t.id, { due: v })} placeholder="Set a due date" accessibilityLabel="Due date" />
|
|
324
316
|
</Field>
|
|
325
317
|
<Field label="Status">
|
|
326
|
-
<InlineSelect value={t.status} options={STATUS_OPTIONS} onSave={(st) => patch(t.id, { status: st })} renderSelected={(o) => <OptionBadge value={STATUS[o.value as Status]} variant="dot" />} renderOptionContent={(o) => <OptionBadge value={STATUS[o.value as Status]} variant="dot" />} accessibilityLabel="Status" />
|
|
318
|
+
<InlineSelect background="transparent" value={t.status} options={STATUS_OPTIONS} onSave={(st) => patch(t.id, { status: st })} renderSelected={(o) => <OptionBadge value={STATUS[o.value as Status]} variant="dot" />} renderOptionContent={(o) => <OptionBadge value={STATUS[o.value as Status]} variant="dot" />} accessibilityLabel="Status" />
|
|
327
319
|
</Field>
|
|
328
320
|
<Field label="Tags">
|
|
329
321
|
<Select multi searchable allowCustom value={t.tags.map((tag) => tag.value)} onValueChange={(next) => patch(t.id, { tags: next.map(tagOf) })} options={TAG_OPTIONS} renderSelected={renderTagBadge} renderOptionContent={renderTagBadge} style={{ borderColor: "transparent" }} placeholder="Add tags" accessibilityLabel="Tags" />
|
|
330
322
|
</Field>
|
|
331
323
|
<Field label="Note">
|
|
332
|
-
<InlineTextInput value={t.note} onSave={(v) => patch(t.id, { note: v })} placeholder="Add a note…" accessibilityLabel="Note" />
|
|
324
|
+
<InlineTextInput background="transparent" value={t.note} onSave={(v) => patch(t.id, { note: v })} placeholder="Add a note…" accessibilityLabel="Note" />
|
|
333
325
|
</Field>
|
|
334
326
|
<Field label="Files" top>
|
|
335
327
|
<FilesEditor files={t.files} itemSize={72} onAdd={(picked) => addFiles(t.id, picked)} onRemove={(id) => removeFile(t.id, id)} />
|
|
@@ -345,7 +337,10 @@ export function TplTasks() {
|
|
|
345
337
|
</View>
|
|
346
338
|
</ScrollView>
|
|
347
339
|
|
|
348
|
-
{/* Draft from notes — Composer → AgentRun → ChangeReview → commit.
|
|
340
|
+
{/* Draft from notes — Composer → AgentRun → ChangeReview → commit. The review
|
|
341
|
+
provider wraps the WHOLE dialog so the `Change`s (scroll area) and the
|
|
342
|
+
commit bar (`DialogFooter`) share one review context. */}
|
|
343
|
+
<ChangeReview>
|
|
349
344
|
<Dialog open={draftOpen} onOpenChange={(o) => { if (!o) closeDraft(); }} maxWidth={620}>
|
|
350
345
|
<DialogHeader><DialogHeaderTitle>Draft from notes</DialogHeaderTitle></DialogHeader>
|
|
351
346
|
<DialogScrollArea>
|
|
@@ -357,20 +352,29 @@ export function TplTasks() {
|
|
|
357
352
|
) : null}
|
|
358
353
|
{phase === "reading" ? <AgentRun items={runItems} state={revealed >= DRAFT_STEPS.length ? "done" : "streaming"} /> : null}
|
|
359
354
|
{phase === "review" ? (
|
|
360
|
-
<
|
|
361
|
-
title="Drafted tasks"
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
355
|
+
<View style={{ gap: 8 }}>
|
|
356
|
+
<ChangeReviewHeader title="Drafted tasks" />
|
|
357
|
+
<Text size="sm" color="muted">{`${proposals.length} from your notes — keep, edit, or drop each. A “Low” mark is worth a glance.`}</Text>
|
|
358
|
+
{proposals.map((p) => {
|
|
359
|
+
const status = decisions[p.id] ?? "pending";
|
|
360
|
+
const kept = status === "accepted";
|
|
361
|
+
return (
|
|
362
|
+
<Change
|
|
363
|
+
key={p.id}
|
|
364
|
+
id={p.id}
|
|
365
|
+
status={status}
|
|
366
|
+
onAccept={() => decide(p.id, "accepted")}
|
|
367
|
+
onReject={() => decide(p.id, "rejected")}
|
|
368
|
+
onUndo={() => decide(p.id, null)}
|
|
369
|
+
>
|
|
370
|
+
<TaskProposal proposal={p} onEdit={editProposal} />
|
|
371
|
+
<ChangeSummary>
|
|
372
|
+
<Text size="sm" numberOfLines={1} color={kept ? "default" : "muted"} style={kept ? undefined : { textDecorationLine: "line-through" }}>{p.title}</Text>
|
|
373
|
+
</ChangeSummary>
|
|
374
|
+
</Change>
|
|
375
|
+
);
|
|
376
|
+
})}
|
|
377
|
+
</View>
|
|
374
378
|
) : null}
|
|
375
379
|
{phase === "done" ? (
|
|
376
380
|
<CompletionState title={`${addedCount} ${addedCount === 1 ? "task" : "tasks"} added`} summary="They're in your list, grouped by due date." />
|
|
@@ -378,7 +382,7 @@ export function TplTasks() {
|
|
|
378
382
|
</DialogScrollArea>
|
|
379
383
|
{phase === "review" ? (
|
|
380
384
|
<DialogFooter>
|
|
381
|
-
<ChangeReviewActions
|
|
385
|
+
<ChangeReviewActions onApply={addAccepted} onDiscard={resetDraft} applyLabel={`Add ${acceptedCount} ${acceptedCount === 1 ? "task" : "tasks"}`} />
|
|
382
386
|
</DialogFooter>
|
|
383
387
|
) : phase === "done" ? (
|
|
384
388
|
<DialogFooter>
|
|
@@ -387,6 +391,7 @@ export function TplTasks() {
|
|
|
387
391
|
</DialogFooter>
|
|
388
392
|
) : null}
|
|
389
393
|
</Dialog>
|
|
394
|
+
</ChangeReview>
|
|
390
395
|
</>
|
|
391
396
|
);
|
|
392
397
|
}
|
|
@@ -396,12 +401,12 @@ function TaskProposal({ proposal, onEdit }: { proposal: Proposal; onEdit: (id: s
|
|
|
396
401
|
<View style={{ gap: 10 }}>
|
|
397
402
|
<View style={{ flexDirection: "row", alignItems: "flex-start", gap: 12 }}>
|
|
398
403
|
<View style={{ flex: 1 }}>
|
|
399
|
-
<InlineTextInput value={proposal.title} onSave={(v) => onEdit(proposal.id, { title: v })} accessibilityLabel="Task title" />
|
|
404
|
+
<InlineTextInput background="transparent" value={proposal.title} onSave={(v) => onEdit(proposal.id, { title: v })} accessibilityLabel="Task title" />
|
|
400
405
|
</View>
|
|
401
406
|
<View style={{ paddingTop: 10 }}><Confidence level={proposal.confidence} /></View>
|
|
402
407
|
</View>
|
|
403
408
|
<View style={{ width: 200 }}>
|
|
404
|
-
<InlineDatePicker value={proposal.due} onSave={(v) => onEdit(proposal.id, { due: v })} placeholder="No date" accessibilityLabel="Due date" />
|
|
409
|
+
<InlineDatePicker background="transparent" value={proposal.due} onSave={(v) => onEdit(proposal.id, { due: v })} placeholder="No date" accessibilityLabel="Due date" />
|
|
405
410
|
</View>
|
|
406
411
|
<View style={styles.source}><Text size="xs" color="muted">{proposal.source}</Text></View>
|
|
407
412
|
</View>
|
|
@@ -429,25 +434,20 @@ const styles = StyleSheet.create({
|
|
|
429
434
|
rowHover: { backgroundColor: colors.white },
|
|
430
435
|
// The expand affordance — its own button (meta + chevron), sibling to the ring
|
|
431
436
|
// and title so no interactive control nests inside another.
|
|
432
|
-
|
|
437
|
+
// FIXED width so every title cell ends at the same x — the meta content
|
|
438
|
+
// (clip count · tag dots · date) varies per row and right-aligns inside;
|
|
439
|
+
// without it the title editors read ragged/misaligned across rows.
|
|
440
|
+
expand: { flexDirection: "row", alignItems: "center", justifyContent: "flex-end", gap: 10, minHeight: 36, paddingHorizontal: 8, borderRadius: 8, width: 148 },
|
|
433
441
|
capture: {
|
|
434
442
|
borderBottomWidth: 1,
|
|
435
443
|
borderBottomColor: colors.zinc[100],
|
|
436
444
|
paddingBottom: 6,
|
|
437
445
|
},
|
|
438
|
-
|
|
439
|
-
// so the capture title lines up with the task titles below it.
|
|
440
|
-
titleInput: {
|
|
441
|
-
borderWidth: 0,
|
|
442
|
-
backgroundColor: "transparent",
|
|
443
|
-
paddingVertical: 0,
|
|
444
|
-
},
|
|
445
|
-
captureField: { flex: 1, flexDirection: "row", alignItems: "center", gap: 8, paddingRight: 4 },
|
|
446
|
+
|
|
446
447
|
sectionHead: { flexDirection: "row", alignItems: "center", gap: 8, paddingBottom: 6 },
|
|
447
448
|
sectionLabel: { letterSpacing: 0.3, textTransform: "uppercase" },
|
|
448
|
-
meta: { flexDirection: "row", alignItems: "center", gap: 10 },
|
|
449
|
+
meta: { flexDirection: "row", alignItems: "center", justifyContent: "flex-end", gap: 10, flex: 1 },
|
|
449
450
|
clip: { flexDirection: "row", alignItems: "center", gap: 2 },
|
|
450
|
-
emptyRing: { width: 20, height: 20, borderRadius: 10, borderWidth: 1.5, borderColor: colors.zinc[300], borderStyle: "dashed" },
|
|
451
451
|
dots: { flexDirection: "row", alignItems: "center", gap: 4 },
|
|
452
452
|
dot: { width: 8, height: 8, borderRadius: 999 },
|
|
453
453
|
// ring (20) + gap (12) + the title's 8 inset → labels line up with the title text
|
package/examples/tpl_tower.tsx
CHANGED
|
@@ -15,7 +15,7 @@ import { StatusGrid, StatusLegend } from "@lotics/ui/status_grid";
|
|
|
15
15
|
import { Timeline } from "@lotics/ui/timeline";
|
|
16
16
|
|
|
17
17
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
18
|
-
// Template ·
|
|
18
|
+
// Template · Status grid — continuous monitoring of hundreds of live
|
|
19
19
|
// units (machines, gates, chargers, sensors). Three bands answer the three
|
|
20
20
|
// ops questions in order: KPI strip + StatusGrid = "is everything OK right
|
|
21
21
|
// now?" (scan for red; the legend drills a state), exceptions rail =
|
|
@@ -200,7 +200,7 @@ export function TplTower() {
|
|
|
200
200
|
return (
|
|
201
201
|
<View key={site.key} style={{ gap: 8 }}>
|
|
202
202
|
<View style={{ flexDirection: "row", alignItems: "baseline", gap: 8 }}>
|
|
203
|
-
<Text size="xs" color="muted"
|
|
203
|
+
<Text size="xs" color="muted" weight="medium">{site.label}</Text>
|
|
204
204
|
<Text size="xs" color="muted" tabular>{siteUnits.length}</Text>
|
|
205
205
|
</View>
|
|
206
206
|
<StatusGrid
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lotics/ui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "9.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
"./tokens": "./src/tokens.ts",
|
|
@@ -30,6 +30,9 @@
|
|
|
30
30
|
"./rotatable_image": "./src/rotatable_image.tsx",
|
|
31
31
|
"./use_image_rotation": "./src/use_image_rotation.ts",
|
|
32
32
|
"./use_selection": "./src/use_selection.ts",
|
|
33
|
+
"./use_section_nav": "./src/use_section_nav.ts",
|
|
34
|
+
"./ledger": "./src/ledger.tsx",
|
|
35
|
+
"./inline_tag_select": "./src/inline_tag_select.tsx",
|
|
33
36
|
"./use_selection_mode": "./src/use_selection_mode.ts",
|
|
34
37
|
"./file_thumbnail_grid": "./src/file_thumbnail_grid.tsx",
|
|
35
38
|
"./uploading_thumbnail": "./src/uploading_thumbnail.tsx",
|
|
@@ -81,18 +84,11 @@
|
|
|
81
84
|
},
|
|
82
85
|
"./markdown.css": "./src/markdown.css",
|
|
83
86
|
"./confidence": "./src/confidence.tsx",
|
|
84
|
-
"./review_card": "./src/review_card.tsx",
|
|
85
87
|
"./change_review": "./src/change_review.tsx",
|
|
88
|
+
"./finding": "./src/finding.tsx",
|
|
86
89
|
"./clarify": "./src/clarify.tsx",
|
|
87
90
|
"./choice_list": "./src/choice_list.tsx",
|
|
88
91
|
"./sources": "./src/sources.tsx",
|
|
89
|
-
"./record_fields": "./src/record_fields.tsx",
|
|
90
|
-
"./spec_list": "./src/spec_list.tsx",
|
|
91
|
-
"./match_sides": "./src/match_sides.tsx",
|
|
92
|
-
"./finding": "./src/finding.tsx",
|
|
93
|
-
"./discrepancy": "./src/discrepancy.tsx",
|
|
94
|
-
"./triage_row": "./src/triage_row.tsx",
|
|
95
|
-
"./scored_option": "./src/scored_option.tsx",
|
|
96
92
|
"./icon": "./src/icon.tsx",
|
|
97
93
|
"./dynamic_icon": {
|
|
98
94
|
"react-native": "./src/dynamic_icon.tsx",
|
|
@@ -161,6 +157,10 @@
|
|
|
161
157
|
"./table": "./src/table.tsx",
|
|
162
158
|
"./data_grid": "./src/data_grid.tsx",
|
|
163
159
|
"./detail_row": "./src/detail_row.tsx",
|
|
160
|
+
"./record_summary": "./src/record_summary.tsx",
|
|
161
|
+
"./capture_row": "./src/capture_row.tsx",
|
|
162
|
+
"./checklist": "./src/checklist.tsx",
|
|
163
|
+
"./suggestion_chip": "./src/suggestion_chip.tsx",
|
|
164
164
|
"./danger_zone": "./src/danger_zone.tsx",
|
|
165
165
|
"./scan_field": "./src/scan_field.tsx",
|
|
166
166
|
"./completion_state": "./src/completion_state.tsx",
|
package/src/agent_run.tsx
CHANGED
|
@@ -147,7 +147,7 @@ const INK = colors.zinc[700];
|
|
|
147
147
|
* fires (not a growing stack of dots); once prose resumes the group settles into
|
|
148
148
|
* one row — "{final action} · {n} steps" — that EXPANDS on press to the steps in
|
|
149
149
|
* between. Text segments render as prose, the last one carrying a live caret. The
|
|
150
|
-
* run always ends on the agent's text. Pair with `Composer` + `
|
|
150
|
+
* run always ends on the agent's text. Pair with `Composer` + `ChangeReview`.
|
|
151
151
|
*/
|
|
152
152
|
export function AgentRun(props: AgentRunProps) {
|
|
153
153
|
const { items, labelForTool, stepsLabel = (n) => `${n} steps`, accessibilityLabel } = props;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { StyleSheet, View } from "react-native";
|
|
2
|
+
import { Button } from "./button";
|
|
3
|
+
import { colors } from "./colors";
|
|
4
|
+
import { useLoticsLocale } from "./locale";
|
|
5
|
+
import { TextInputField } from "./text_input_field";
|
|
6
|
+
|
|
7
|
+
export interface CaptureRowProps {
|
|
8
|
+
/** The draft text — controlled by the host list. */
|
|
9
|
+
value: string;
|
|
10
|
+
onChangeText: (next: string) => void;
|
|
11
|
+
/** Commit the draft (Enter, or the Save button that appears on type). The
|
|
12
|
+
* host trims/validates and clears `value`. */
|
|
13
|
+
onSubmit: () => void;
|
|
14
|
+
/** The invitation ("Add a task…"). */
|
|
15
|
+
placeholder?: string;
|
|
16
|
+
/** Save button title. Defaults to the locale's inline Save. */
|
|
17
|
+
saveLabel?: string;
|
|
18
|
+
accessibilityLabel: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The add-an-item row that closes an editable list: a dashed empty ring where
|
|
23
|
+
* the item's leading control will be, a borderless input on the same inset as
|
|
24
|
+
* the item titles above it, and a primary Save that appears once there's text.
|
|
25
|
+
* Enter commits too. One shape wherever a list grows in place — task
|
|
26
|
+
* checklists, simple item lists — so the affordance reads the same everywhere.
|
|
27
|
+
*/
|
|
28
|
+
export function CaptureRow(props: CaptureRowProps) {
|
|
29
|
+
const { value, onChangeText, onSubmit, placeholder, saveLabel, accessibilityLabel } = props;
|
|
30
|
+
const labels = useLoticsLocale().inline;
|
|
31
|
+
return (
|
|
32
|
+
<View style={styles.row}>
|
|
33
|
+
<View style={styles.emptyRing} />
|
|
34
|
+
<View style={styles.field}>
|
|
35
|
+
<View style={styles.grow}>
|
|
36
|
+
<TextInputField
|
|
37
|
+
value={value}
|
|
38
|
+
onChangeText={onChangeText}
|
|
39
|
+
onSubmitEditing={onSubmit}
|
|
40
|
+
placeholder={placeholder}
|
|
41
|
+
style={styles.input}
|
|
42
|
+
accessibilityLabel={accessibilityLabel}
|
|
43
|
+
/>
|
|
44
|
+
</View>
|
|
45
|
+
{value.trim() !== "" ? <Button title={saveLabel ?? labels.save} color="primary" onPress={onSubmit} /> : null}
|
|
46
|
+
</View>
|
|
47
|
+
</View>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const styles = StyleSheet.create({
|
|
52
|
+
row: { flexDirection: "row", alignItems: "center", gap: 12, minHeight: 32 },
|
|
53
|
+
emptyRing: { width: 20, height: 20, borderRadius: 10, borderWidth: 1.5, borderColor: colors.zinc[300], borderStyle: "dashed" },
|
|
54
|
+
field: { flex: 1, flexDirection: "row", alignItems: "center", gap: 8 },
|
|
55
|
+
grow: { flex: 1 },
|
|
56
|
+
// Borderless, matching an inline title's resting inset so the invitation
|
|
57
|
+
// lines up with the item titles above.
|
|
58
|
+
input: { borderWidth: 0, backgroundColor: "transparent", paddingVertical: 0 },
|
|
59
|
+
});
|