@lotics/ui 7.16.0 → 7.18.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 +6 -3
- package/package.json +1 -1
- package/src/file_gallery_modal.tsx +3 -2
- package/src/file_preview.tsx +3 -2
- package/src/file_preview.web.tsx +3 -2
- package/src/file_preview_types.ts +5 -34
- package/src/file_rows.tsx +3 -2
- package/src/finding.tsx +13 -7
- package/src/locale.tsx +46 -0
package/AGENTS.md
CHANGED
|
@@ -21,9 +21,11 @@ typeahead, async search, virtualization, and a11y the primitive already ships.
|
|
|
21
21
|
app stays English. Wired so far: `Pagination`, `SortHeader`/`Table.sortLabels`, `OptionList`
|
|
22
22
|
(+ `Select`/`Combobox` built on it), `FilterChip`, `Drawer`, `Confidence`, `RemainderMeter`,
|
|
23
23
|
`ChangeReview`/`ChangeReviewActions`, `DateRangeFilterField`/`DateFilter` (presets + footer +
|
|
24
|
-
time-segment a11y, one `dateRange` slice),
|
|
24
|
+
time-segment a11y, one `dateRange` slice), `FormField` (the `optional` marker), and
|
|
25
|
+
`FileRows`/`FileGalleryModal`/`FilePreview` (one `gallery` slice: the ⋯ menu incl. remove+confirm,
|
|
26
|
+
the gallery chrome, and the preview captions). Adding a string to a wired component's `*Labels` forces
|
|
25
27
|
both packs in `locale.tsx` to fill it (compile error) — that's how the kit avoids a silent English
|
|
26
|
-
leak. Not yet on the provider (pass `labels` for now): calendar/gantt, the
|
|
28
|
+
leak. Not yet on the provider (pass `labels` for now): calendar/gantt, the comment labels.
|
|
27
29
|
|
|
28
30
|
---
|
|
29
31
|
|
|
@@ -532,7 +534,8 @@ controls — while **status/severity/diffs use functional colour** the way the r
|
|
|
532
534
|
- **Confidence** — a 3-tick meter + the full phrase ("High confidence" / "Medium" / "Low"), emerald /
|
|
533
535
|
amber / zinc by level; `labels` to translate (one phrase per level).
|
|
534
536
|
- **Finding severity** — a coloured dot `Badge` + metric: red critical, amber warning, blue note,
|
|
535
|
-
emerald on-track (+ stacking order, most severe first).
|
|
537
|
+
emerald on-track (+ stacking order, most severe first). The badge words localize through
|
|
538
|
+
`LoticsLocaleProvider` (`locale.finding`, override per-instance via `labels`) — never hardcode them.
|
|
536
539
|
- **Stepper / AgentRun nodes** — progress dots on a spine: `current` a **pulsing accent ring** (white
|
|
537
540
|
centre), `done` a filled accent dot + **white check**, `upcoming` a faint **grey** ring, the terminal
|
|
538
541
|
`complete` a **blackish ring + black check**, `warning` **amber**. Default accent is neutral ink
|
package/package.json
CHANGED
|
@@ -18,7 +18,8 @@ import type { DisplayFile } from "./file_thumbnail";
|
|
|
18
18
|
import { FilePreview } from "./file_preview";
|
|
19
19
|
import { isImageMimeType } from "./mime";
|
|
20
20
|
import { useImageRotation, type ImageRotation } from "./use_image_rotation";
|
|
21
|
-
import {
|
|
21
|
+
import { type GalleryLabels } from "./file_preview_types";
|
|
22
|
+
import { useLoticsLocale } from "./locale";
|
|
22
23
|
import { downloadFileFromUrl } from "./download";
|
|
23
24
|
import { ActionMenu, type ActionMenuItem } from "./action_menu";
|
|
24
25
|
import { useScreenSize } from "./use_screen_size";
|
|
@@ -153,7 +154,7 @@ export function FileGalleryModal(props: FileGalleryModalProps) {
|
|
|
153
154
|
const file = files[activeIndex];
|
|
154
155
|
if (!file) return null;
|
|
155
156
|
|
|
156
|
-
const l =
|
|
157
|
+
const l = { ...useLoticsLocale().gallery, ...labels };
|
|
157
158
|
const isImage = isImageMimeType(file.mimeType);
|
|
158
159
|
const total = files.length;
|
|
159
160
|
const rotated = rot.rotationFor(file.id) !== 0;
|
package/src/file_preview.tsx
CHANGED
|
@@ -3,7 +3,8 @@ import { Text } from "./text";
|
|
|
3
3
|
import { DocumentCard } from "./file_thumbnail";
|
|
4
4
|
import { isImageMimeType } from "./mime";
|
|
5
5
|
import { RotatableImage } from "./rotatable_image";
|
|
6
|
-
import { type FilePreviewProps
|
|
6
|
+
import { type FilePreviewProps } from "./file_preview_types";
|
|
7
|
+
import { useLoticsLocale } from "./locale";
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Native (mobile) fallback: images render full-bleed; everything else shows a
|
|
@@ -11,7 +12,7 @@ import { type FilePreviewProps, resolveLabels } from "./file_preview_types";
|
|
|
11
12
|
* the `.web.tsx` variant carries it; native consumers download instead.
|
|
12
13
|
*/
|
|
13
14
|
export function FilePreview({ file, labels, rotation }: FilePreviewProps) {
|
|
14
|
-
const l =
|
|
15
|
+
const l = { ...useLoticsLocale().gallery, ...labels };
|
|
15
16
|
if (isImageMimeType(file.mimeType)) {
|
|
16
17
|
return <RotatableImage uri={file.url} alt={file.filename} rotation={rotation} />;
|
|
17
18
|
}
|
package/src/file_preview.web.tsx
CHANGED
|
@@ -13,7 +13,8 @@ import {
|
|
|
13
13
|
isAudioMimeType,
|
|
14
14
|
isPreviewableMimeType,
|
|
15
15
|
} from "./mime";
|
|
16
|
-
import { type FilePreviewProps,
|
|
16
|
+
import { type FilePreviewProps, type PreviewLabels } from "./file_preview_types";
|
|
17
|
+
import { useLoticsLocale } from "./locale";
|
|
17
18
|
import { SpreadsheetView } from "./spreadsheet_view";
|
|
18
19
|
import { RotatableImage } from "./rotatable_image";
|
|
19
20
|
import { downloadFileFromUrl } from "./download";
|
|
@@ -36,7 +37,7 @@ import { colors } from "./colors";
|
|
|
36
37
|
* (purity test). Lifted from the frontend gallery so both share one renderer.
|
|
37
38
|
*/
|
|
38
39
|
export function FilePreview({ file, labels, onError, rotation, credentials }: FilePreviewProps) {
|
|
39
|
-
const l =
|
|
40
|
+
const l = { ...useLoticsLocale().gallery, ...labels };
|
|
40
41
|
|
|
41
42
|
if (isImageMimeType(file.mimeType)) {
|
|
42
43
|
return (
|
|
@@ -2,8 +2,9 @@ import type { DisplayFile } from "./file_thumbnail";
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Caption/error strings shown inside the preview. `@lotics/ui` is i18n-free
|
|
5
|
-
* (purity test forbids lingui)
|
|
6
|
-
*
|
|
5
|
+
* (purity test forbids lingui): the strings resolve from the `LoticsLocale`
|
|
6
|
+
* `gallery` slice (`LoticsLocaleProvider`, default English) with a per-instance
|
|
7
|
+
* `labels` prop as the one-off override.
|
|
7
8
|
*/
|
|
8
9
|
export interface PreviewLabels {
|
|
9
10
|
notAvailable: string;
|
|
@@ -12,13 +13,6 @@ export interface PreviewLabels {
|
|
|
12
13
|
passwordProtected: string;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
export const defaultPreviewLabels: PreviewLabels = {
|
|
16
|
-
notAvailable: "Preview not available",
|
|
17
|
-
loadFailed: "Failed to load preview",
|
|
18
|
-
download: "Download file",
|
|
19
|
-
passwordProtected: "This file is password-protected and cannot be previewed",
|
|
20
|
-
};
|
|
21
|
-
|
|
22
16
|
export interface FilePreviewProps {
|
|
23
17
|
file: DisplayFile;
|
|
24
18
|
/** Override any subset of the English default labels. */
|
|
@@ -40,14 +34,10 @@ export interface FilePreviewProps {
|
|
|
40
34
|
credentials?: RequestCredentials;
|
|
41
35
|
}
|
|
42
36
|
|
|
43
|
-
export function resolveLabels(labels: Partial<PreviewLabels> | undefined): PreviewLabels {
|
|
44
|
-
return labels ? { ...defaultPreviewLabels, ...labels } : defaultPreviewLabels;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
37
|
/**
|
|
48
38
|
* Strings for the full-screen `FileGalleryModal` chrome (toolbar + nav), on top
|
|
49
|
-
* of the inner `PreviewLabels`. Same
|
|
50
|
-
*
|
|
39
|
+
* of the inner `PreviewLabels`. Same contract: the `LoticsLocale` `gallery`
|
|
40
|
+
* slice supplies them; a per-instance `labels` prop overrides.
|
|
51
41
|
*/
|
|
52
42
|
export interface GalleryLabels extends PreviewLabels {
|
|
53
43
|
close: string;
|
|
@@ -65,22 +55,3 @@ export interface GalleryLabels extends PreviewLabels {
|
|
|
65
55
|
saveRotation: string;
|
|
66
56
|
}
|
|
67
57
|
|
|
68
|
-
export const defaultGalleryLabels: GalleryLabels = {
|
|
69
|
-
...defaultPreviewLabels,
|
|
70
|
-
close: "Close",
|
|
71
|
-
previous: "Previous",
|
|
72
|
-
next: "Next",
|
|
73
|
-
actions: "Actions",
|
|
74
|
-
openExternal: "Open in new tab",
|
|
75
|
-
remove: "Remove",
|
|
76
|
-
removeConfirmTitle: "Remove file",
|
|
77
|
-
removeConfirmMessage: (filename) => `Remove "${filename}"?`,
|
|
78
|
-
cancel: "Cancel",
|
|
79
|
-
rotateLeft: "Rotate left",
|
|
80
|
-
rotateRight: "Rotate right",
|
|
81
|
-
saveRotation: "Save rotation",
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
export function resolveGalleryLabels(labels: Partial<GalleryLabels> | undefined): GalleryLabels {
|
|
85
|
-
return labels ? { ...defaultGalleryLabels, ...labels } : defaultGalleryLabels;
|
|
86
|
-
}
|
package/src/file_rows.tsx
CHANGED
|
@@ -12,7 +12,8 @@ import { ActionMenu, type ActionMenuItem } from "./action_menu";
|
|
|
12
12
|
import { FileGalleryModal } from "./file_gallery_modal";
|
|
13
13
|
import { resolveMime } from "./file_badge";
|
|
14
14
|
import { downloadFileFromUrl } from "./download";
|
|
15
|
-
import {
|
|
15
|
+
import { type GalleryLabels } from "./file_preview_types";
|
|
16
|
+
import { useLoticsLocale } from "./locale";
|
|
16
17
|
import type { DisplayFile } from "./file_thumbnail";
|
|
17
18
|
|
|
18
19
|
export interface FileRowsProps {
|
|
@@ -47,7 +48,7 @@ export interface FileRowsProps {
|
|
|
47
48
|
*/
|
|
48
49
|
export function FileRows({ files, meta, onRemove, onOpenExternal, onDownload, onError, labels, gap = 8, credentials }: FileRowsProps) {
|
|
49
50
|
const [activeIndex, setActiveIndex] = useState<number | null>(null);
|
|
50
|
-
const l =
|
|
51
|
+
const l = { ...useLoticsLocale().gallery, ...labels };
|
|
51
52
|
|
|
52
53
|
if (files.length === 0) return null;
|
|
53
54
|
|
package/src/finding.tsx
CHANGED
|
@@ -4,11 +4,14 @@ import { Text } from "./text";
|
|
|
4
4
|
import { Badge } from "./badge";
|
|
5
5
|
import { Button } from "./button";
|
|
6
6
|
import { Sources, type SourceRef } from "./sources";
|
|
7
|
+
import { useLoticsLocale } from "./locale";
|
|
7
8
|
|
|
8
9
|
export type FindingSeverity = "critical" | "warning" | "info" | "positive";
|
|
9
10
|
|
|
10
11
|
export interface FindingProps {
|
|
11
12
|
severity?: FindingSeverity;
|
|
13
|
+
/** Translated severity words. Defaults resolve through the locale ("Critical" …). */
|
|
14
|
+
labels?: Partial<FindingLabels>;
|
|
12
15
|
/** The headline — what the agent found. */
|
|
13
16
|
title: string;
|
|
14
17
|
/** One or two lines explaining it. */
|
|
@@ -28,12 +31,13 @@ export interface FindingProps {
|
|
|
28
31
|
|
|
29
32
|
// Severity reads through a coloured dot badge + the order it's stacked in (most
|
|
30
33
|
// severe first): red critical, amber warning, blue note, emerald on-track. The
|
|
31
|
-
// metric takes the same colour.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
// metric takes the same colour. Words resolve prop → locale → English default.
|
|
35
|
+
export type FindingLabels = Record<FindingSeverity, string>;
|
|
36
|
+
const SEV_COLOR: Record<FindingSeverity, ColorName> = {
|
|
37
|
+
critical: "red",
|
|
38
|
+
warning: "amber",
|
|
39
|
+
info: "blue",
|
|
40
|
+
positive: "emerald",
|
|
37
41
|
};
|
|
38
42
|
|
|
39
43
|
/**
|
|
@@ -45,7 +49,9 @@ const SEV: Record<FindingSeverity, { word: string; color: ColorName }> = {
|
|
|
45
49
|
* carries its own next action.
|
|
46
50
|
*/
|
|
47
51
|
export function Finding(props: FindingProps) {
|
|
48
|
-
const
|
|
52
|
+
const severity = props.severity ?? "info";
|
|
53
|
+
const words = { ...useLoticsLocale().finding, ...props.labels };
|
|
54
|
+
const sev = { word: words[severity], color: SEV_COLOR[severity] };
|
|
49
55
|
return (
|
|
50
56
|
<View style={styles.card}>
|
|
51
57
|
<View style={styles.header}>
|
package/src/locale.tsx
CHANGED
|
@@ -2,8 +2,10 @@ import { createContext, useContext, type ReactNode } from "react";
|
|
|
2
2
|
import { type PaginationLabels } from "./pagination";
|
|
3
3
|
import { type SortHeaderLabels } from "./sort_header";
|
|
4
4
|
import { type ConfidenceLabels } from "./confidence";
|
|
5
|
+
import { type FindingLabels } from "./finding";
|
|
5
6
|
import { type RemainderMeterLabels } from "./remainder_meter";
|
|
6
7
|
import { type DateRangeFilterFieldLabels } from "./date_range_filter_field";
|
|
8
|
+
import { type GalleryLabels } from "./file_preview_types";
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* The kit's localizable strings, one slice per string-bearing component. A
|
|
@@ -36,6 +38,8 @@ export interface LoticsLocale {
|
|
|
36
38
|
drawer: { previous: string; next: string; close: string };
|
|
37
39
|
/** `Confidence`: the full level phrase ("High confidence" …). */
|
|
38
40
|
confidence: ConfidenceLabels;
|
|
41
|
+
/** `Finding`: the severity badge word ("Critical" …). */
|
|
42
|
+
finding: FindingLabels;
|
|
39
43
|
/** `RemainderMeter`: the applied / remaining / over / exact captions. */
|
|
40
44
|
remainderMeter: Required<RemainderMeterLabels>;
|
|
41
45
|
/** `ChangeReview` (+ `ChangeReviewActions`): the review-card chrome — the
|
|
@@ -58,6 +62,10 @@ export interface LoticsLocale {
|
|
|
58
62
|
* from/to, the footer Clear/Done, the trigger placeholder, and the time-field
|
|
59
63
|
* segment a11y names. */
|
|
60
64
|
dateRange: DateRangeFilterFieldLabels;
|
|
65
|
+
/** `FileRows` / `FileGalleryModal` / `FilePreview`: the per-row ⋯ menu
|
|
66
|
+
* (open/download/remove + the confirm), the gallery chrome (close, prev/next,
|
|
67
|
+
* rotate), and the preview captions (not-available / load-failed / password). */
|
|
68
|
+
gallery: GalleryLabels;
|
|
61
69
|
}
|
|
62
70
|
|
|
63
71
|
/** The platform default — English. Every component's hardcoded default lives
|
|
@@ -81,6 +89,7 @@ export const en: LoticsLocale = {
|
|
|
81
89
|
formField: { optional: "Optional" },
|
|
82
90
|
drawer: { previous: "Previous record", next: "Next record", close: "Close" },
|
|
83
91
|
confidence: { high: "High confidence", medium: "Medium confidence", low: "Low confidence" },
|
|
92
|
+
finding: { critical: "Critical", warning: "Warning", info: "Note", positive: "On track" },
|
|
84
93
|
remainderMeter: {
|
|
85
94
|
applied: (allocated, total) => `${allocated} of ${total} applied`,
|
|
86
95
|
remaining: (remainder) => `${remainder} unapplied`,
|
|
@@ -108,6 +117,24 @@ export const en: LoticsLocale = {
|
|
|
108
117
|
selectDateRange: "Select date range", selectDate: "Select date",
|
|
109
118
|
clear: "Clear", done: "Done", placeholder: "All time",
|
|
110
119
|
},
|
|
120
|
+
gallery: {
|
|
121
|
+
close: "Close",
|
|
122
|
+
previous: "Previous",
|
|
123
|
+
next: "Next",
|
|
124
|
+
actions: "Actions",
|
|
125
|
+
openExternal: "Open in new tab",
|
|
126
|
+
remove: "Remove",
|
|
127
|
+
removeConfirmTitle: "Remove file",
|
|
128
|
+
removeConfirmMessage: (filename) => `Remove "${filename}"?`,
|
|
129
|
+
cancel: "Cancel",
|
|
130
|
+
rotateLeft: "Rotate left",
|
|
131
|
+
rotateRight: "Rotate right",
|
|
132
|
+
saveRotation: "Save rotation",
|
|
133
|
+
notAvailable: "Preview not available",
|
|
134
|
+
loadFailed: "Failed to load preview",
|
|
135
|
+
download: "Download file",
|
|
136
|
+
passwordProtected: "This file is password-protected and cannot be previewed",
|
|
137
|
+
},
|
|
111
138
|
};
|
|
112
139
|
|
|
113
140
|
/** Vietnamese. Maintained once here so every app (and the frontend) shares one
|
|
@@ -131,6 +158,7 @@ export const vi: LoticsLocale = {
|
|
|
131
158
|
formField: { optional: "Tùy chọn" },
|
|
132
159
|
drawer: { previous: "Bản ghi trước", next: "Bản ghi sau", close: "Đóng" },
|
|
133
160
|
confidence: { high: "Độ tin cậy cao", medium: "Độ tin cậy trung bình", low: "Độ tin cậy thấp" },
|
|
161
|
+
finding: { critical: "Nghiêm trọng", warning: "Cảnh báo", info: "Ghi chú", positive: "Đúng tiến độ" },
|
|
134
162
|
remainderMeter: {
|
|
135
163
|
applied: (allocated, total) => `Đã phân bổ ${allocated}/${total}`,
|
|
136
164
|
remaining: (remainder) => `Còn ${remainder}`,
|
|
@@ -158,6 +186,24 @@ export const vi: LoticsLocale = {
|
|
|
158
186
|
selectDateRange: "Chọn khoảng ngày", selectDate: "Chọn ngày",
|
|
159
187
|
clear: "Xóa", done: "Xong", placeholder: "Tất cả thời gian",
|
|
160
188
|
},
|
|
189
|
+
gallery: {
|
|
190
|
+
close: "Đóng",
|
|
191
|
+
previous: "Trước",
|
|
192
|
+
next: "Sau",
|
|
193
|
+
actions: "Thao tác",
|
|
194
|
+
openExternal: "Mở trong thẻ mới",
|
|
195
|
+
remove: "Gỡ tệp",
|
|
196
|
+
removeConfirmTitle: "Gỡ tệp",
|
|
197
|
+
removeConfirmMessage: (filename) => `Gỡ "${filename}"?`,
|
|
198
|
+
cancel: "Hủy",
|
|
199
|
+
rotateLeft: "Xoay trái",
|
|
200
|
+
rotateRight: "Xoay phải",
|
|
201
|
+
saveRotation: "Lưu ảnh đã xoay",
|
|
202
|
+
notAvailable: "Không xem trước được định dạng này",
|
|
203
|
+
loadFailed: "Không tải được bản xem trước",
|
|
204
|
+
download: "Tải xuống",
|
|
205
|
+
passwordProtected: "Tệp có mật khẩu — không xem trước được",
|
|
206
|
+
},
|
|
161
207
|
};
|
|
162
208
|
|
|
163
209
|
const LoticsLocaleContext = createContext<LoticsLocale>(en);
|