@motiadev/plugin-logs 0.15.5-beta.174-069425 → 0.15.5-beta.174-384621

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/components/logs-page.tsx","../src/types/log.ts"],"sourcesContent":[],"mappings":";;;cAOa,gBAAQ,kBAAA,CAAA,GAAA,CAAA;;;KCPT,GAAA;;;EDOC,IAAA,EAAA,MA6FZ;;;;ECpGW,KAAA,EAAG,MAAA,EAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/components/logs-page.tsx","../src/types/log.ts"],"sourcesContent":[],"mappings":";;;cAWa,gBAAQ,kBAAA,CAAA,GAAA,CAAA;;;KCXT,GAAA;;;EDWC,IAAA,EAAA,MA0IZ;;;;ECrJW,KAAA,EAAG,MAAA,EAAA"}
package/dist/index.js CHANGED
@@ -1,9 +1,10 @@
1
- import { Stream } from "@motiadev/stream-client-browser";
2
- import { create } from "zustand";
3
- import { c } from "react/compiler-runtime";
4
- import { Button, Input, LevelDot, Sidebar, Table, TableBody, TableCell, TableRow, cn } from "@motiadev/ui";
1
+ import { Button, Input, LevelDot, Sidebar, cn } from "@motiadev/ui";
2
+ import { useVirtualizer } from "@tanstack/react-virtual";
5
3
  import { Search, Trash, X } from "lucide-react";
6
- import { useMemo, useState } from "react";
4
+ import { useMemo, useRef, useState } from "react";
5
+ import { c } from "react/compiler-runtime";
6
+ import { useStreamGroup } from "@motiadev/stream-client-react";
7
+ import { create } from "zustand";
7
8
  import ReactJson from "react18-json-view";
8
9
  import "react18-json-view/src/dark.css";
9
10
  import "react18-json-view/src/style.css";
