@flamingo-stack/openframe-frontend-core 0.0.740 → 0.0.742
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/build/export-map.json +1 -1
- package/dist/{chunk-BV5WZVOP.cjs → chunk-32Q7K5OP.cjs} +254 -254
- package/dist/{chunk-ZLTCQKTD.cjs → chunk-3LW43GIJ.cjs} +6 -6
- package/dist/{chunk-VA7OMDZO.cjs → chunk-4FWURR3W.cjs} +50 -50
- package/dist/{chunk-EKGL5K5K.cjs → chunk-4LDU32OB.cjs} +17 -17
- package/dist/{chunk-VC4IV3AY.cjs → chunk-4PKORVOX.cjs} +2804 -954
- package/dist/{chunk-3ZIQOYNX.cjs → chunk-54EA4RLX.cjs} +8 -8
- package/dist/{chunk-YXKB5WRE.cjs → chunk-5MH6BRPY.cjs} +36 -36
- package/dist/{chunk-PQ2M7P2U.cjs → chunk-5TCIRWHL.cjs} +9 -9
- package/dist/{chunk-WFPCCBOM.cjs → chunk-7AM3AFM4.cjs} +10 -10
- package/dist/{chunk-7ZFCR7UY.cjs → chunk-ABLUMGCI.cjs} +3 -3
- package/dist/{chunk-PX7FY655.cjs → chunk-ASMNNSUQ.cjs} +3 -3
- package/dist/{chunk-HF7Z2E4W.cjs → chunk-BI3XBCQX.cjs} +2 -2
- package/dist/{chunk-SA7LDEVJ.cjs → chunk-CSJO7ZGN.cjs} +3 -3
- package/dist/{chunk-VOX4E6AX.cjs → chunk-DGFDBPO7.cjs} +7 -5
- package/dist/{chunk-KHXZFLIN.cjs → chunk-DKOLQPE7.cjs} +8 -2
- package/dist/{chunk-BDA5F6RM.cjs → chunk-DNFSMVXJ.cjs} +16 -16
- package/dist/{chunk-B3XBJCOD.cjs → chunk-H3UA7UY5.cjs} +7 -7
- package/dist/{chunk-2Y5Y4FLM.cjs → chunk-HDBY2URA.cjs} +64 -64
- package/dist/{chunk-UW3YXDC4.cjs → chunk-HIRVNBX5.cjs} +20 -20
- package/dist/{chunk-LLAC2UZ3.cjs → chunk-ISJXKKYW.cjs} +28 -28
- package/dist/{chunk-FICJS5OA.cjs → chunk-KTPR66EE.cjs} +4 -4
- package/dist/{chunk-7UIQPMBJ.cjs → chunk-L34TKFTS.cjs} +21 -164
- package/dist/{chunk-AAFW4STC.cjs → chunk-LU7TD6QS.cjs} +5 -5
- package/dist/{chunk-3HPB4H4C.cjs → chunk-NTGIQP7O.cjs} +7 -7
- package/dist/{chunk-WBANGORJ.cjs → chunk-OBODY7F3.cjs} +3 -3
- package/dist/{chunk-GABDE3FM.cjs → chunk-OWGLRE74.cjs} +19 -19
- package/dist/{chunk-CWRVTQUO.cjs → chunk-PS6T65KA.cjs} +101 -101
- package/dist/{chunk-IFS4AH77.cjs → chunk-QYLIHZKZ.cjs} +10 -10
- package/dist/{chunk-GVTDVS5U.cjs → chunk-R34JD6NP.cjs} +3 -3
- package/dist/{chunk-VU4CKUJW.cjs → chunk-R6SCSV2I.cjs} +103 -103
- package/dist/{chunk-II4QMH26.cjs → chunk-STEJZPIO.cjs} +3 -3
- package/dist/{chunk-4RLGLB45.cjs → chunk-T433HSL6.cjs} +47 -47
- package/dist/{chunk-DSVNFRY5.cjs → chunk-U2O5YQRP.cjs} +38 -38
- package/dist/{chunk-O67V3HJR.cjs → chunk-URVIPSAJ.cjs} +24 -24
- package/dist/{chunk-HYBMADYO.cjs → chunk-UWHCRIQQ.cjs} +22 -22
- package/dist/{chunk-GSWQZX23.cjs → chunk-UY6A6TEY.cjs} +2 -2
- package/dist/{chunk-D5KEWWLW.cjs → chunk-UZLRSFGG.cjs} +32 -32
- package/dist/{chunk-DUVOQHQI.cjs → chunk-W2RM764D.cjs} +7 -7
- package/dist/{chunk-NTJE6SM6.cjs → chunk-WI2OTGKC.cjs} +2 -2
- package/dist/{chunk-SFY4UYPY.cjs → chunk-WIHHA54J.cjs} +44 -44
- package/dist/{chunk-YRE3O65E.cjs → chunk-XAA5EZ5T.cjs} +12 -12
- package/dist/{chunk-KLI4OSRA.cjs → chunk-YQGDXH3M.cjs} +2 -2
- package/dist/{chunk-SWR22DOZ.cjs → chunk-Z2DQYKZA.cjs} +89 -89
- package/dist/{chunk-5SVOGLNK.cjs → chunk-ZIFYSLIM.cjs} +6 -6
- package/dist/{cloud-tenants-PVXDI5MU.cjs → cloud-tenants-6ICAL3HO.cjs} +34 -34
- package/dist/components/case-studies/index.cjs +37 -37
- package/dist/components/chat/index.cjs +17 -17
- package/dist/components/contact/index.cjs +25 -25
- package/dist/components/docs/index.cjs +28 -28
- package/dist/components/embeds/index.cjs +25 -25
- package/dist/components/faq/index.cjs +26 -26
- package/dist/components/features/capability-explorer.d.ts +9 -2
- package/dist/components/features/capability-explorer.d.ts.map +1 -1
- package/dist/components/features/capability-explorer.js +113 -6
- package/dist/components/features/index.cjs +162 -24
- package/dist/components/features/index.d.ts +1 -0
- package/dist/components/features/index.d.ts.map +1 -1
- package/dist/components/features/index.js +1 -0
- package/dist/components/features/scripts/execution-cells.d.ts +65 -0
- package/dist/components/features/scripts/execution-cells.d.ts.map +1 -0
- package/dist/components/features/scripts/execution-cells.js +75 -0
- package/dist/components/features/scripts/execution-status.d.ts +48 -0
- package/dist/components/features/scripts/execution-status.d.ts.map +1 -0
- package/dist/components/features/scripts/execution-status.js +15 -0
- package/dist/components/features/scripts/index.d.ts +20 -0
- package/dist/components/features/scripts/index.d.ts.map +1 -0
- package/dist/components/features/scripts/index.js +59 -0
- package/dist/components/features/scripts/schedule-form/index.d.ts +18 -0
- package/dist/components/features/scripts/schedule-form/index.d.ts.map +1 -0
- package/dist/components/features/scripts/schedule-form/index.js +84 -0
- package/dist/components/features/scripts/schedule-form/schedule-enums.d.ts +24 -0
- package/dist/components/features/scripts/schedule-form/schedule-enums.d.ts.map +1 -0
- package/dist/components/features/scripts/schedule-form/schedule-enums.js +18 -0
- package/dist/components/features/scripts/schedule-form/schedule-form-fields.d.ts +57 -0
- package/dist/components/features/scripts/schedule-form/schedule-form-fields.d.ts.map +1 -0
- package/dist/components/features/scripts/schedule-form/schedule-form-fields.js +169 -0
- package/dist/components/features/scripts/schedule-form/schedule-form-types.d.ts +138 -0
- package/dist/components/features/scripts/schedule-form/schedule-form-types.d.ts.map +1 -0
- package/dist/components/features/scripts/schedule-form/schedule-form-types.js +72 -0
- package/dist/components/features/scripts/schedule-form/schedule-interval-input.d.ts +54 -0
- package/dist/components/features/scripts/schedule-form/schedule-interval-input.d.ts.map +1 -0
- package/dist/components/features/scripts/schedule-form/schedule-interval-input.js +60 -0
- package/dist/components/features/scripts/schedule-form/schedule-offline-fields.d.ts +29 -0
- package/dist/components/features/scripts/schedule-form/schedule-offline-fields.d.ts.map +1 -0
- package/dist/components/features/scripts/schedule-form/schedule-offline-fields.js +124 -0
- package/dist/components/features/scripts/schedule-form/schedule-script-picker-card.d.ts +52 -0
- package/dist/components/features/scripts/schedule-form/schedule-script-picker-card.d.ts.map +1 -0
- package/dist/components/features/scripts/schedule-form/schedule-script-picker-card.js +244 -0
- package/dist/components/features/scripts/schedule-form/schedule-script-search-field.d.ts +35 -0
- package/dist/components/features/scripts/schedule-form/schedule-script-search-field.d.ts.map +1 -0
- package/dist/components/features/scripts/schedule-form/schedule-script-search-field.js +58 -0
- package/dist/components/features/scripts/schedule-form/schedule-scripts-field.d.ts +25 -0
- package/dist/components/features/scripts/schedule-form/schedule-scripts-field.d.ts.map +1 -0
- package/dist/components/features/scripts/schedule-form/schedule-scripts-field.js +66 -0
- package/dist/components/features/scripts/schedule-form/schedule-timing-fields.d.ts +77 -0
- package/dist/components/features/scripts/schedule-form/schedule-timing-fields.d.ts.map +1 -0
- package/dist/components/features/scripts/schedule-form/schedule-timing-fields.js +262 -0
- package/dist/components/features/scripts/schedule-form/schedule-timing.d.ts +288 -0
- package/dist/components/features/scripts/schedule-form/schedule-timing.d.ts.map +1 -0
- package/dist/components/features/scripts/schedule-form/schedule-timing.js +165 -0
- package/dist/components/features/scripts/script-cells.d.ts +11 -0
- package/dist/components/features/scripts/script-cells.d.ts.map +1 -0
- package/dist/components/features/scripts/script-cells.js +19 -0
- package/dist/components/features/scripts/script-executions-table.d.ts +58 -0
- package/dist/components/features/scripts/script-executions-table.d.ts.map +1 -0
- package/dist/components/features/scripts/script-executions-table.js +88 -0
- package/dist/components/features/scripts/script-schedules-table.d.ts +38 -0
- package/dist/components/features/scripts/script-schedules-table.d.ts.map +1 -0
- package/dist/components/features/scripts/script-schedules-table.js +71 -0
- package/dist/components/features/scripts/script-shell-badge.d.ts +9 -0
- package/dist/components/features/scripts/script-shell-badge.d.ts.map +1 -0
- package/dist/components/features/scripts/script-shell-badge.js +15 -0
- package/dist/components/features/scripts/scripts-table-columns.d.ts +162 -0
- package/dist/components/features/scripts/scripts-table-columns.d.ts.map +1 -0
- package/dist/components/features/scripts/scripts-table-columns.js +69 -0
- package/dist/components/features/scripts/scripts-table-shared.d.ts +19 -0
- package/dist/components/features/scripts/scripts-table-shared.d.ts.map +1 -0
- package/dist/components/features/scripts/scripts-table-shared.js +35 -0
- package/dist/components/features/scripts/scripts-table.d.ts +40 -0
- package/dist/components/features/scripts/scripts-table.d.ts.map +1 -0
- package/dist/components/features/scripts/scripts-table.js +84 -0
- package/dist/components/features/scripts/scripts-tabs.d.ts +5 -0
- package/dist/components/features/scripts/scripts-tabs.d.ts.map +1 -0
- package/dist/components/features/scripts/scripts-tabs.js +10 -0
- package/dist/components/features/scripts/shell-types.d.ts +13 -0
- package/dist/components/features/scripts/shell-types.d.ts.map +1 -0
- package/dist/components/features/scripts/shell-types.js +21 -0
- package/dist/components/help-center-pages/index.cjs +144 -144
- package/dist/components/index.cjs +224 -86
- package/dist/components/meeting-scheduler/index.cjs +74 -74
- package/dist/components/navigation/index.cjs +27 -27
- package/dist/components/onboarding-guides/index.cjs +27 -27
- package/dist/components/product-demo/fixtures/scripts.d.ts +34 -0
- package/dist/components/product-demo/fixtures/scripts.d.ts.map +1 -0
- package/dist/components/product-demo/fixtures/scripts.js +123 -0
- package/dist/components/product-demo/fixtures/shared.d.ts +8 -0
- package/dist/components/product-demo/fixtures/shared.d.ts.map +1 -1
- package/dist/components/product-demo/fixtures/shared.js +7 -1
- package/dist/components/product-demo/fixtures/software-update.d.ts.map +1 -1
- package/dist/components/product-demo/fixtures/software-update.js +1 -5
- package/dist/components/product-demo/index.cjs +16 -13
- package/dist/components/product-demo/product-screen.d.ts.map +1 -1
- package/dist/components/product-demo/product-screen.js +2 -1
- package/dist/components/product-demo/registry.d.ts.map +1 -1
- package/dist/components/product-demo/registry.js +2 -1
- package/dist/components/product-demo/screen-keys.cjs +2 -1
- package/dist/components/product-demo/screen-keys.d.ts +1 -0
- package/dist/components/product-demo/screen-keys.d.ts.map +1 -1
- package/dist/components/product-demo/screen-keys.js +2 -1
- package/dist/components/product-demo/screens/scripts.d.ts +11 -0
- package/dist/components/product-demo/screens/scripts.d.ts.map +1 -0
- package/dist/components/product-demo/screens/scripts.js +130 -0
- package/dist/components/related-content/index.cjs +12 -12
- package/dist/components/tickets/index.cjs +30 -30
- package/dist/components/ui/index.cjs +24 -24
- package/dist/{devices-TWEA7XOR.cjs → devices-OFP2IBKN.cjs} +30 -30
- package/dist/{embeddable-chat-6JKKFQEB.cjs → embeddable-chat-4C22MGNC.cjs} +92 -92
- package/dist/hooks/index.cjs +10 -10
- package/dist/index.cjs +169 -31
- package/dist/{knowledge-4GQDBNQB.cjs → knowledge-KKBCSNUA.cjs} +29 -29
- package/dist/{logs-EVU6GJHZ.cjs → logs-X2GRNO57.cjs} +46 -46
- package/dist/{policies-OZUEA5YS.cjs → policies-35BXKB6P.cjs} +31 -31
- package/dist/{remote-session-DNFZ5OLY.cjs → remote-session-N4PJB5FW.cjs} +15 -15
- package/dist/scripts-KQYVQOLU.cjs +2112 -0
- package/dist/{software-update-BLIVYOY2.cjs → software-update-ED33WSKT.cjs} +36 -40
- package/dist/{tickets-board-CKZRB6YV.cjs → tickets-board-O753S4ZB.cjs} +31 -31
- package/dist/utils/embed-authed-fetch.d.ts +33 -0
- package/dist/utils/embed-authed-fetch.d.ts.map +1 -1
- package/dist/utils/embed-authed-fetch.js +3 -1
- package/dist/utils/index.cjs +3 -1
- package/dist/{video-file-player-ZH2ILAPO.cjs → video-file-player-QZ3YOLHO.cjs} +2 -2
- package/package.json +1 -1
- package/src/components/features/capability-explorer.tsx +134 -7
- package/src/components/features/index.ts +1 -0
- package/src/components/features/scripts/execution-cells.tsx +149 -0
- package/src/components/features/scripts/execution-status.ts +42 -0
- package/src/components/features/scripts/index.ts +42 -0
- package/src/components/features/scripts/schedule-form/index.ts +59 -0
- package/src/components/features/scripts/schedule-form/schedule-enums.ts +26 -0
- package/src/components/features/scripts/schedule-form/schedule-form-fields.tsx +249 -0
- package/src/components/features/scripts/schedule-form/schedule-form-types.ts +189 -0
- package/src/components/features/scripts/schedule-form/schedule-interval-input.tsx +121 -0
- package/src/components/features/scripts/schedule-form/schedule-offline-fields.tsx +218 -0
- package/src/components/features/scripts/schedule-form/schedule-script-picker-card.tsx +321 -0
- package/src/components/features/scripts/schedule-form/schedule-script-search-field.tsx +115 -0
- package/src/components/features/scripts/schedule-form/schedule-scripts-field.tsx +97 -0
- package/src/components/features/scripts/schedule-form/schedule-timing-fields.tsx +482 -0
- package/src/components/features/scripts/schedule-form/schedule-timing.ts +484 -0
- package/src/components/features/scripts/script-cells.tsx +29 -0
- package/src/components/features/scripts/script-executions-table.tsx +145 -0
- package/src/components/features/scripts/script-schedules-table.tsx +113 -0
- package/src/components/features/scripts/script-shell-badge.tsx +25 -0
- package/src/components/features/scripts/scripts-table-columns.ts +95 -0
- package/src/components/features/scripts/scripts-table-shared.tsx +43 -0
- package/src/components/features/scripts/scripts-table.tsx +124 -0
- package/src/components/features/scripts/scripts-tabs.ts +11 -0
- package/src/components/features/scripts/shell-types.ts +26 -0
- package/src/components/product-demo/fixtures/scripts.ts +186 -0
- package/src/components/product-demo/fixtures/shared.ts +7 -0
- package/src/components/product-demo/fixtures/software-update.ts +1 -7
- package/src/components/product-demo/product-screen.tsx +3 -1
- package/src/components/product-demo/registry.ts +1 -0
- package/src/components/product-demo/screen-keys.ts +1 -0
- package/src/components/product-demo/screens/scripts.tsx +192 -0
- package/src/utils/embed-authed-fetch.ts +42 -1
|
@@ -21,13 +21,15 @@ export const PRODUCT_SCREEN_COMPACT_HEIGHT = 300;
|
|
|
21
21
|
* host's readable width: the device list's customer column breaks a name
|
|
22
22
|
* mid-word below it, and the logs table beside its open drawer has no room
|
|
23
23
|
* for the message; the software form is laid out wide so the package, the
|
|
24
|
-
* schedule and the picked devices all fit the frame's height
|
|
24
|
+
* schedule and the picked devices all fit the frame's height; the scripts
|
|
25
|
+
* screen puts a schedule's name, its timing and its repeat on one row. A frame never
|
|
25
26
|
* lays such a screen out narrower.
|
|
26
27
|
*/
|
|
27
28
|
const MIN_LAYOUT_WIDTH: Partial<Record<ProductScreenKey, number>> = {
|
|
28
29
|
devices: 1120,
|
|
29
30
|
logs: 1120,
|
|
30
31
|
'software-update': 1400,
|
|
32
|
+
scripts: 1400,
|
|
31
33
|
};
|
|
32
34
|
|
|
33
35
|
/**
|
|
@@ -17,4 +17,5 @@ export const PRODUCT_SCREEN_LOADERS: Record<ProductScreenKey, ProductScreenLoade
|
|
|
17
17
|
'tickets-board': () => import('./screens/tickets-board'),
|
|
18
18
|
logs: () => import('./screens/logs'),
|
|
19
19
|
knowledge: () => import('./screens/knowledge'),
|
|
20
|
+
scripts: () => import('./screens/scripts'),
|
|
20
21
|
};
|
|
@@ -15,6 +15,7 @@ export const PRODUCT_SCREEN_LABELS = {
|
|
|
15
15
|
'tickets-board': 'Ticket board',
|
|
16
16
|
logs: 'Logs with a log open',
|
|
17
17
|
knowledge: 'Knowledge base',
|
|
18
|
+
scripts: 'Script library and schedules',
|
|
18
19
|
} as const;
|
|
19
20
|
|
|
20
21
|
export type ProductScreenKey = keyof typeof PRODUCT_SCREEN_LABELS;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useMemo } from 'react';
|
|
4
|
+
import {
|
|
5
|
+
EXECUTION_COLUMNS,
|
|
6
|
+
ScriptExecutionResult,
|
|
7
|
+
ScriptExecutionsTable,
|
|
8
|
+
type ScriptExecutionTableRow,
|
|
9
|
+
ScriptShellBadge,
|
|
10
|
+
type ScriptsTableColumnLayout,
|
|
11
|
+
} from '../../features/scripts';
|
|
12
|
+
import { TimerIcon } from '../../icons-v2-generated/date-and-time/timer-icon';
|
|
13
|
+
import { TruncateText } from '../../ui/truncate-text';
|
|
14
|
+
import { useProductDemoCast } from '../cast';
|
|
15
|
+
import { buildScriptsFixture, type DemoScript } from '../fixtures/scripts';
|
|
16
|
+
import type { ProductScreenViewProps } from '../types';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The picture has nowhere to open a run and no row menu, and the Result
|
|
20
|
+
* column would repeat the output opened under its row.
|
|
21
|
+
*/
|
|
22
|
+
const WIDE_HIDDEN_COLUMNS = ['result', 'open'];
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* A card's width hides the device column (the product's page drops it below
|
|
26
|
+
* `lg`), and the device is what the narrow picture is about: here it takes
|
|
27
|
+
* the room, with the status beside it.
|
|
28
|
+
*/
|
|
29
|
+
const COMPACT_COLUMNS: readonly ScriptsTableColumnLayout[] = [
|
|
30
|
+
{ ...EXECUTION_COLUMNS.machineId, width: 'flex-1 min-w-0', hideAt: undefined },
|
|
31
|
+
EXECUTION_COLUMNS.status,
|
|
32
|
+
];
|
|
33
|
+
const COMPACT_ROWS = 4;
|
|
34
|
+
|
|
35
|
+
type TokenKind = 'comment' | 'keyword' | 'string' | 'variable' | 'command' | 'parameter' | 'number' | 'plain';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* PowerShell token colours: the product editor's `ods-dark` highlight style
|
|
39
|
+
* (`script-editor-view.tsx`) in ODS tokens. Keywords yellow, strings and
|
|
40
|
+
* variables cyan, commands green, numbers amber, parameters grey, comments
|
|
41
|
+
* muted.
|
|
42
|
+
*/
|
|
43
|
+
const TOKEN_CLASS: Record<TokenKind, string> = {
|
|
44
|
+
comment: 'italic text-ods-text-tertiary',
|
|
45
|
+
keyword: 'text-ods-warning',
|
|
46
|
+
string: 'text-ods-flamingo-cyan',
|
|
47
|
+
variable: 'text-ods-flamingo-cyan',
|
|
48
|
+
command: 'text-ods-success',
|
|
49
|
+
parameter: 'text-ods-text-secondary',
|
|
50
|
+
number: 'text-ods-open-yellow',
|
|
51
|
+
plain: 'text-ods-text-primary',
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/** One group per kind, in {@link TOKEN_KINDS} order; a comment or a string swallows what it contains. */
|
|
55
|
+
const TOKEN_PATTERN =
|
|
56
|
+
/(#.*$)|("(?:[^"\\]|\\.)*"|'[^']*')|(\$[\w:]+)|\b(foreach|in|if|else|exit|return|param)\b|\b([A-Z][a-z]+-[A-Z][A-Za-z]+)\b|(-[A-Za-z]+)\b|\b(\d+(?:GB|MB|KB)?)\b/g;
|
|
57
|
+
const TOKEN_KINDS: readonly TokenKind[] = [
|
|
58
|
+
'comment',
|
|
59
|
+
'string',
|
|
60
|
+
'variable',
|
|
61
|
+
'keyword',
|
|
62
|
+
'command',
|
|
63
|
+
'parameter',
|
|
64
|
+
'number',
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
interface Token {
|
|
68
|
+
text: string;
|
|
69
|
+
kind: TokenKind;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** One line of PowerShell as coloured runs. Only the picture's own script is read, so the rules are few. */
|
|
73
|
+
function tokenize(line: string): Token[] {
|
|
74
|
+
const tokens: Token[] = [];
|
|
75
|
+
let last = 0;
|
|
76
|
+
for (const match of line.matchAll(TOKEN_PATTERN)) {
|
|
77
|
+
const start = match.index ?? 0;
|
|
78
|
+
if (start > last) tokens.push({ text: line.slice(last, start), kind: 'plain' });
|
|
79
|
+
const group = match.slice(1).findIndex(Boolean);
|
|
80
|
+
tokens.push({ text: match[0], kind: TOKEN_KINDS[group] ?? 'plain' });
|
|
81
|
+
last = start + match[0].length;
|
|
82
|
+
}
|
|
83
|
+
if (last < line.length) tokens.push({ text: line.slice(last), kind: 'plain' });
|
|
84
|
+
return tokens;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* The script as the product's details page shows it: its name and shell over
|
|
89
|
+
* the editor's read-only view. The product's editor is CodeMirror, which the
|
|
90
|
+
* library does not carry, so this draws the editor's geometry (a 46px number
|
|
91
|
+
* gutter, the code 32px past it, 22px rows of 14px mono) in its colours. The
|
|
92
|
+
* header adds the schedule that runs the script and who wrote and approved
|
|
93
|
+
* it: the two things the conversation beside the picture settled.
|
|
94
|
+
*/
|
|
95
|
+
function ScriptCodePicture({ script }: { script: DemoScript }) {
|
|
96
|
+
// Keyed by line number: the script never changes under the picture.
|
|
97
|
+
const lines = useMemo(
|
|
98
|
+
() => script.code.split('\n').map((line, index) => ({ number: index + 1, tokens: tokenize(line) })),
|
|
99
|
+
[script.code],
|
|
100
|
+
);
|
|
101
|
+
const { schedule, provenance } = script;
|
|
102
|
+
return (
|
|
103
|
+
<section
|
|
104
|
+
aria-label={script.name}
|
|
105
|
+
className="flex h-full min-w-0 flex-col overflow-hidden rounded-[8px] border border-ods-border bg-ods-card"
|
|
106
|
+
>
|
|
107
|
+
<div className="flex flex-col gap-[var(--spacing-system-xxs)] border-b border-ods-border p-[var(--spacing-system-m)]">
|
|
108
|
+
<div className="flex min-w-0 items-center justify-between gap-[var(--spacing-system-m)]">
|
|
109
|
+
<TruncateText variant="h4">{script.name}</TruncateText>
|
|
110
|
+
<span className="flex shrink-0 items-center gap-[var(--spacing-system-xxs)] text-ods-text-primary text-h6">
|
|
111
|
+
<TimerIcon className="size-4 text-ods-text-secondary" />
|
|
112
|
+
{`${schedule.repeat} ${schedule.time} · ${schedule.deviceCount} devices`}
|
|
113
|
+
</span>
|
|
114
|
+
</div>
|
|
115
|
+
<div className="flex min-w-0 items-center justify-between gap-[var(--spacing-system-m)]">
|
|
116
|
+
<TruncateText variant="h6" tone="secondary">
|
|
117
|
+
{`Written by ${provenance.author} · approved by ${provenance.approver}`}
|
|
118
|
+
</TruncateText>
|
|
119
|
+
<ScriptShellBadge
|
|
120
|
+
value={script.shellType}
|
|
121
|
+
className="shrink-0 text-ods-text-secondary text-h6"
|
|
122
|
+
iconClassName="size-4"
|
|
123
|
+
/>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
<pre className="m-0 min-h-0 flex-1 overflow-hidden bg-ods-bg py-3 font-mono text-[14px] leading-[22px]">
|
|
127
|
+
{lines.map(line => (
|
|
128
|
+
<div key={line.number} className="flex">
|
|
129
|
+
<span className="w-[46px] shrink-0 select-none pr-[4px] text-right text-ods-text-tertiary">
|
|
130
|
+
{line.number}
|
|
131
|
+
</span>
|
|
132
|
+
<code className="whitespace-pre pl-[32px]">
|
|
133
|
+
{line.tokens.map((token, at) => (
|
|
134
|
+
<span key={`${at}-${token.text}`} className={TOKEN_CLASS[token.kind]}>
|
|
135
|
+
{token.text}
|
|
136
|
+
</span>
|
|
137
|
+
))}
|
|
138
|
+
</code>
|
|
139
|
+
</div>
|
|
140
|
+
))}
|
|
141
|
+
</pre>
|
|
142
|
+
</section>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Where the page's Mingo conversation leaves off: the script it wrote, on the
|
|
148
|
+
* left, and the first night its schedule ran, on the right, as the product's
|
|
149
|
+
* Execution History (a run still going, one failed, one finished with its
|
|
150
|
+
* output open under it: what it cleaned and what it freed). The schedule form
|
|
151
|
+
* and the library are the conversation's part, so the picture leaves them
|
|
152
|
+
* out. The narrow rendering is the run alone: which devices, and how each went.
|
|
153
|
+
*/
|
|
154
|
+
export default function ScriptsScreen({ compact = false }: ProductScreenViewProps) {
|
|
155
|
+
const cast = useProductDemoCast();
|
|
156
|
+
const fixture = useMemo(() => buildScriptsFixture(cast), [cast]);
|
|
157
|
+
|
|
158
|
+
if (compact) {
|
|
159
|
+
return (
|
|
160
|
+
// Four runs of the card's row height fill the frame; the padding is what is left round them.
|
|
161
|
+
<div className="h-full overflow-hidden bg-ods-bg p-[var(--spacing-system-xsf)]">
|
|
162
|
+
<ScriptExecutionsTable
|
|
163
|
+
rows={fixture.executions.slice(0, COMPACT_ROWS)}
|
|
164
|
+
columns={COMPACT_COLUMNS}
|
|
165
|
+
showHeader={false}
|
|
166
|
+
compact
|
|
167
|
+
/>
|
|
168
|
+
</div>
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const renderOutput = (row: ScriptExecutionTableRow) =>
|
|
173
|
+
row.id === fixture.openExecutionId ? <ScriptExecutionResult result={row.result} empty="—" /> : null;
|
|
174
|
+
|
|
175
|
+
return (
|
|
176
|
+
// The frame's tighter padding and no header row: the output opened under a run needs the height.
|
|
177
|
+
<div className="flex h-full gap-[var(--spacing-system-m)] overflow-hidden bg-ods-bg p-[var(--spacing-system-m)]">
|
|
178
|
+
<div className="w-[39%] shrink-0">
|
|
179
|
+
<ScriptCodePicture script={fixture.script} />
|
|
180
|
+
</div>
|
|
181
|
+
<div className="min-w-0 flex-1 overflow-hidden">
|
|
182
|
+
<ScriptExecutionsTable
|
|
183
|
+
rows={fixture.executions}
|
|
184
|
+
hideColumns={WIDE_HIDDEN_COLUMNS}
|
|
185
|
+
renderSubRow={renderOutput}
|
|
186
|
+
showHeader={false}
|
|
187
|
+
compact
|
|
188
|
+
/>
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
@@ -86,6 +86,34 @@ export interface EmbedAuthAdapter {
|
|
|
86
86
|
* host's own auth layer, not to this fetch wrapper.
|
|
87
87
|
*/
|
|
88
88
|
refresh?: () => Promise<boolean>;
|
|
89
|
+
/**
|
|
90
|
+
* Optional guard, run before every request goes out — the first attempt
|
|
91
|
+
* and the retry after a `refresh`. Throw — or reject — to refuse the
|
|
92
|
+
* request: `embedAuthedFetch` rejects with that error and nothing reaches
|
|
93
|
+
* the network.
|
|
94
|
+
*
|
|
95
|
+
* For a host-wide state that makes every request pointless. openframe's
|
|
96
|
+
* mobile force-update is the case it exists for: once the gateway has
|
|
97
|
+
* refused this client build, every later request is refused locally, so no
|
|
98
|
+
* surface's retry or reconnect loop keeps hitting the gateway. Runs inside
|
|
99
|
+
* the returned promise (after the synchronous same-origin guard), so
|
|
100
|
+
* callers see a rejection, never a synchronous throw.
|
|
101
|
+
*/
|
|
102
|
+
beforeRequest?: () => void | Promise<void>;
|
|
103
|
+
/**
|
|
104
|
+
* Optional response hook, run on every response (the post-refresh retry's
|
|
105
|
+
* too) BEFORE the 401 self-heal. Throw — or reject — to fail the request
|
|
106
|
+
* with that error: `embedAuthedFetch` rejects with it, and neither
|
|
107
|
+
* `refresh` nor a retry runs. Return to let the response continue through
|
|
108
|
+
* the wrapper unchanged.
|
|
109
|
+
*
|
|
110
|
+
* For a status the host handles app-wide rather than per surface — e.g.
|
|
111
|
+
* openframe's gateway answering `426 Upgrade Required` to a too-old mobile
|
|
112
|
+
* build, which swaps the whole app for an "update required" screen and
|
|
113
|
+
* must never be retried or read as an auth failure. Read the body from
|
|
114
|
+
* `response.clone()`: once the hook returns, the caller reads the original.
|
|
115
|
+
*/
|
|
116
|
+
onResponse?: (response: Response) => void | Promise<void>;
|
|
89
117
|
/**
|
|
90
118
|
* Exact extra origins (`scheme://host[:port]`, as produced by `URL.origin`)
|
|
91
119
|
* the same-origin guard additionally accepts. The guard normally rejects
|
|
@@ -253,6 +281,11 @@ export function needsBearerAssetFetch(url: string): boolean {
|
|
|
253
281
|
* no longer need a host-side `window.fetch` monkey-patch to survive an
|
|
254
282
|
* expired access token mid-chat. With no adapter (or no `refresh`), the
|
|
255
283
|
* 401 passes straight through unchanged.
|
|
284
|
+
*
|
|
285
|
+
* **Host hooks:** an adapter's `beforeRequest` may refuse a request before it
|
|
286
|
+
* is sent, and its `onResponse` sees every response ahead of the 401
|
|
287
|
+
* self-heal and may fail the request instead (see their docs). Without them
|
|
288
|
+
* the wrapper behaves exactly as described above.
|
|
256
289
|
*/
|
|
257
290
|
export function embedAuthedFetch(url: string, init: RequestInit = {}): Promise<Response> {
|
|
258
291
|
// Same-origin guard runs SYNCHRONOUSLY (not awaited inside the async
|
|
@@ -354,6 +387,11 @@ async function fetchWithRefresh(
|
|
|
354
387
|
baseHeaders: Record<string, string>,
|
|
355
388
|
isRetry: boolean,
|
|
356
389
|
): Promise<Response> {
|
|
390
|
+
const adapter = getRegisteredAuthAdapter();
|
|
391
|
+
// Per attempt, so a retry after `refresh` is refused too when the host's
|
|
392
|
+
// state changed while the refresh was in flight.
|
|
393
|
+
await adapter?.beforeRequest?.();
|
|
394
|
+
|
|
357
395
|
// Re-run the merge each attempt: `applyProxyAuth` reads the latest stored
|
|
358
396
|
// proxy creds and `getHeaders()` reads the latest bearer, so a retry after
|
|
359
397
|
// refresh carries the rotated token rather than the stale one. `{...baseHeaders}`
|
|
@@ -365,7 +403,6 @@ async function fetchWithRefresh(
|
|
|
365
403
|
// is the host's explicit "this is my auth model" override, intentionally
|
|
366
404
|
// last-writer-wins. When no adapter is registered, this is a zero-cost
|
|
367
405
|
// no-op (object spread of `{}`).
|
|
368
|
-
const adapter = getRegisteredAuthAdapter();
|
|
369
406
|
if (adapter?.getHeaders) {
|
|
370
407
|
// Filter `undefined` values — the adapter type allows them so consumers
|
|
371
408
|
// don't have to narrow `{ Authorization: token ? '…' : undefined }`-shaped
|
|
@@ -387,6 +424,10 @@ async function fetchWithRefresh(
|
|
|
387
424
|
credentials,
|
|
388
425
|
});
|
|
389
426
|
|
|
427
|
+
// Ahead of the 401 self-heal: a status the host claims by throwing must
|
|
428
|
+
// never reach `refresh` or a retry.
|
|
429
|
+
await adapter?.onResponse?.(response);
|
|
430
|
+
|
|
390
431
|
// 401 self-heal: refresh the token once and retry. Only when an adapter
|
|
391
432
|
// opted into `refresh`, and only on the first attempt — a 401 on the
|
|
392
433
|
// retry means the fresh token is also unauthorized, so surface it.
|