wenay-react2 1.0.37 → 1.0.38
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/README.md +2 -0
- package/lib/common/src/components/Modal/ModalContextProvider.js +1 -1
- package/lib/common/src/components/Settings/SettingsDialog.d.ts +12 -2
- package/lib/common/src/components/Settings/SettingsDialog.js +401 -16
- package/lib/common/src/components/Toolbar/Toolbar.d.ts +21 -2
- package/lib/common/src/components/Toolbar/Toolbar.js +84 -14
- package/lib/common/src/grid/columnState/CardList.js +5 -2
- package/lib/common/src/grid/columnState/ColumnDots.js +8 -18
- package/lib/common/src/grid/columnState/columnState.d.ts +4 -0
- package/lib/common/src/grid/columnState/columnState.js +39 -14
- package/lib/common/src/hooks/useReplay.d.ts +54 -0
- package/lib/common/src/hooks/useReplay.js +222 -0
- package/lib/common/src/logs/logStyles.d.ts +13 -0
- package/lib/common/src/logs/logStyles.js +18 -0
- package/lib/common/src/logs/logs.js +5 -7
- package/lib/common/src/logs/logsContext.js +22 -26
- package/lib/common/src/menu/menu.d.ts +2 -1
- package/lib/common/src/menu/menu.js +28 -21
- package/lib/common/src/styles/tokens.d.ts +14 -1
- package/lib/common/src/styles/tokens.js +14 -1
- package/lib/common/src/utils/index.d.ts +1 -0
- package/lib/common/src/utils/index.js +1 -0
- package/lib/common/src/utils/searchHistory.d.ts +14 -0
- package/lib/common/src/utils/searchHistory.js +59 -0
- package/lib/style/menuRight.css +2 -2
- package/lib/style/style.css +563 -54
- package/lib/style/tokens.css +15 -3
- package/package.json +2 -2
|
@@ -111,6 +111,114 @@ export function useReplaySubscribe(remote, cb, options = {}) {
|
|
|
111
111
|
}, []);
|
|
112
112
|
return useMemo(() => ({ ready, error, stale, seq, lastTs, restart }), [ready, error, stale, seq, lastTs, restart]);
|
|
113
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* Route-replaceable replay subscription. Use this when the app can promote/demote between
|
|
116
|
+
* transports (relay <-> direct) and must keep one logical replay fold with no gaps or dups.
|
|
117
|
+
* Changing the `remote` prop is still a fresh subscription boundary; no-gap hand-off is explicit
|
|
118
|
+
* through `controller.switchRoute(...)`.
|
|
119
|
+
*/
|
|
120
|
+
export function useReplayRouteSubscribe(remote, cb, options = {}) {
|
|
121
|
+
const { since, keepSeq = true, enabled = true, label, onSeq, onError, onRoute, policy, hint } = options;
|
|
122
|
+
const cbRef = useLatestRef(cb);
|
|
123
|
+
const hooksRef = useLatestRef({ onSeq, onError, onRoute });
|
|
124
|
+
const hintRef = useLatestRef(hint);
|
|
125
|
+
const seqRef = useRef(since);
|
|
126
|
+
const labelRef = useRef(label);
|
|
127
|
+
const activeRef = useRef(false);
|
|
128
|
+
const subRef = useRef(null);
|
|
129
|
+
const lastRemoteRef = useRef(undefined);
|
|
130
|
+
const [ready, setReady] = useState(false);
|
|
131
|
+
const [error, setError] = useState(null);
|
|
132
|
+
const [route, setRoute] = useState(null);
|
|
133
|
+
const [switching, setSwitching] = useState(false);
|
|
134
|
+
useEffect(() => {
|
|
135
|
+
if (!remote || !enabled)
|
|
136
|
+
return;
|
|
137
|
+
if (lastRemoteRef.current !== undefined && lastRemoteRef.current !== remote)
|
|
138
|
+
seqRef.current = undefined;
|
|
139
|
+
lastRemoteRef.current = remote;
|
|
140
|
+
let alive = true;
|
|
141
|
+
setReady(false);
|
|
142
|
+
setError(null);
|
|
143
|
+
setRoute(null);
|
|
144
|
+
setSwitching(false);
|
|
145
|
+
labelRef.current = label;
|
|
146
|
+
activeRef.current = false;
|
|
147
|
+
const off = Replay.replayRouteSubscribe(remote, (...event) => cbRef.current(...event), {
|
|
148
|
+
since: seqRef.current,
|
|
149
|
+
label,
|
|
150
|
+
policy,
|
|
151
|
+
hint: hintRef.current,
|
|
152
|
+
onSeq: seq => {
|
|
153
|
+
seqRef.current = seq;
|
|
154
|
+
hooksRef.current.onSeq?.(seq);
|
|
155
|
+
},
|
|
156
|
+
onError: e => {
|
|
157
|
+
if (alive)
|
|
158
|
+
setError(e);
|
|
159
|
+
hooksRef.current.onError?.(e);
|
|
160
|
+
},
|
|
161
|
+
onRoute: ev => {
|
|
162
|
+
if (alive) {
|
|
163
|
+
setRoute(ev);
|
|
164
|
+
setSwitching(ev.phase == 'switching');
|
|
165
|
+
if (ev.phase == 'switching')
|
|
166
|
+
setReady(false);
|
|
167
|
+
if (ev.phase == 'ready') {
|
|
168
|
+
labelRef.current = ev.to;
|
|
169
|
+
activeRef.current = true;
|
|
170
|
+
setReady(true);
|
|
171
|
+
}
|
|
172
|
+
if (ev.phase == 'closed') {
|
|
173
|
+
activeRef.current = false;
|
|
174
|
+
setReady(false);
|
|
175
|
+
}
|
|
176
|
+
if (ev.phase == 'error') {
|
|
177
|
+
activeRef.current = subRef.current?.active() ?? false;
|
|
178
|
+
setReady(activeRef.current);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
hooksRef.current.onRoute?.(ev);
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
subRef.current = off;
|
|
185
|
+
off.ready.then(() => {
|
|
186
|
+
if (!alive)
|
|
187
|
+
return;
|
|
188
|
+
activeRef.current = off.active();
|
|
189
|
+
labelRef.current = off.label();
|
|
190
|
+
setReady(true);
|
|
191
|
+
setSwitching(false);
|
|
192
|
+
}, e => {
|
|
193
|
+
if (!alive)
|
|
194
|
+
return;
|
|
195
|
+
setError(e);
|
|
196
|
+
setReady(false);
|
|
197
|
+
setSwitching(false);
|
|
198
|
+
});
|
|
199
|
+
return () => {
|
|
200
|
+
alive = false;
|
|
201
|
+
subRef.current = null;
|
|
202
|
+
activeRef.current = false;
|
|
203
|
+
off();
|
|
204
|
+
if (!keepSeq)
|
|
205
|
+
seqRef.current = since;
|
|
206
|
+
};
|
|
207
|
+
// label/since/keepSeq are start-position metadata; route changes go through switchRoute(). hint rides a ref.
|
|
208
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
209
|
+
}, [remote, enabled, policy]);
|
|
210
|
+
const seq = useCallback(() => subRef.current?.seq() ?? seqRef.current ?? -1, []);
|
|
211
|
+
const currentLabel = useCallback(() => subRef.current?.label() ?? labelRef.current, []);
|
|
212
|
+
const active = useCallback(() => subRef.current?.active() ?? activeRef.current, []);
|
|
213
|
+
const switchRoute = useCallback((nextRemote, switchOptions) => {
|
|
214
|
+
const sub = subRef.current;
|
|
215
|
+
if (!sub)
|
|
216
|
+
return Promise.reject(new Error("useReplayRouteSubscribe: no active route subscription"));
|
|
217
|
+
setError(null);
|
|
218
|
+
return sub.switch(nextRemote, switchOptions);
|
|
219
|
+
}, []);
|
|
220
|
+
return useMemo(() => ({ ready, error, route, switching, seq, label: currentLabel, active, switchRoute }), [ready, error, route, switching, seq, currentLabel, active, switchRoute]);
|
|
221
|
+
}
|
|
114
222
|
/**
|
|
115
223
|
* Sync an existing mirror store from a store replay line (`Observe.exposeStoreReplay(...).api.replay`).
|
|
116
224
|
* Thin lifecycle wrapper over `Observe.syncStoreReplay`: the keyframe (root patch) and the
|
|
@@ -188,6 +296,120 @@ export function useStoreReplaySync(store, remote, options = {}) {
|
|
|
188
296
|
}, []);
|
|
189
297
|
return useMemo(() => ({ ready, error, stale, seq, lastTs, restart }), [ready, error, stale, seq, lastTs, restart]);
|
|
190
298
|
}
|
|
299
|
+
/**
|
|
300
|
+
* Route-replaceable store replay sync. The supplied store remains the fold target while
|
|
301
|
+
* switchRoute() promotes/demotes the underlying replay route.
|
|
302
|
+
*/
|
|
303
|
+
export function useStoreReplayRouteSync(store, remote, options = {}) {
|
|
304
|
+
const { since, keepSeq = true, enabled = true, label, onSeq, onError, onRoute, policy, hint } = options;
|
|
305
|
+
const hooksRef = useLatestRef({ onSeq, onError, onRoute });
|
|
306
|
+
const hintRef = useLatestRef(hint);
|
|
307
|
+
const seqRef = useRef(since);
|
|
308
|
+
const labelRef = useRef(label);
|
|
309
|
+
const activeRef = useRef(false);
|
|
310
|
+
const subRef = useRef(null);
|
|
311
|
+
const lastRemoteRef = useRef(undefined);
|
|
312
|
+
const [ready, setReady] = useState(false);
|
|
313
|
+
const [error, setError] = useState(null);
|
|
314
|
+
const [route, setRoute] = useState(null);
|
|
315
|
+
const [switching, setSwitching] = useState(false);
|
|
316
|
+
useEffect(() => {
|
|
317
|
+
if (!store || !remote || !enabled)
|
|
318
|
+
return;
|
|
319
|
+
if (lastRemoteRef.current !== undefined && lastRemoteRef.current !== remote)
|
|
320
|
+
seqRef.current = undefined;
|
|
321
|
+
lastRemoteRef.current = remote;
|
|
322
|
+
let alive = true;
|
|
323
|
+
setReady(false);
|
|
324
|
+
setError(null);
|
|
325
|
+
setRoute(null);
|
|
326
|
+
setSwitching(false);
|
|
327
|
+
labelRef.current = label;
|
|
328
|
+
activeRef.current = false;
|
|
329
|
+
const off = Observe.syncStoreReplayRoute(store, remote, {
|
|
330
|
+
since: seqRef.current,
|
|
331
|
+
label,
|
|
332
|
+
policy,
|
|
333
|
+
hint: hintRef.current,
|
|
334
|
+
onSeq: seq => {
|
|
335
|
+
seqRef.current = seq;
|
|
336
|
+
hooksRef.current.onSeq?.(seq);
|
|
337
|
+
},
|
|
338
|
+
onError: e => {
|
|
339
|
+
if (alive)
|
|
340
|
+
setError(e);
|
|
341
|
+
hooksRef.current.onError?.(e);
|
|
342
|
+
},
|
|
343
|
+
onRoute: ev => {
|
|
344
|
+
if (alive) {
|
|
345
|
+
setRoute(ev);
|
|
346
|
+
setSwitching(ev.phase == 'switching');
|
|
347
|
+
if (ev.phase == 'switching')
|
|
348
|
+
setReady(false);
|
|
349
|
+
if (ev.phase == 'ready') {
|
|
350
|
+
labelRef.current = ev.to;
|
|
351
|
+
activeRef.current = true;
|
|
352
|
+
setReady(true);
|
|
353
|
+
}
|
|
354
|
+
if (ev.phase == 'closed') {
|
|
355
|
+
activeRef.current = false;
|
|
356
|
+
setReady(false);
|
|
357
|
+
}
|
|
358
|
+
if (ev.phase == 'error') {
|
|
359
|
+
activeRef.current = subRef.current?.active() ?? false;
|
|
360
|
+
setReady(activeRef.current);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
hooksRef.current.onRoute?.(ev);
|
|
364
|
+
},
|
|
365
|
+
});
|
|
366
|
+
subRef.current = off;
|
|
367
|
+
off.ready.then(() => {
|
|
368
|
+
if (!alive)
|
|
369
|
+
return;
|
|
370
|
+
activeRef.current = off.active();
|
|
371
|
+
labelRef.current = off.label();
|
|
372
|
+
setReady(true);
|
|
373
|
+
setSwitching(false);
|
|
374
|
+
}, e => {
|
|
375
|
+
if (!alive)
|
|
376
|
+
return;
|
|
377
|
+
setError(e);
|
|
378
|
+
setReady(false);
|
|
379
|
+
setSwitching(false);
|
|
380
|
+
});
|
|
381
|
+
return () => {
|
|
382
|
+
alive = false;
|
|
383
|
+
subRef.current = null;
|
|
384
|
+
activeRef.current = false;
|
|
385
|
+
off();
|
|
386
|
+
if (!keepSeq)
|
|
387
|
+
seqRef.current = since;
|
|
388
|
+
};
|
|
389
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
390
|
+
}, [store, remote, enabled, policy]);
|
|
391
|
+
const seq = useCallback(() => subRef.current?.seq() ?? seqRef.current ?? -1, []);
|
|
392
|
+
const currentLabel = useCallback(() => subRef.current?.label() ?? labelRef.current, []);
|
|
393
|
+
const active = useCallback(() => subRef.current?.active() ?? activeRef.current, []);
|
|
394
|
+
const switchRoute = useCallback((nextRemote, switchOptions) => {
|
|
395
|
+
const sub = subRef.current;
|
|
396
|
+
if (!sub)
|
|
397
|
+
return Promise.reject(new Error("useStoreReplayRouteSync: no active route subscription"));
|
|
398
|
+
setError(null);
|
|
399
|
+
return sub.switch(nextRemote, switchOptions);
|
|
400
|
+
}, []);
|
|
401
|
+
return useMemo(() => ({ ready, error, route, switching, seq, label: currentLabel, active, switchRoute }), [ready, error, route, switching, seq, currentLabel, active, switchRoute]);
|
|
402
|
+
}
|
|
403
|
+
/** Create a local mirror store and keep it synced through a route-replaceable replay remote. */
|
|
404
|
+
export function useStoreReplayRouteMirror(remote, initial, options = {}) {
|
|
405
|
+
const storeRef = useRef(null);
|
|
406
|
+
if (!storeRef.current || storeRef.current.remote !== remote) {
|
|
407
|
+
storeRef.current = { remote, store: Observe.createStore(initial) };
|
|
408
|
+
}
|
|
409
|
+
const store = storeRef.current.store;
|
|
410
|
+
const sync = useStoreReplayRouteSync(store, remote, options);
|
|
411
|
+
return useMemo(() => ({ ...sync, store }), [sync, store]);
|
|
412
|
+
}
|
|
191
413
|
/**
|
|
192
414
|
* Convenience: create a local mirror store and keep it synced from a store replay line.
|
|
193
415
|
* The store lives in a ref — a component remount keeps both the state and the seq,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const logStyleTokens: {
|
|
2
|
+
readonly text: "var(--logs-notification-text, #fff)";
|
|
3
|
+
readonly accent: "var(--logs-notification-accent, #5D9FFA)";
|
|
4
|
+
readonly toggleBg: "var(--logs-toggle-bg, rgb(58, 58, 58))";
|
|
5
|
+
readonly toggleOffBg: "var(--logs-toggle-off-bg, rgb(144, 60, 60))";
|
|
6
|
+
readonly divider: "var(--logs-divider, rgba(255, 255, 255, 1))";
|
|
7
|
+
readonly tabNavBg: "var(--logs-tab-nav-bg, #333)";
|
|
8
|
+
readonly tabBg: "var(--logs-tab-bg, #444)";
|
|
9
|
+
readonly tabActiveBg: "var(--logs-tab-active-bg, #666)";
|
|
10
|
+
readonly tabText: "var(--logs-tab-text, #fff)";
|
|
11
|
+
};
|
|
12
|
+
export declare function logSeverityBackground(importance?: number): string;
|
|
13
|
+
export declare function logDividerGradient(): string;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const logStyleTokens = {
|
|
2
|
+
text: 'var(--logs-notification-text, #fff)',
|
|
3
|
+
accent: 'var(--logs-notification-accent, #5D9FFA)',
|
|
4
|
+
toggleBg: 'var(--logs-toggle-bg, rgb(58, 58, 58))',
|
|
5
|
+
toggleOffBg: 'var(--logs-toggle-off-bg, rgb(144, 60, 60))',
|
|
6
|
+
divider: 'var(--logs-divider, rgba(255, 255, 255, 1))',
|
|
7
|
+
tabNavBg: 'var(--logs-tab-nav-bg, #333)',
|
|
8
|
+
tabBg: 'var(--logs-tab-bg, #444)',
|
|
9
|
+
tabActiveBg: 'var(--logs-tab-active-bg, #666)',
|
|
10
|
+
tabText: 'var(--logs-tab-text, #fff)',
|
|
11
|
+
};
|
|
12
|
+
export function logSeverityBackground(importance = 0) {
|
|
13
|
+
const level = Number.isFinite(importance) ? Math.max(0, importance) : 0;
|
|
14
|
+
return `rgb(${Math.min(255, level * 10)}, 73, 35)`;
|
|
15
|
+
}
|
|
16
|
+
export function logDividerGradient() {
|
|
17
|
+
return `linear-gradient(to right, transparent, ${logStyleTokens.divider}, transparent)`;
|
|
18
|
+
}
|
|
@@ -6,6 +6,7 @@ import { renderBy, updateBy } from "../../updateBy";
|
|
|
6
6
|
import { contextMenu } from "../menu/menuMouse";
|
|
7
7
|
import { memoryGetOrCreate } from "../utils/memoryStore";
|
|
8
8
|
import { ParamsEditor } from "../components/ParamsEditor";
|
|
9
|
+
import { logDividerGradient, logSeverityBackground, logStyleTokens } from "./logStyles";
|
|
9
10
|
const cashLogs = new Map();
|
|
10
11
|
const datumConst = {
|
|
11
12
|
map: cashLogs,
|
|
@@ -172,11 +173,8 @@ export function PageLogs({ update }) {
|
|
|
172
173
|
return _jsx(Main, {});
|
|
173
174
|
}
|
|
174
175
|
function Message({ logs }) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
red = 255;
|
|
178
|
-
return _jsxs("div", { className: "testAnime", style: { width: "200px", color: "rgb(255,255,255)", height: "auto", marginTop: "10px", borderRight: "5px solid #5D9FFA", background: `rgb(${red},73,35)` }, children: [_jsx("p", { style: { textAlign: "center", fontSize: "10px", marginBottom: "1px" }, children: "notification" }), _jsx("hr", { style: {
|
|
179
|
-
backgroundImage: "linear-gradient(to right, transparent, rgba(255, 255, 255, 1), transparent)",
|
|
176
|
+
return _jsxs("div", { className: "testAnime", style: { width: "200px", color: logStyleTokens.text, height: "auto", marginTop: "10px", borderRight: `5px solid ${logStyleTokens.accent}`, background: logSeverityBackground(logs.var) }, children: [_jsx("p", { style: { textAlign: "center", fontSize: "10px", marginBottom: "1px" }, children: "notification" }), _jsx("hr", { style: {
|
|
177
|
+
backgroundImage: logDividerGradient(),
|
|
180
178
|
border: 0,
|
|
181
179
|
height: "1px",
|
|
182
180
|
margin: "0 0 0 0",
|
|
@@ -208,8 +206,8 @@ export function MessageEventLogs({ zIndex }) {
|
|
|
208
206
|
const tr = [...Object.values(tt)].reverse().slice(0, 10);
|
|
209
207
|
return _jsxs("div", { style: { maxHeight: "50vh", position: "absolute", right: "1px", zIndex }, children: [_jsx("div", { onClick: () => { setting.params.show = !setting.params.show; renderBy(tt); }, style: { margin: 3, padding: 3, right: 0, position: "absolute", zIndex: 120,
|
|
210
208
|
...setting.params.show ?
|
|
211
|
-
{ background:
|
|
212
|
-
{ background:
|
|
209
|
+
{ background: logStyleTokens.toggleBg, fontSize: "25px" } :
|
|
210
|
+
{ background: logStyleTokens.toggleOffBg }
|
|
213
211
|
}, children: setting.params.show ? "X" : "log" }), _jsx("div", { children: setting.params.show ? tr : null })] });
|
|
214
212
|
}
|
|
215
213
|
const defPageBase = {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { createContext, useContext, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import { AgGridReact } from 'ag-grid-react';
|
|
4
|
+
import { logDividerGradient, logSeverityBackground, logStyleTokens } from './logStyles';
|
|
4
5
|
/** -----------------------------
|
|
5
6
|
* 2. memoryGetOrCreate function -
|
|
6
7
|
* loads and saves data in localStorage.
|
|
@@ -185,36 +186,31 @@ export function LogsNotifications() {
|
|
|
185
186
|
if (!showMessages) {
|
|
186
187
|
// If popups are hidden, show only "log"
|
|
187
188
|
return (_jsx("div", { style: { position: 'absolute', right: 10, top: 10, zIndex: 999 }, children: _jsx("div", { style: {
|
|
188
|
-
background:
|
|
189
|
+
background: logStyleTokens.toggleOffBg,
|
|
189
190
|
padding: '6px 10px',
|
|
190
191
|
cursor: 'pointer'
|
|
191
192
|
}, onClick: () => setShowMessages(true), children: "log" }) }));
|
|
192
193
|
}
|
|
193
194
|
// Otherwise render the current notification list
|
|
194
195
|
return (_jsxs("div", { style: { position: 'absolute', right: 10, top: 10, zIndex: 999 }, children: [_jsx("div", { style: {
|
|
195
|
-
background:
|
|
196
|
+
background: logStyleTokens.toggleBg,
|
|
196
197
|
fontSize: '20px',
|
|
197
198
|
padding: '6px 10px',
|
|
198
199
|
cursor: 'pointer'
|
|
199
|
-
}, onClick: () => setShowMessages(false), children: "X" }), _jsx("div", { children: notifications.slice(0, 10).map(({ id, log }) => {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
border: 0,
|
|
214
|
-
height: 1,
|
|
215
|
-
margin: 0
|
|
216
|
-
} }), _jsx("div", { style: { textAlign: 'right', marginRight: 10 }, children: typeof log.txt === 'object' ? JSON.stringify(log.txt) : log.txt }), _jsx("p", { style: { textAlign: 'right', marginRight: 10 }, children: new Date(log.time).toLocaleDateString() })] }, id));
|
|
217
|
-
}) })] }));
|
|
200
|
+
}, onClick: () => setShowMessages(false), children: "X" }), _jsx("div", { children: notifications.slice(0, 10).map(({ id, log }) => (_jsxs("div", { className: "testAnime example-exit", style: {
|
|
201
|
+
width: 200,
|
|
202
|
+
color: logStyleTokens.text,
|
|
203
|
+
marginTop: 10,
|
|
204
|
+
borderRight: `5px solid ${logStyleTokens.accent}`,
|
|
205
|
+
backgroundColor: logSeverityBackground(log.var),
|
|
206
|
+
padding: 8,
|
|
207
|
+
wordWrap: 'break-word'
|
|
208
|
+
}, children: [_jsx("p", { style: { textAlign: 'center', fontSize: 10, marginBottom: 1 }, children: "notification" }), _jsx("hr", { style: {
|
|
209
|
+
backgroundImage: logDividerGradient(),
|
|
210
|
+
border: 0,
|
|
211
|
+
height: 1,
|
|
212
|
+
margin: 0
|
|
213
|
+
} }), _jsx("div", { style: { textAlign: 'right', marginRight: 10 }, children: typeof log.txt === 'object' ? JSON.stringify(log.txt) : log.txt }), _jsx("p", { style: { textAlign: 'right', marginRight: 10 }, children: new Date(log.time).toLocaleDateString() })] }, id))) })] }));
|
|
218
214
|
}
|
|
219
215
|
/** -----------------------------
|
|
220
216
|
* 8. LogsSettings component
|
|
@@ -231,15 +227,15 @@ export function LogsSettings() {
|
|
|
231
227
|
*/
|
|
232
228
|
export function MainPage() {
|
|
233
229
|
const [currentTab, setCurrentTab] = useState('table');
|
|
234
|
-
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%' }, children: [_jsxs("div", { style: { display: 'flex', gap: 8, padding: 10, background:
|
|
235
|
-
backgroundColor: currentTab === 'table' ?
|
|
236
|
-
color:
|
|
230
|
+
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%' }, children: [_jsxs("div", { style: { display: 'flex', gap: 8, padding: 10, background: logStyleTokens.tabNavBg }, children: [_jsx("button", { onClick: () => setCurrentTab('table'), style: {
|
|
231
|
+
backgroundColor: currentTab === 'table' ? logStyleTokens.tabActiveBg : logStyleTokens.tabBg,
|
|
232
|
+
color: logStyleTokens.tabText,
|
|
237
233
|
border: 'none',
|
|
238
234
|
padding: '8px',
|
|
239
235
|
cursor: 'pointer'
|
|
240
236
|
}, children: "Log table" }), _jsx("button", { onClick: () => setCurrentTab('settings'), style: {
|
|
241
|
-
backgroundColor: currentTab === 'settings' ?
|
|
242
|
-
color:
|
|
237
|
+
backgroundColor: currentTab === 'settings' ? logStyleTokens.tabActiveBg : logStyleTokens.tabBg,
|
|
238
|
+
color: logStyleTokens.tabText,
|
|
243
239
|
border: 'none',
|
|
244
240
|
padding: '8px',
|
|
245
241
|
cursor: 'pointer'
|
|
@@ -31,11 +31,12 @@ declare function MenuProgress({ data }: {
|
|
|
31
31
|
/*******************************************************
|
|
32
32
|
* Main menu element with onClick and counters
|
|
33
33
|
*******************************************************/
|
|
34
|
-
declare function MenuElement({ data: item, toLeft, className, update, }: {
|
|
34
|
+
declare function MenuElement({ data: item, toLeft, className, update, open, }: {
|
|
35
35
|
data: Pick<MenuItemStrict, "onClick" | "active" | "name" | "getStatus">;
|
|
36
36
|
toLeft: boolean;
|
|
37
37
|
className?: (active?: boolean) => string;
|
|
38
38
|
update: () => void;
|
|
39
|
+
open?: boolean;
|
|
39
40
|
}): ReactElement;
|
|
40
41
|
/*******************************************************
|
|
41
42
|
* Menu renders the popup menu with support for
|
|
@@ -29,7 +29,7 @@ function MenuProgress({ data }) {
|
|
|
29
29
|
/*******************************************************
|
|
30
30
|
* Main menu element with onClick and counters
|
|
31
31
|
*******************************************************/
|
|
32
|
-
function MenuElement({ data: item, toLeft, className, update, }) {
|
|
32
|
+
function MenuElement({ data: item, toLeft, className, update, open, }) {
|
|
33
33
|
const unsubOk = useRef(null);
|
|
34
34
|
const unsubErr = useRef(null);
|
|
35
35
|
useEffect(() => {
|
|
@@ -42,8 +42,9 @@ function MenuElement({ data: item, toLeft, className, update, }) {
|
|
|
42
42
|
};
|
|
43
43
|
}, []);
|
|
44
44
|
const [progress, setProgress] = useState(null);
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
const active = open || item.active?.();
|
|
46
|
+
return (_jsx("div", { className: className?.(active) ||
|
|
47
|
+
"MenuR " + (active ? "toButtonA" : "toButton"), style: { float: toLeft ? "left" : "right" }, onClick: () => {
|
|
47
48
|
if (!item.onClick)
|
|
48
49
|
return;
|
|
49
50
|
const result = item?.onClick?.(item);
|
|
@@ -111,12 +112,12 @@ function MenuElement({ data: item, toLeft, className, update, }) {
|
|
|
111
112
|
? item.name
|
|
112
113
|
: item.name(item.getStatus?.()), progress && _jsx(MenuProgress, { data: progress })] }) }));
|
|
113
114
|
}
|
|
114
|
-
const MenuItemWrapper = ({ item, index, update, className, isLeftAligned, leftPos, menuElement,
|
|
115
|
+
const MenuItemWrapper = ({ item, index, update, className, isLeftAligned, leftPos, menuElement, open, setOpenIndex, }) => {
|
|
115
116
|
const [childMenu, setChildMenu] = useState([]);
|
|
116
117
|
const [asyncFuncElement, setAsyncFuncElement] = useState(null);
|
|
117
118
|
const [onFocusMenu, setOnFocusMenu] = useState([]);
|
|
118
119
|
useEffect(() => {
|
|
119
|
-
if (
|
|
120
|
+
if (open && item.next) {
|
|
120
121
|
let alive = true; // guard: do not set state after unmount or item change
|
|
121
122
|
const result = item.next();
|
|
122
123
|
if (result instanceof Promise) {
|
|
@@ -133,9 +134,9 @@ const MenuItemWrapper = ({ item, index, update, className, isLeftAligned, leftPo
|
|
|
133
134
|
else {
|
|
134
135
|
setChildMenu([]);
|
|
135
136
|
}
|
|
136
|
-
}, [
|
|
137
|
+
}, [open, item.next]);
|
|
137
138
|
useEffect(() => {
|
|
138
|
-
if (
|
|
139
|
+
if (open && item.func) {
|
|
139
140
|
let alive = true;
|
|
140
141
|
const result = item.func();
|
|
141
142
|
if (result instanceof Promise) {
|
|
@@ -152,9 +153,9 @@ const MenuItemWrapper = ({ item, index, update, className, isLeftAligned, leftPo
|
|
|
152
153
|
else {
|
|
153
154
|
setAsyncFuncElement(null);
|
|
154
155
|
}
|
|
155
|
-
}, [
|
|
156
|
+
}, [open, item.func]);
|
|
156
157
|
useEffect(() => {
|
|
157
|
-
if (
|
|
158
|
+
if (open && item.onFocus) {
|
|
158
159
|
let alive = true;
|
|
159
160
|
const result = item.onFocus();
|
|
160
161
|
if (result instanceof Promise) {
|
|
@@ -171,33 +172,31 @@ const MenuItemWrapper = ({ item, index, update, className, isLeftAligned, leftPo
|
|
|
171
172
|
else {
|
|
172
173
|
setOnFocusMenu([]);
|
|
173
174
|
}
|
|
174
|
-
}, [
|
|
175
|
+
}, [open, item.onFocus]);
|
|
175
176
|
const onMouseEnter = () => {
|
|
176
|
-
if (
|
|
177
|
+
if (open)
|
|
177
178
|
return;
|
|
178
|
-
|
|
179
|
-
it.status = j === index;
|
|
180
|
-
});
|
|
181
|
-
update();
|
|
179
|
+
setOpenIndex(index);
|
|
182
180
|
};
|
|
181
|
+
const viewItem = open == !!item.status ? item : { ...item, status: open };
|
|
183
182
|
return (_jsxs("div", { className: "toLine", onMouseEnter: onMouseEnter, children: [menuElement
|
|
184
|
-
? menuElement(
|
|
183
|
+
? menuElement(viewItem)
|
|
185
184
|
: item.menuElement?.({
|
|
186
185
|
toLeft: isLeftAligned,
|
|
187
|
-
data:
|
|
186
|
+
data: viewItem,
|
|
188
187
|
className,
|
|
189
188
|
update,
|
|
190
|
-
}) ?? (_jsx(MenuElement, { toLeft: isLeftAligned, data:
|
|
189
|
+
}) ?? (_jsx(MenuElement, { toLeft: isLeftAligned, data: viewItem, className: className, update: update, open: open })), _jsxs("div", { children: [open && childMenu.length > 0 && (_jsx("div", { style: { position: "relative" }, children: _jsx(Menu, { data: childMenu, coordinate: {
|
|
191
190
|
x: 3,
|
|
192
191
|
y: 0,
|
|
193
192
|
toLeft: isLeftAligned,
|
|
194
193
|
left: leftPos,
|
|
195
|
-
} }) })),
|
|
194
|
+
} }) })), open && asyncFuncElement && (_jsx("div", { style: { position: "relative" }, children: _jsx(Menu, { menu: () => asyncFuncElement, data: [], coordinate: {
|
|
196
195
|
x: 3,
|
|
197
196
|
y: 0,
|
|
198
197
|
toLeft: isLeftAligned,
|
|
199
198
|
left: leftPos,
|
|
200
|
-
} }) })),
|
|
199
|
+
} }) })), open && onFocusMenu.length > 0 && (_jsx("div", { style: { position: "relative" }, children: _jsx(Menu, { data: onFocusMenu, coordinate: {
|
|
201
200
|
x: 3,
|
|
202
201
|
y: 0,
|
|
203
202
|
toLeft: isLeftAligned,
|
|
@@ -209,6 +208,14 @@ export function Menu({ coordinate = { x: 0, y: 0, toLeft: false, left: 0 }, data
|
|
|
209
208
|
const update = () => forceUpdate((p) => !p);
|
|
210
209
|
const refMenu = useRef(null);
|
|
211
210
|
const dataMemo = useMemo(() => data.filter(Boolean), [data, data.length]);
|
|
211
|
+
const initialActiveIndex = () => {
|
|
212
|
+
const i = dataMemo.findIndex(item => item.status);
|
|
213
|
+
return i == -1 ? null : i;
|
|
214
|
+
};
|
|
215
|
+
const [activeIndex, setActiveIndex] = useState(initialActiveIndex);
|
|
216
|
+
useEffect(() => {
|
|
217
|
+
setActiveIndex(prev => prev != null && dataMemo[prev] ? prev : initialActiveIndex());
|
|
218
|
+
}, [dataMemo]);
|
|
212
219
|
const [top, setTop] = useState(coordinate.y);
|
|
213
220
|
const [leftPos, setLeftPos] = useState(coordinate.x);
|
|
214
221
|
const [menuWidth, setMenuWidth] = useState(0);
|
|
@@ -250,6 +257,6 @@ export function Menu({ coordinate = { x: 0, y: 0, toLeft: false, left: 0 }, data
|
|
|
250
257
|
...alignStyle,
|
|
251
258
|
}, children: menu
|
|
252
259
|
? menu(dataMemo)
|
|
253
|
-
: dataMemo.map((item, i, arr) => (_jsx(MenuItemWrapper, { item: item, index: i, update: update, className: className, isLeftAligned: isLeftAligned, leftPos: leftPos, menuElement: menuElement,
|
|
260
|
+
: dataMemo.map((item, i, arr) => (_jsx(MenuItemWrapper, { item: item, index: i, update: update, className: className, isLeftAligned: isLeftAligned, leftPos: leftPos, menuElement: menuElement, open: activeIndex === i, setOpenIndex: setActiveIndex }, typeof item.name === "string" ? item.name : i))) }));
|
|
254
261
|
}
|
|
255
262
|
export { MenuProgress, MenuElement };
|
|
@@ -17,6 +17,7 @@ export declare const tokens: {
|
|
|
17
17
|
readonly itemColor: "#fff";
|
|
18
18
|
readonly itemHoverColor: "#101010";
|
|
19
19
|
readonly itemHoverBgColor: "#fff";
|
|
20
|
+
readonly outlineColor: "#007bff";
|
|
20
21
|
readonly shadow: "0 0 20px 14px rgba(34, 60, 80, 0.2)";
|
|
21
22
|
};
|
|
22
23
|
/** FloatingWindow window chrome (--wnd-*). Defaults = legacy look; apps re-skin via :root[data-theme].
|
|
@@ -55,7 +56,7 @@ export declare const tokens: {
|
|
|
55
56
|
readonly shadow: "0 12px 40px rgba(0, 0, 0, 0.5)";
|
|
56
57
|
/** var(--color-bg-light) in CSS */
|
|
57
58
|
readonly navBg: "#17202e";
|
|
58
|
-
readonly navWidth: "
|
|
59
|
+
readonly navWidth: "220px";
|
|
59
60
|
};
|
|
60
61
|
/** Toolbar (createToolbar) chrome (--tb-*). Dark defaults; apps re-skin via :root[data-theme]. */
|
|
61
62
|
readonly tb: {
|
|
@@ -74,6 +75,18 @@ export declare const tokens: {
|
|
|
74
75
|
readonly popRadius: "8px";
|
|
75
76
|
readonly popShadow: "0 8px 28px rgba(0, 0, 0, 0.5)";
|
|
76
77
|
};
|
|
78
|
+
/** Logs chrome (--logs-*). Defaults preserve the old logger look; apps re-skin via CSS vars. */
|
|
79
|
+
readonly logs: {
|
|
80
|
+
readonly notificationText: "#fff";
|
|
81
|
+
readonly notificationAccent: "#5D9FFA";
|
|
82
|
+
readonly toggleBg: "rgb(58, 58, 58)";
|
|
83
|
+
readonly toggleOffBg: "rgb(144, 60, 60)";
|
|
84
|
+
readonly divider: "rgba(255, 255, 255, 1)";
|
|
85
|
+
readonly tabNavBg: "#333";
|
|
86
|
+
readonly tabBg: "#444";
|
|
87
|
+
readonly tabActiveBg: "#666";
|
|
88
|
+
readonly tabText: "#fff";
|
|
89
|
+
};
|
|
77
90
|
readonly font: {
|
|
78
91
|
readonly family: "Roboto";
|
|
79
92
|
readonly sizeBase: "12px";
|
|
@@ -19,6 +19,7 @@ export const tokens = {
|
|
|
19
19
|
itemColor: '#fff',
|
|
20
20
|
itemHoverColor: '#101010',
|
|
21
21
|
itemHoverBgColor: '#fff',
|
|
22
|
+
outlineColor: '#007bff',
|
|
22
23
|
shadow: '0 0 20px 14px rgba(34, 60, 80, 0.2)',
|
|
23
24
|
},
|
|
24
25
|
/** FloatingWindow window chrome (--wnd-*). Defaults = legacy look; apps re-skin via :root[data-theme].
|
|
@@ -57,7 +58,7 @@ export const tokens = {
|
|
|
57
58
|
shadow: '0 12px 40px rgba(0, 0, 0, 0.5)',
|
|
58
59
|
/** var(--color-bg-light) in CSS */
|
|
59
60
|
navBg: '#17202e',
|
|
60
|
-
navWidth: '
|
|
61
|
+
navWidth: '220px',
|
|
61
62
|
},
|
|
62
63
|
/** Toolbar (createToolbar) chrome (--tb-*). Dark defaults; apps re-skin via :root[data-theme]. */
|
|
63
64
|
tb: {
|
|
@@ -76,6 +77,18 @@ export const tokens = {
|
|
|
76
77
|
popRadius: '8px',
|
|
77
78
|
popShadow: '0 8px 28px rgba(0, 0, 0, 0.5)',
|
|
78
79
|
},
|
|
80
|
+
/** Logs chrome (--logs-*). Defaults preserve the old logger look; apps re-skin via CSS vars. */
|
|
81
|
+
logs: {
|
|
82
|
+
notificationText: '#fff',
|
|
83
|
+
notificationAccent: '#5D9FFA',
|
|
84
|
+
toggleBg: 'rgb(58, 58, 58)',
|
|
85
|
+
toggleOffBg: 'rgb(144, 60, 60)',
|
|
86
|
+
divider: 'rgba(255, 255, 255, 1)',
|
|
87
|
+
tabNavBg: '#333',
|
|
88
|
+
tabBg: '#444',
|
|
89
|
+
tabActiveBg: '#666',
|
|
90
|
+
tabText: '#fff',
|
|
91
|
+
},
|
|
79
92
|
font: {
|
|
80
93
|
family: 'Roboto',
|
|
81
94
|
sizeBase: '12px',
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type SearchHistoryState = {
|
|
2
|
+
items: string[];
|
|
3
|
+
};
|
|
4
|
+
export type SearchHistoryApi = ReturnType<typeof createSearchHistory>;
|
|
5
|
+
export declare function createSearchHistory(opts: {
|
|
6
|
+
key: string;
|
|
7
|
+
max?: number;
|
|
8
|
+
}): {
|
|
9
|
+
readonly items: string[];
|
|
10
|
+
use(): string[];
|
|
11
|
+
add(value: string): void;
|
|
12
|
+
remove(value: string): void;
|
|
13
|
+
clear(): void;
|
|
14
|
+
};
|