@@ -13,11 +14,9 @@ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
13
14
  const useLogsStore = create()((set) => ({
14
15
  logs: [],
15
16
  selectedLogId: void 0,
16
- addLog: (log) => set((state) => {
17
- if (state.logs.find((l) => l.id === log.id)) return state;
18
- return { logs: [log, ...state.logs] };
17
+ setLogs: (logs) => set(() => {
18
+ return { logs: [...Array.isArray(logs) ? logs : []].reverse() };
19
19
  }),
20
- setLogs: (logs) => set({ logs: [...logs].reverse() }),
21
20
  resetLogs: () => {
22
21
  set({ logs: [] });
23
22
  },
@@ -25,18 +24,31 @@ const useLogsStore = create()((set) => ({
25
24
  }));
26
25
 
27
26
  //#endregion
28
- //#region src/utils/init-log-listener.ts
27
+ //#region src/hooks/use-logs-stream.ts
29
28
  const streamName = "__motia.logs";
30
29
  const groupId = "default";
31
- const type = "log";
32
- const initLogListener = () => {
33
- const subscription = new Stream(window.location.origin.replace("http", "ws")).subscribeGroup(streamName, groupId);
34
- const store = useLogsStore.getState();
35
- subscription.addChangeListener((logs) => {
36
- if (logs) store.setLogs(logs);
37
- });
38
- subscription.onEvent(type, store.addLog);
30
+ const useLogsStream = () => {
31
+ const $ = c(3);
32
+ if ($[0] !== "07a885904ac486305cf3393f9436986fc94bf9cf48b0fc1eba70eadcdf49ee10") {
33
+ for (let $i = 0; $i < 3; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
34
+ $[0] = "07a885904ac486305cf3393f9436986fc94bf9cf48b0fc1eba70eadcdf49ee10";
35
+ }
36
+ const setData = useLogsStore(_temp);
37
+ let t0;
38
+ if ($[1] !== setData) {
39
+ t0 = {
40
+ streamName,
41
+ groupId,
42
+ setData
43
+ };
44
+ $[1] = setData;
45
+ $[2] = t0;
46
+ } else t0 = $[2];
47
+ useStreamGroup(t0);
39
48
  };
49
+ function _temp(state) {
50
+ return state.setLogs;
51
+ }
40
52
 
41
53
  //#endregion
42
54
  //#region src/utils/format-timestamp.ts
@@ -124,217 +136,125 @@ const LogDetail = ({ log, onClose }) => {
124
136
 
125
137
  //#endregion
126
138
  //#region src/components/logs-page.tsx
139
+ const ROW_HEIGHT = 40;
127
140
  const LogsPage = () => {
128
- const $ = c(42);
129
- if ($[0] !== "600f8e0c583358e32584786b1654032f3bbb95a52b3fd260dec41b34a3fad7d7") {
130
- for (let $i = 0; $i < 42; $i += 1) $[$i] = Symbol.for("react.memo_cache_sentinel");
131
- $[0] = "600f8e0c583358e32584786b1654032f3bbb95a52b3fd260dec41b34a3fad7d7";
132
- }
133
- const logs = useLogsStore(_temp);
134
- const resetLogs = useLogsStore(_temp2);
135
- const selectedLogId = useLogsStore(_temp3);
136
- const selectLogId = useLogsStore(_temp4);
137
- let t0;
138
- if ($[1] !== logs || $[2] !== selectedLogId) {
139
- t0 = selectedLogId ? logs.find((log) => log.id === selectedLogId) : void 0;
140
- $[1] = logs;
141
- $[2] = selectedLogId;
142
- $[3] = t0;
143
- } else t0 = $[3];
144
- const selectedLog = t0;
141
+ useLogsStream();
142
+ const logs = useLogsStore((state) => state.logs);
143
+ const resetLogs = useLogsStore((state_0) => state_0.resetLogs);
144
+ const selectedLogId = useLogsStore((state_1) => state_1.selectedLogId);
145
+ const selectLogId = useLogsStore((state_2) => state_2.selectLogId);
146
+ const selectedLog = useMemo(() => selectedLogId ? logs.find((log) => log.id === selectedLogId) : void 0, [logs, selectedLogId]);
145
147
  const [search, setSearch] = useState("");
146
- let t1;
147
- if ($[4] !== logs || $[5] !== search) {
148
- let t2$1;
149
- if ($[7] !== search) {
150
- t2$1 = (log_0) => String(log_0.msg || "").toLowerCase().includes(search.toLowerCase()) || String(log_0.traceId || "").toLowerCase().includes(search.toLowerCase()) || String(log_0.step || "").toLowerCase().includes(search.toLowerCase());
151
- $[7] = search;
152
- $[8] = t2$1;
153
- } else t2$1 = $[8];
154
- t1 = logs.filter(t2$1);
155
- $[4] = logs;
156
- $[5] = search;
157
- $[6] = t1;
158
- } else t1 = $[6];
159
- const filteredLogs = t1;
160
- let t2;
161
- if ($[9] === Symbol.for("react.memo_cache_sentinel")) {
162
- t2 = (e) => setSearch(e.target.value);
163
- $[9] = t2;
164
- } else t2 = $[9];
165
- let t3;
166
- if ($[10] !== search) {
167
- t3 = /* @__PURE__ */ jsx(Input, {
168
- variant: "shade",
169
- value: search,
170
- onChange: t2,
171
- className: "px-9! font-medium",
172
- placeholder: "Search by Trace ID or Message"
173
- });
174
- $[10] = search;
175
- $[11] = t3;
176
- } else t3 = $[11];
177
- let t4;
178
- if ($[12] === Symbol.for("react.memo_cache_sentinel")) {
179
- t4 = /* @__PURE__ */ jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground/50" });
180
- $[12] = t4;
181
- } else t4 = $[12];
182
- let t5;
183
- if ($[13] === Symbol.for("react.memo_cache_sentinel")) {
184
- t5 = /* @__PURE__ */ jsx(X, {
185
- className: "cursor-pointer absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground/50 hover:text-muted-foreground",
186
- onClick: () => setSearch("")
187
- });
188
- $[13] = t5;
189
- } else t5 = $[13];
190
- let t6;
191
- if ($[14] !== t3) {
192
- t6 = /* @__PURE__ */ jsxs("div", {
193
- className: "flex-1 relative",
194
- children: [
195
- t3,
196
- t4,
197
- t5
198
- ]
199
- });
200
- $[14] = t3;
201
- $[15] = t6;
202
- } else t6 = $[15];
203
- let t7;
204
- if ($[16] === Symbol.for("react.memo_cache_sentinel")) {
205
- t7 = /* @__PURE__ */ jsx(Trash, {});
206
- $[16] = t7;
207
- } else t7 = $[16];
208
- let t8;
209
- if ($[17] !== resetLogs) {
210
- t8 = /* @__PURE__ */ jsxs(Button, {
211
- variant: "default",
212
- onClick: resetLogs,
213
- className: "h-[34px]",
214
- children: [t7, " Clear"]
148
+ const filteredLogs = useMemo(() => {
149
+ return logs.filter((log_0) => {
150
+ return String(log_0.msg || "").toLowerCase().includes(search.toLowerCase()) || String(log_0.traceId || "").toLowerCase().includes(search.toLowerCase()) || String(log_0.step || "").toLowerCase().includes(search.toLowerCase());
215
151
  });
216
- $[17] = resetLogs;
217
- $[18] = t8;
218
- } else t8 = $[18];
219
- let t9;
220
- if ($[19] !== t6 || $[20] !== t8) {
221
- t9 = /* @__PURE__ */ jsxs("div", {
152
+ }, [logs, search]);
153
+ const scrollContainerRef = useRef(null);
154
+ const virtualizer = useVirtualizer({
155
+ count: filteredLogs.length,
156
+ getScrollElement: () => scrollContainerRef.current,
157
+ estimateSize: () => ROW_HEIGHT,
158
+ overscan: 5
159
+ });
160
+ const virtualItems = virtualizer.getVirtualItems();
161
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
162
+ className: "grid grid-rows-[auto_1fr] h-full",
163
+ "data-testid": "logs-container",
164
+ children: [/* @__PURE__ */ jsxs("div", {
222
165
  className: "flex p-2 border-b gap-2",
223
166
  "data-testid": "logs-search-container",
224
- children: [t6, t8]
225
- });
226
- $[19] = t6;
227
- $[20] = t8;
228
- $[21] = t9;
229
- } else t9 = $[21];
230
- let t10;
231
- if ($[22] !== filteredLogs || $[23] !== selectLogId || $[24] !== selectedLogId) {
232
- let t11$1;
233
- if ($[26] !== selectLogId || $[27] !== selectedLogId) {
234
- t11$1 = (log_1, index) => /* @__PURE__ */ jsxs(TableRow, {
235
- "data-testid": "log-row",
236
- className: cn("font-mono font-semibold cursor-pointer border-0", {
237
- "bg-muted-foreground/10 hover:bg-muted-foreground/20": selectedLogId === log_1.id,
238
- "hover:bg-muted-foreground/10": selectedLogId !== log_1.id
239
- }),
240
- onClick: () => selectLogId(log_1.id),
167
+ children: [/* @__PURE__ */ jsxs("div", {
168
+ className: "flex-1 relative",
241
169
  children: [
242
- /* @__PURE__ */ jsxs(TableCell, {
243
- "data-testid": `time-${index}`,
244
- className: "whitespace-nowrap flex items-center gap-2 text-muted-foreground",
245
- children: [/* @__PURE__ */ jsx(LevelDot, { level: log_1.level }), formatTimestamp(log_1.time)]
246
- }),
247
- /* @__PURE__ */ jsx(TableCell, {
248
- "data-testid": `trace-${log_1.traceId}`,
249
- className: "whitespace-nowrap cursor-pointer hover:text-primary text-muted-foreground",
250
- onClick: () => setSearch(log_1.traceId),
251
- children: log_1.traceId
252
- }),
253
- /* @__PURE__ */ jsx(TableCell, {
254
- "data-testid": `step-${index}`,
255
- "aria-label": log_1.step,
256
- className: "whitespace-nowrap",
257
- children: log_1.step
170
+ /* @__PURE__ */ jsx(Input, {
171
+ variant: "shade",
172
+ value: search,
173
+ onChange: (e) => setSearch(e.target.value),
174
+ className: "px-9! font-medium",
175
+ placeholder: "Search by Trace ID or Message"
258
176
  }),
259
- /* @__PURE__ */ jsx(TableCell, {
260
- "data-testid": `msg-${index}`,
261
- "aria-label": log_1.msg,
262
- className: "whitespace-nowrap max-w-[500px] truncate w-full",
263
- children: log_1.msg
177
+ /* @__PURE__ */ jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground/50" }),
178
+ /* @__PURE__ */ jsx(X, {
179
+ className: "cursor-pointer absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground/50 hover:text-muted-foreground",
180
+ onClick: () => setSearch("")
264
181
  })
265
182
  ]
266
- }, index);
267
- $[26] = selectLogId;
268
- $[27] = selectedLogId;
269
- $[28] = t11$1;
270
- } else t11$1 = $[28];
271
- t10 = filteredLogs.map(t11$1);
272
- $[22] = filteredLogs;
273
- $[23] = selectLogId;
274
- $[24] = selectedLogId;
275
- $[25] = t10;
276
- } else t10 = $[25];
277
- let t11;
278
- if ($[29] !== t10) {
279
- t11 = /* @__PURE__ */ jsx(Table, { children: /* @__PURE__ */ jsx(TableBody, {
280
- className: "font-mono font-medium",
281
- children: t10
282
- }) });
283
- $[29] = t10;
284
- $[30] = t11;
285
- } else t11 = $[30];
286
- let t12;
287
- if ($[31] !== t11 || $[32] !== t9) {
288
- t12 = /* @__PURE__ */ jsxs("div", {
289
- className: "grid grid-rows-[auto_1fr] h-full",
290
- "data-testid": "logs-container",
291
- children: [t9, t11]
292
- });
293
- $[31] = t11;
294
- $[32] = t9;
295
- $[33] = t12;
296
- } else t12 = $[33];
297
- let t13;
298
- if ($[34] !== selectLogId) {
299
- t13 = () => selectLogId(void 0);
300
- $[34] = selectLogId;
301
- $[35] = t13;
302
- } else t13 = $[35];
303
- let t14;
304
- if ($[36] !== selectedLog || $[37] !== t13) {
305
- t14 = /* @__PURE__ */ jsx(LogDetail, {
306
- log: selectedLog,
307
- onClose: t13
308
- });
309
- $[36] = selectedLog;
310
- $[37] = t13;
311
- $[38] = t14;
312
- } else t14 = $[38];
313
- let t15;
314
- if ($[39] !== t12 || $[40] !== t14) {
315
- t15 = /* @__PURE__ */ jsxs(Fragment, { children: [t12, t14] });
316
- $[39] = t12;
317
- $[40] = t14;
318
- $[41] = t15;
319
- } else t15 = $[41];
320
- return t15;
183
+ }), /* @__PURE__ */ jsxs(Button, {
184
+ variant: "default",
185
+ onClick: resetLogs,
186
+ className: "h-[34px]",
187
+ children: [/* @__PURE__ */ jsx(Trash, {}), " Clear"]
188
+ })]
189
+ }), /* @__PURE__ */ jsx("div", {
190
+ ref: scrollContainerRef,
191
+ className: "overflow-auto h-full",
192
+ children: /* @__PURE__ */ jsx("div", {
193
+ className: "relative w-full",
194
+ style: { height: virtualizer.getTotalSize() },
195
+ children: virtualItems.map((virtualRow) => {
196
+ const log_1 = filteredLogs[virtualRow.index];
197
+ if (!log_1) return null;
198
+ const index = virtualRow.index;
199
+ return /* @__PURE__ */ jsxs("div", {
200
+ "data-testid": "log-row",
201
+ "data-index": virtualRow.index,
202
+ ref: virtualizer.measureElement,
203
+ role: "row",
204
+ tabIndex: 0,
205
+ className: cn("absolute left-0 w-full flex items-center font-mono font-semibold cursor-pointer text-sm", {
206
+ "bg-muted-foreground/10 hover:bg-muted-foreground/20": selectedLogId === log_1.id,
207
+ "hover:bg-muted-foreground/10": selectedLogId !== log_1.id
208
+ }),
209
+ style: {
210
+ height: ROW_HEIGHT,
211
+ transform: `translateY(${virtualRow.start}px)`
212
+ },
213
+ onClick: () => selectLogId(log_1.id),
214
+ onKeyDown: (e_0) => {
215
+ if (e_0.key === "Enter" || e_0.key === " ") selectLogId(log_1.id);
216
+ },
217
+ children: [
218
+ /* @__PURE__ */ jsxs("div", {
219
+ "data-testid": `time-${index}`,
220
+ role: "cell",
221
+ className: "whitespace-nowrap flex items-center gap-2 text-muted-foreground p-2 shrink-0",
222
+ children: [/* @__PURE__ */ jsx(LevelDot, { level: log_1.level }), formatTimestamp(log_1.time)]
223
+ }),
224
+ /* @__PURE__ */ jsx("button", {
225
+ type: "button",
226
+ "data-testid": `trace-${log_1.traceId}`,
227
+ className: "whitespace-nowrap cursor-pointer hover:text-primary text-muted-foreground p-2 shrink-0 bg-transparent border-0 text-left font-mono font-semibold text-sm",
228
+ onClick: (e_1) => {
229
+ e_1.stopPropagation();
230
+ setSearch(log_1.traceId);
231
+ },
232
+ children: log_1.traceId
233
+ }),
234
+ /* @__PURE__ */ jsx("div", {
235
+ "data-testid": `step-${index}`,
236
+ role: "cell",
237
+ title: log_1.step,
238
+ className: "whitespace-nowrap p-2 shrink-0",
239
+ children: log_1.step
240
+ }),
241
+ /* @__PURE__ */ jsx("div", {
242
+ "data-testid": `msg-${index}`,
243
+ role: "cell",
244
+ title: log_1.msg,
245
+ className: "whitespace-nowrap max-w-[500px] truncate p-2 flex-1",
246
+ children: log_1.msg
247
+ })
248
+ ]
249
+ }, log_1.id);
250
+ })
251
+ })
252
+ })]
253
+ }), /* @__PURE__ */ jsx(LogDetail, {
254
+ log: selectedLog,
255
+ onClose: () => selectLogId(void 0)
256
+ })] });
321
257
  };
322
- function _temp(state) {
323
- return state.logs;
324
- }
325
- function _temp2(state_0) {
326
- return state_0.resetLogs;
327
- }
328
- function _temp3(state_1) {
329
- return state_1.selectedLogId;
330
- }
331
- function _temp4(state_2) {
332
- return state_2.selectLogId;
333
- }
334
-
335
- //#endregion
336
- //#region src/index.ts
337
- initLogListener();
338
258
 
339
259
  //#endregion
340
260
  export { LogsPage };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["create","Log","LogsState","logs","selectedLogId","addLog","log","setLogs","resetLogs","selectLogId","logId","useLogsStore","set","undefined","state","find","l","id","reverse","Stream","useLogsStore","Log","streamName","groupId","type","initLogListener","stream","window","location","origin","replace","subscription","subscribeGroup","store","getState","addChangeListener","logs","setLogs","onEvent","addLog","formatTimestamp","time","date","Date","Number","toLocaleDateString","year","undefined","month","day","toLocaleTimeString","hour","minute","second","hourCycle","getMilliseconds","toString","padStart","LevelDot","Sidebar","X","React","useMemo","useState","ReactJson","Log","formatTimestamp","Props","log","onClose","defaultProps","LogDetail","FC","hasOtherProps","setHasOtherProps","otherPropsObject","otherProps","Object","keys","filter","key","includes","length","reduce","acc","Record","icon","onClick","label","value","level","time","step","flows","join","traceId","Button","cn","Input","LevelDot","Table","TableBody","TableCell","TableRow","Search","Trash","X","useMemo","useState","useLogsStore","formatTimestamp","LogDetail","LogsPage","$","_c","$i","Symbol","for","logs","_temp","resetLogs","_temp2","selectedLogId","_temp3","selectLogId","_temp4","t0","find","log","id","undefined","selectedLog","search","setSearch","t1","t2","log_0","String","msg","toLowerCase","includes","traceId","step","filter","filteredLogs","e","target","value","t3","t4","t5","t6","t7","t8","t9","t10","t11","log_1","index","level","time","map","t12","t13","t14","t15","state","state_0","state_1","state_2","initLogListener","LogsPage","Log"],"sources":["../src/stores/use-logs-store.ts","../src/utils/init-log-listener.ts","../src/utils/format-timestamp.ts","../src/components/log-detail.tsx","../src/components/logs-page.tsx","../src/index.ts"],"sourcesContent":["import { create } from 'zustand'\nimport type { Log } from '../types/log'\n\nexport type LogsState = {\n logs: Log[]\n selectedLogId?: string\n addLog: (log: Log) => void\n setLogs: (logs: Log[]) => void\n resetLogs: () => void\n selectLogId: (logId?: string) => void\n}\n\nexport const useLogsStore = create<LogsState>()((set) => ({\n logs: [],\n selectedLogId: undefined,\n addLog: (log) =>\n set((state) => {\n if (state.logs.find((l) => l.id === log.id)) {\n return state\n }\n return {\n logs: [log, ...state.logs],\n }\n }),\n setLogs: (logs) =>\n set({\n logs: [...logs].reverse(),\n }),\n resetLogs: () => {\n set({ logs: [] })\n },\n selectLogId: (logId) => set({ selectedLogId: logId }),\n}))\n","import { Stream } from '@motiadev/stream-client-browser'\nimport { useLogsStore } from '../stores/use-logs-store'\nimport type { Log } from '../types/log'\n\nconst streamName = '__motia.logs'\nconst groupId = 'default'\nconst type = 'log'\n\nexport const initLogListener = () => {\n const stream = new Stream(window.location.origin.replace('http', 'ws'))\n const subscription = stream.subscribeGroup<Log>(streamName, groupId)\n const store = useLogsStore.getState()\n\n subscription.addChangeListener((logs) => {\n if (logs) {\n store.setLogs(logs)\n }\n })\n\n subscription.onEvent(type, store.addLog)\n}\n","export const formatTimestamp = (time: number) => {\n const date = new Date(Number(time))\n return `${date.toLocaleDateString('en-US', { year: undefined, month: 'short', day: '2-digit' })}, ${date.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', second: '2-digit', hourCycle: 'h24' })}.${date.getMilliseconds().toString().padStart(3, '0')}`\n}\n","import { LevelDot, Sidebar } from '@motiadev/ui'\nimport { X } from 'lucide-react'\nimport type React from 'react'\nimport { useMemo, useState } from 'react'\nimport ReactJson from 'react18-json-view'\nimport 'react18-json-view/src/dark.css'\nimport 'react18-json-view/src/style.css'\nimport type { Log } from '../types/log'\nimport { formatTimestamp } from '../utils/format-timestamp'\n\ntype Props = {\n log?: Log\n onClose: () => void\n}\n\nconst defaultProps = ['id', 'msg', 'time', 'level', 'step', 'flows', 'traceId']\n\nexport const LogDetail: React.FC<Props> = ({ log, onClose }) => {\n const [hasOtherProps, setHasOtherProps] = useState(false)\n\n const otherPropsObject = useMemo(() => {\n if (!log) {\n return null\n }\n\n const otherProps = Object.keys(log ?? {}).filter((key) => !defaultProps.includes(key))\n setHasOtherProps(otherProps.length > 0)\n\n return otherProps.reduce(\n (acc, key) => {\n acc[key] = log[key]\n return acc\n },\n {} as Record<string, unknown>,\n )\n }, [log])\n\n if (!log) {\n return null\n }\n\n return (\n <Sidebar\n onClose={onClose}\n title=\"Logs Details\"\n subtitle=\"Details including custom properties\"\n actions={[{ icon: <X />, onClick: onClose, label: 'Close' }]}\n details={[\n {\n label: 'Level',\n value: (\n <div className=\"flex items-center gap-2\">\n <LevelDot level={log.level} />\n <div className=\"capitalize\">{log.level}</div>\n </div>\n ),\n },\n { label: 'Time', value: formatTimestamp(log.time) },\n { label: 'Step', value: log.step },\n { label: 'Flows', value: log.flows.join(', ') },\n { label: 'Trace ID', value: log.traceId },\n ]}\n >\n {hasOtherProps && <ReactJson src={otherPropsObject} theme=\"default\" enableClipboard />}\n </Sidebar>\n )\n}\n","import { Button, cn, Input, LevelDot, Table, TableBody, TableCell, TableRow } from '@motiadev/ui'\nimport { Search, Trash, X } from 'lucide-react'\nimport { useMemo, useState } from 'react'\nimport { useLogsStore } from '../stores/use-logs-store'\nimport { formatTimestamp } from '../utils/format-timestamp'\nimport { LogDetail } from './log-detail'\n\nexport const LogsPage = () => {\n const logs = useLogsStore((state) => state.logs)\n const resetLogs = useLogsStore((state) => state.resetLogs)\n const selectedLogId = useLogsStore((state) => state.selectedLogId)\n const selectLogId = useLogsStore((state) => state.selectLogId)\n const selectedLog = useMemo(\n () => (selectedLogId ? logs.find((log) => log.id === selectedLogId) : undefined),\n [logs, selectedLogId],\n )\n\n const [search, setSearch] = useState('')\n const filteredLogs = useMemo(() => {\n return logs.filter((log) => {\n return (\n String(log.msg || '')\n .toLowerCase()\n .includes(search.toLowerCase()) ||\n String(log.traceId || '')\n .toLowerCase()\n .includes(search.toLowerCase()) ||\n String(log.step || '')\n .toLowerCase()\n .includes(search.toLowerCase())\n )\n })\n }, [logs, search])\n\n return (\n <>\n <div className=\"grid grid-rows-[auto_1fr] h-full\" data-testid=\"logs-container\">\n <div className=\"flex p-2 border-b gap-2\" data-testid=\"logs-search-container\">\n <div className=\"flex-1 relative\">\n <Input\n variant=\"shade\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n className=\"px-9! font-medium\"\n placeholder=\"Search by Trace ID or Message\"\n />\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground/50\" />\n <X\n className=\"cursor-pointer absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground/50 hover:text-muted-foreground\"\n onClick={() => setSearch('')}\n />\n </div>\n <Button variant=\"default\" onClick={resetLogs} className=\"h-[34px]\">\n <Trash /> Clear\n </Button>\n </div>\n <Table>\n <TableBody className=\"font-mono font-medium\">\n {filteredLogs.map((log, index) => (\n <TableRow\n data-testid=\"log-row\"\n className={cn('font-mono font-semibold cursor-pointer border-0', {\n 'bg-muted-foreground/10 hover:bg-muted-foreground/20': selectedLogId === log.id,\n 'hover:bg-muted-foreground/10': selectedLogId !== log.id,\n })}\n key={index}\n onClick={() => selectLogId(log.id)}\n >\n <TableCell\n data-testid={`time-${index}`}\n className=\"whitespace-nowrap flex items-center gap-2 text-muted-foreground\"\n >\n <LevelDot level={log.level} />\n {formatTimestamp(log.time)}\n </TableCell>\n <TableCell\n data-testid={`trace-${log.traceId}`}\n className=\"whitespace-nowrap cursor-pointer hover:text-primary text-muted-foreground\"\n onClick={() => setSearch(log.traceId)}\n >\n {log.traceId}\n </TableCell>\n <TableCell data-testid={`step-${index}`} aria-label={log.step} className=\"whitespace-nowrap\">\n {log.step}\n </TableCell>\n <TableCell\n data-testid={`msg-${index}`}\n aria-label={log.msg}\n className=\"whitespace-nowrap max-w-[500px] truncate w-full\"\n >\n {log.msg}\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n <LogDetail log={selectedLog} onClose={() => selectLogId(undefined)} />\n </>\n )\n}\n","import { initLogListener } from './utils/init-log-listener'\n\nexport { LogsPage } from './components/logs-page'\nexport type { Log } from './types/log'\n\ninitLogListener()\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["create","Log","LogsState","logs","selectedLogId","setLogs","resetLogs","selectLogId","logId","useLogsStore","set","undefined","safeLogs","Array","isArray","reverse","useStreamGroup","useLogsStore","Log","streamName","groupId","useLogsStream","$","_c","$i","Symbol","for","setData","_temp","t0","state","setLogs","formatTimestamp","time","date","Date","Number","toLocaleDateString","year","undefined","month","day","toLocaleTimeString","hour","minute","second","hourCycle","getMilliseconds","toString","padStart","LevelDot","Sidebar","X","React","useMemo","useState","ReactJson","Log","formatTimestamp","Props","log","onClose","defaultProps","LogDetail","FC","hasOtherProps","setHasOtherProps","otherPropsObject","otherProps","Object","keys","filter","key","includes","length","reduce","acc","Record","icon","onClick","label","value","level","time","step","flows","join","traceId","Button","cn","Input","LevelDot","useVirtualizer","Search","Trash","X","useMemo","useRef","useState","useLogsStream","useLogsStore","formatTimestamp","LogDetail","ROW_HEIGHT","LogsPage","logs","state","resetLogs","selectedLogId","selectLogId","selectedLog","find","log","id","undefined","search","setSearch","filteredLogs","filter","String","msg","toLowerCase","includes","traceId","step","scrollContainerRef","HTMLDivElement","virtualizer","count","length","getScrollElement","current","estimateSize","overscan","virtualItems","getVirtualItems","e","target","value","height","getTotalSize","map","virtualRow","index","measureElement","transform","start","key","level","time","stopPropagation"],"sources":["../src/stores/use-logs-store.ts","../src/hooks/use-logs-stream.ts","../src/utils/format-timestamp.ts","../src/components/log-detail.tsx","../src/components/logs-page.tsx"],"sourcesContent":["import { create } from 'zustand'\nimport type { Log } from '../types/log'\n\nexport type LogsState = {\n logs: Log[]\n selectedLogId?: string\n setLogs: (logs: Log[]) => void\n resetLogs: () => void\n selectLogId: (logId?: string) => void\n}\n\nexport const useLogsStore = create<LogsState>()((set) => ({\n logs: [],\n selectedLogId: undefined,\n setLogs: (logs: Log[]) =>\n set(() => {\n const safeLogs = Array.isArray(logs) ? logs : []\n return {\n logs: [...safeLogs].reverse(),\n }\n }),\n resetLogs: () => {\n set({ logs: [] })\n },\n selectLogId: (logId) => set({ selectedLogId: logId }),\n}))\n","import { useStreamGroup } from '@motiadev/stream-client-react'\nimport { useLogsStore } from '../stores/use-logs-store'\nimport type { Log } from '../types/log'\n\nconst streamName = '__motia.logs'\nconst groupId = 'default'\n\nexport const useLogsStream = () => {\n const setData = useLogsStore((state) => state.setLogs)\n\n useStreamGroup<Log>({\n streamName,\n groupId,\n setData,\n })\n}\n","export const formatTimestamp = (time: number) => {\n const date = new Date(Number(time))\n return `${date.toLocaleDateString('en-US', { year: undefined, month: 'short', day: '2-digit' })}, ${date.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', second: '2-digit', hourCycle: 'h24' })}.${date.getMilliseconds().toString().padStart(3, '0')}`\n}\n","import { LevelDot, Sidebar } from '@motiadev/ui'\nimport { X } from 'lucide-react'\nimport type React from 'react'\nimport { useMemo, useState } from 'react'\nimport ReactJson from 'react18-json-view'\nimport 'react18-json-view/src/dark.css'\nimport 'react18-json-view/src/style.css'\nimport type { Log } from '../types/log'\nimport { formatTimestamp } from '../utils/format-timestamp'\n\ntype Props = {\n log?: Log\n onClose: () => void\n}\n\nconst defaultProps = ['id', 'msg', 'time', 'level', 'step', 'flows', 'traceId']\n\nexport const LogDetail: React.FC<Props> = ({ log, onClose }) => {\n const [hasOtherProps, setHasOtherProps] = useState(false)\n\n const otherPropsObject = useMemo(() => {\n if (!log) {\n return null\n }\n\n const otherProps = Object.keys(log ?? {}).filter((key) => !defaultProps.includes(key))\n setHasOtherProps(otherProps.length > 0)\n\n return otherProps.reduce(\n (acc, key) => {\n acc[key] = log[key]\n return acc\n },\n {} as Record<string, unknown>,\n )\n }, [log])\n\n if (!log) {\n return null\n }\n\n return (\n <Sidebar\n onClose={onClose}\n title=\"Logs Details\"\n subtitle=\"Details including custom properties\"\n actions={[{ icon: <X />, onClick: onClose, label: 'Close' }]}\n details={[\n {\n label: 'Level',\n value: (\n <div className=\"flex items-center gap-2\">\n <LevelDot level={log.level} />\n <div className=\"capitalize\">{log.level}</div>\n </div>\n ),\n },\n { label: 'Time', value: formatTimestamp(log.time) },\n { label: 'Step', value: log.step },\n { label: 'Flows', value: log.flows.join(', ') },\n { label: 'Trace ID', value: log.traceId },\n ]}\n >\n {hasOtherProps && <ReactJson src={otherPropsObject} theme=\"default\" enableClipboard />}\n </Sidebar>\n )\n}\n","import { Button, cn, Input, LevelDot } from '@motiadev/ui'\nimport { useVirtualizer } from '@tanstack/react-virtual'\nimport { Search, Trash, X } from 'lucide-react'\nimport { useMemo, useRef, useState } from 'react'\nimport { useLogsStream } from '../hooks/use-logs-stream'\nimport { useLogsStore } from '../stores/use-logs-store'\nimport { formatTimestamp } from '../utils/format-timestamp'\nimport { LogDetail } from './log-detail'\n\nconst ROW_HEIGHT = 40\n\nexport const LogsPage = () => {\n useLogsStream()\n\n const logs = useLogsStore((state) => state.logs)\n const resetLogs = useLogsStore((state) => state.resetLogs)\n const selectedLogId = useLogsStore((state) => state.selectedLogId)\n const selectLogId = useLogsStore((state) => state.selectLogId)\n const selectedLog = useMemo(\n () => (selectedLogId ? logs.find((log) => log.id === selectedLogId) : undefined),\n [logs, selectedLogId],\n )\n\n const [search, setSearch] = useState('')\n const filteredLogs = useMemo(() => {\n return logs.filter((log) => {\n return (\n String(log.msg || '')\n .toLowerCase()\n .includes(search.toLowerCase()) ||\n String(log.traceId || '')\n .toLowerCase()\n .includes(search.toLowerCase()) ||\n String(log.step || '')\n .toLowerCase()\n .includes(search.toLowerCase())\n )\n })\n }, [logs, search])\n\n const scrollContainerRef = useRef<HTMLDivElement>(null)\n\n const virtualizer = useVirtualizer({\n count: filteredLogs.length,\n getScrollElement: () => scrollContainerRef.current,\n estimateSize: () => ROW_HEIGHT,\n overscan: 5,\n })\n\n const virtualItems = virtualizer.getVirtualItems()\n\n return (\n <>\n <div className=\"grid grid-rows-[auto_1fr] h-full\" data-testid=\"logs-container\">\n <div className=\"flex p-2 border-b gap-2\" data-testid=\"logs-search-container\">\n <div className=\"flex-1 relative\">\n <Input\n variant=\"shade\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n className=\"px-9! font-medium\"\n placeholder=\"Search by Trace ID or Message\"\n />\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground/50\" />\n <X\n className=\"cursor-pointer absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground/50 hover:text-muted-foreground\"\n onClick={() => setSearch('')}\n />\n </div>\n <Button variant=\"default\" onClick={resetLogs} className=\"h-[34px]\">\n <Trash /> Clear\n </Button>\n </div>\n <div ref={scrollContainerRef} className=\"overflow-auto h-full\">\n <div className=\"relative w-full\" style={{ height: virtualizer.getTotalSize() }}>\n {virtualItems.map((virtualRow) => {\n const log = filteredLogs[virtualRow.index]\n if (!log) return null\n const index = virtualRow.index\n return (\n <div\n data-testid=\"log-row\"\n data-index={virtualRow.index}\n ref={virtualizer.measureElement}\n key={log.id}\n role=\"row\"\n tabIndex={0}\n className={cn(\n 'absolute left-0 w-full flex items-center font-mono font-semibold cursor-pointer text-sm',\n {\n 'bg-muted-foreground/10 hover:bg-muted-foreground/20': selectedLogId === log.id,\n 'hover:bg-muted-foreground/10': selectedLogId !== log.id,\n },\n )}\n style={{\n height: ROW_HEIGHT,\n transform: `translateY(${virtualRow.start}px)`,\n }}\n onClick={() => selectLogId(log.id)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n selectLogId(log.id)\n }\n }}\n >\n <div\n data-testid={`time-${index}`}\n role=\"cell\"\n className=\"whitespace-nowrap flex items-center gap-2 text-muted-foreground p-2 shrink-0\"\n >\n <LevelDot level={log.level} />\n {formatTimestamp(log.time)}\n </div>\n <button\n type=\"button\"\n data-testid={`trace-${log.traceId}`}\n className=\"whitespace-nowrap cursor-pointer hover:text-primary text-muted-foreground p-2 shrink-0 bg-transparent border-0 text-left font-mono font-semibold text-sm\"\n onClick={(e) => {\n e.stopPropagation()\n setSearch(log.traceId)\n }}\n >\n {log.traceId}\n </button>\n <div\n data-testid={`step-${index}`}\n role=\"cell\"\n title={log.step}\n className=\"whitespace-nowrap p-2 shrink-0\"\n >\n {log.step}\n </div>\n <div\n data-testid={`msg-${index}`}\n role=\"cell\"\n title={log.msg}\n className=\"whitespace-nowrap max-w-[500px] truncate p-2 flex-1\"\n >\n {log.msg}\n </div>\n </div>\n )\n })}\n </div>\n </div>\n </div>\n <LogDetail log={selectedLog} onClose={() => selectLogId(undefined)} />\n </>\n )\n}\n"],"mappings":";;;;;;;;;;;;;AAWA,MAAaS,eAAeT,QAAmB,EAAEU,SAAS;CACxDP,MAAM,EAAE;CACRC,eAAeO;CACfN,UAAUF,SACRO,UAAU;AAER,SAAO,EACLP,MAAM,CAAC,GAFQU,MAAMC,QAAQX,KAAK,GAAGA,OAAO,EAAE,CAE3B,CAACY,SAAQ,EAC7B;GACD;CACJT,iBAAiB;AACfI,MAAI,EAAEP,MAAM,EAAA,EAAI,CAAC;;CAEnBI,cAAcC,UAAUE,IAAI,EAAEN,eAAeI,OAAO,CAAA;CACrD,EAAE;;;;ACrBH,MAAMW,aAAa;AACnB,MAAMC,UAAU;AAEhB,MAAaC,sBAAgB;CAAA,MAAAC,IAAAC,EAAA,EAAA;AAAA,KAAAD,EAAA,OAAA,oEAAA;AAAA,OAAA,IAAAE,KAAA,GAAAA,KAAA,GAAAA,MAAA,EAAAF,GAAAE,MAAAC,OAAAC,IAAA,4BAAA;AAAAJ,IAAA,KAAA;;CAC3B,MAAAK,UAAgBV,aAAaW,MAAyB;CAAA,IAAAC;AAAA,KAAAP,EAAA,OAAAK,SAAA;AAElCE,OAAA;GAAAV;GAAAC;GAAAO;GAInB;AAAAL,IAAA,KAAAK;AAAAL,IAAA,KAAAO;OAAAA,MAAAP,EAAA;AAJDN,gBAAoBa,GAIlB;;AAPyB,SAAAD,MAAAE,OAAA;AAAA,QACaA,MAAKC;;;;;ACR/C,MAAaC,mBAAmBC,SAAiB;CAC/C,MAAMC,OAAO,IAAIC,KAAKC,OAAOH,KAAK,CAAC;AACnC,QAAO,GAAGC,KAAKG,mBAAmB,SAAS;EAAEC,MAAMC;EAAWC,OAAO;EAASC,KAAK;EAAW,CAAC,CAAA,IAAKP,KAAKQ,mBAAmB,SAAS;EAAEC,MAAM;EAAWC,QAAQ;EAAWC,QAAQ;EAAWC,WAAW;EAAO,CAAC,CAAA,GAAIZ,KAAKa,iBAAiB,CAACC,UAAU,CAACC,SAAS,GAAG,IAAI;;;;;ACazQ,MAAMa,eAAe;CAAC;CAAM;CAAO;CAAQ;CAAS;CAAQ;CAAS;CAAU;AAE/E,MAAaC,aAA8B,EAAEH,KAAKC,cAAc;CAC9D,MAAM,CAACI,eAAeC,oBAAoBX,SAAS,MAAM;CAEzD,MAAMY,mBAAmBb,cAAc;AACrC,MAAI,CAACM,IACH,QAAO;EAGT,MAAMQ,aAAaC,OAAOC,KAAKV,OAAO,EAAE,CAAC,CAACW,QAAQC,QAAQ,CAACV,aAAaW,SAASD,IAAI,CAAC;AACtFN,mBAAiBE,WAAWM,SAAS,EAAE;AAEvC,SAAON,WAAWO,QACfC,KAAKJ,UAAQ;AACZI,OAAIJ,SAAOZ,IAAIY;AACf,UAAOI;KAET,EAAE,CACH;IACA,CAAChB,IAAI,CAAC;AAET,KAAI,CAACA,IACH,QAAO;AAGT,QACE,oBAAC;EACUC;EACT,OAAM;EACN,UAAS;EACT,SAAS,CAAC;GAAEiB,MAAM,oBAAC,MAAI;GAAEC,SAASlB;GAASmB,OAAO;GAAS,CAAC;EAC5D,SAAS;GACP;IACEA,OAAO;IACPC,OACE,qBAAC;KAAI,WAAU;gBACb,oBAAC,YAAS,OAAOrB,IAAIsB,QAAM,EAC3B,oBAAC;MAAI,WAAU;gBAActB,IAAIsB;OAAW;MACzC;IAER;GACD;IAAEF,OAAO;IAAQC,OAAOvB,gBAAgBE,IAAIuB,KAAI;IAAG;GACnD;IAAEH,OAAO;IAAQC,OAAOrB,IAAIwB;IAAM;GAClC;IAAEJ,OAAO;IAASC,OAAOrB,IAAIyB,MAAMC,KAAK,KAAI;IAAG;GAC/C;IAAEN,OAAO;IAAYC,OAAOrB,IAAI2B;IAAS;GAC1C;YAEAtB,iBAAiB,oBAAC;GAAU,KAAKE;GAAkB,OAAM;GAAU;IAAkB;GAC9E;;;;;ACvDd,MAAMoC,aAAa;AAEnB,MAAaC,iBAAiB;AAC5BL,gBAAe;CAEf,MAAMM,OAAOL,cAAcM,UAAUA,MAAMD,KAAK;CAChD,MAAME,YAAYP,cAAcM,YAAUA,QAAMC,UAAU;CAC1D,MAAMC,gBAAgBR,cAAcM,YAAUA,QAAME,cAAc;CAClE,MAAMC,cAAcT,cAAcM,YAAUA,QAAMG,YAAY;CAC9D,MAAMC,cAAcd,cACXY,gBAAgBH,KAAKM,MAAMC,QAAQA,IAAIC,OAAOL,cAAc,GAAGM,QACtE,CAACT,MAAMG,cACT,CAAC;CAED,MAAM,CAACO,QAAQC,aAAalB,SAAS,GAAG;CACxC,MAAMmB,eAAerB,cAAc;AACjC,SAAOS,KAAKa,QAAQN,UAAQ;AAC1B,UACEO,OAAOP,MAAIQ,OAAO,GAAG,CAClBC,aAAa,CACbC,SAASP,OAAOM,aAAa,CAAC,IACjCF,OAAOP,MAAIW,WAAW,GAAG,CACtBF,aAAa,CACbC,SAASP,OAAOM,aAAa,CAAC,IACjCF,OAAOP,MAAIY,QAAQ,GAAG,CACnBH,aAAa,CACbC,SAASP,OAAOM,aAAa,CAAC;IAEnC;IACD,CAAChB,MAAMU,OAAO,CAAC;CAElB,MAAMU,qBAAqB5B,OAAuB,KAAK;CAEvD,MAAM8B,cAAcnC,eAAe;EACjCoC,OAAOX,aAAaY;EACpBC,wBAAwBL,mBAAmBM;EAC3CC,oBAAoB7B;EACpB8B,UAAU;EACX,CAAC;CAEF,MAAMC,eAAeP,YAAYQ,iBAAiB;AAElD,QACE,4CACE,qBAAC;EAAI,WAAU;EAAmC,eAAY;aAC5D,qBAAC;GAAI,WAAU;GAA0B,eAAY;cACnD,qBAAC;IAAI,WAAU;;KACb,oBAAC;MACC,SAAQ;MACR,OAAOpB;MACP,WAAWqB,MAAMpB,UAAUoB,EAAEC,OAAOC,MAAM;MAC1C,WAAU;MACV,aAAY;OAA+B;KAE7C,oBAAC,UAAO,WAAU,8EAA2E;KAC7F,oBAAC;MACC,WAAU;MACV,eAAetB,UAAU,GAAG;OAAC;;KAE5B,EACL,qBAAC;IAAO,SAAQ;IAAU,SAAST;IAAW,WAAU;eACtD,oBAAC,UAAQ;KACH;IACL,EACL,oBAAC;GAAI,KAAKkB;GAAoB,WAAU;aACtC,oBAAC;IAAI,WAAU;IAAkB,OAAO,EAAEc,QAAQZ,YAAYa,cAAa,EAAG;cAC3EN,aAAaO,KAAKC,eAAe;KAChC,MAAM9B,QAAMK,aAAayB,WAAWC;AACpC,SAAI,CAAC/B,MAAK,QAAO;KACjB,MAAM+B,QAAQD,WAAWC;AACzB,YACE,qBAAC;MACC,eAAY;MACZ,cAAYD,WAAWC;MACvB,KAAKhB,YAAYiB;MAEjB,MAAK;MACL,UAAU;MACV,WAAWvD,GACT,2FACA;OACE,uDAAuDmB,kBAAkBI,MAAIC;OAC7E,gCAAgCL,kBAAkBI,MAAIC;OAE1D,CAAC;MACD,OAAO;OACL0B,QAAQpC;OACR0C,WAAW,cAAcH,WAAWI,MAAK;OAC1C;MACD,eAAerC,YAAYG,MAAIC,GAAG;MAClC,YAAYuB,QAAM;AAChB,WAAIA,IAAEW,QAAQ,WAAWX,IAAEW,QAAQ,IACjCtC,aAAYG,MAAIC,GAAG;;;OAIvB,qBAAC;QACC,eAAa,QAAQ8B;QACrB,MAAK;QACL,WAAU;mBAEV,oBAAC,YAAS,OAAO/B,MAAIoC,QAAM,EAC1B/C,gBAAgBW,MAAIqC,KAAK;SACvB;OACL,oBAAC;QACC,MAAK;QACL,eAAa,SAASrC,MAAIW;QAC1B,WAAU;QACV,UAAUa,QAAM;AACdA,aAAEc,iBAAiB;AACnBlC,mBAAUJ,MAAIW,QAAQ;;kBAGvBX,MAAIW;SACC;OACR,oBAAC;QACC,eAAa,QAAQoB;QACrB,MAAK;QACL,OAAO/B,MAAIY;QACX,WAAU;kBAETZ,MAAIY;SACF;OACL,oBAAC;QACC,eAAa,OAAOmB;QACpB,MAAK;QACL,OAAO/B,MAAIQ;QACX,WAAU;kBAETR,MAAIQ;SACF;;QAvDAR,MAAIC,GAwDL;MAER;KACC;IACF;GACF,EACL,oBAAC;EAAU,KAAKH;EAAa,eAAeD,YAAYK,OAAU;GAAC,IAClE"}
package/dist/styles.css CHANGED
@@ -8,6 +8,8 @@
8
8
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
9
9
  "Courier New", monospace;
10
10
  --spacing: 0.25rem;
11
+ --text-sm: 0.875rem;
12
+ --text-sm--line-height: calc(1.25 / 0.875);
11
13
  --font-weight-medium: 500;
12
14
  --font-weight-semibold: 600;
13
15
  --default-font-family: var(--font-sans);
@@ -178,6 +180,9 @@
178
180
  .right-3 {
179
181
  right: calc(var(--spacing) * 3);
180
182
  }
183
+ .left-0 {
184
+ left: calc(var(--spacing) * 0);
185
+ }
181
186
  .left-3 {
182
187
  left: calc(var(--spacing) * 3);
183
188
  }
@@ -208,10 +213,16 @@
208
213
  .flex-1 {
209
214
  flex: 1;
210
215
  }
216
+ .shrink-0 {
217
+ flex-shrink: 0;
218
+ }
211
219
  .-translate-y-1\/2 {
212
220
  --tw-translate-y: calc(calc(1/2 * 100%) * -1);
213
221
  translate: var(--tw-translate-x) var(--tw-translate-y);
214
222
  }
223
+ .transform {
224
+ transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);
225
+ }
215
226
  .cursor-pointer {
216
227
  cursor: pointer;
217
228
  }
@@ -229,6 +240,9 @@
229
240
  text-overflow: ellipsis;
230
241
  white-space: nowrap;
231
242
  }
243
+ .overflow-auto {
244
+ overflow: auto;
245
+ }
232
246
  .border-0 {
233
247
  border-style: var(--tw-border-style);
234
248
  border-width: 0px;
@@ -243,15 +257,25 @@
243
257
  background-color: color-mix(in oklab, var(--muted-foreground) 10%, transparent);
244
258
  }
245
259
  }
260
+ .bg-transparent {
261
+ background-color: transparent;
262
+ }
246
263
  .p-2 {
247
264
  padding: calc(var(--spacing) * 2);
248
265
  }
249
266
  .px-9\! {
250
267
  padding-inline: calc(var(--spacing) * 9) !important;
251
268
  }
269
+ .text-left {
270
+ text-align: left;
271
+ }
252
272
  .font-mono {
253
273
  font-family: var(--font-mono);
254
274
  }
275
+ .text-sm {
276
+ font-size: var(--text-sm);
277
+ line-height: var(--tw-leading, var(--text-sm--line-height));
278
+ }
255
279
  .font-medium {
256
280
  --tw-font-weight: var(--font-weight-medium);
257
281
  font-weight: var(--font-weight-medium);
@@ -653,6 +677,26 @@
653
677
  inherits: false;
654
678
  initial-value: 0;
655
679
  }
680
+ @property --tw-rotate-x {
681
+ syntax: "*";
682
+ inherits: false;
683
+ }
684
+ @property --tw-rotate-y {
685
+ syntax: "*";
686
+ inherits: false;
687
+ }
688
+ @property --tw-rotate-z {
689
+ syntax: "*";
690
+ inherits: false;
691
+ }
692
+ @property --tw-skew-x {
693
+ syntax: "*";
694
+ inherits: false;
695
+ }
696
+ @property --tw-skew-y {
697
+ syntax: "*";
698
+ inherits: false;
699
+ }
656
700
  @property --tw-border-style {
657
701
  syntax: "*";
658
702
  inherits: false;
@@ -668,6 +712,11 @@
668
712
  --tw-translate-x: 0;
669
713
  --tw-translate-y: 0;
670
714
  --tw-translate-z: 0;
715
+ --tw-rotate-x: initial;
716
+ --tw-rotate-y: initial;
717
+ --tw-rotate-z: initial;
718
+ --tw-skew-x: initial;
719
+ --tw-skew-y: initial;
671
720
  --tw-border-style: solid;
672
721
  --tw-font-weight: initial;
673
722
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@motiadev/plugin-logs",
3
- "version": "0.15.5-beta.174-069425",
3
+ "version": "0.15.5-beta.174-384621",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -13,15 +13,17 @@
13
13
  "dist"
14
14
  ],
15
15
  "dependencies": {
16
+ "@tanstack/react-virtual": "^3.13.12",
16
17
  "lucide-react": "^0.555.0",
17
18
  "react": "^19.2.0",
18
19
  "react18-json-view": "^0.2.9",
19
20
  "zustand": "^5.0.8"
20
21
  },
21
22
  "peerDependencies": {
22
- "@motiadev/stream-client-browser": "0.15.5-beta.174-069425",
23
- "@motiadev/ui": "0.15.5-beta.174-069425",
24
- "@motiadev/core": "0.15.5-beta.174-069425"
23
+ "@motiadev/core": "0.15.5-beta.174-384621",
24
+ "@motiadev/stream-client-react": "0.15.5-beta.174-384621",
25
+ "@motiadev/ui": "0.15.5-beta.174-384621",
26
+ "@motiadev/stream-client-browser": "0.15.5-beta.174-384621"
25
27
  },
26
28
  "devDependencies": {
27
29
  "@bosh-code/tsdown-plugin-inject-css": "^2.0.0",