tab-bridge 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +340 -1
- package/dist/index.d.cts +4 -2
- package/dist/index.d.ts +4 -2
- package/dist/{instance-5LIItazN.d.cts → instance-hvEUHx6i.d.cts} +1 -91
- package/dist/{instance-5LIItazN.d.ts → instance-hvEUHx6i.d.ts} +1 -91
- package/dist/jotai/index.cjs +44 -0
- package/dist/jotai/index.d.cts +68 -0
- package/dist/jotai/index.d.ts +68 -0
- package/dist/jotai/index.js +42 -0
- package/dist/options-DmHyGTL0.d.cts +93 -0
- package/dist/options-iN7Rnvwj.d.ts +93 -0
- package/dist/react/index.cjs +332 -0
- package/dist/react/index.d.cts +25 -2
- package/dist/react/index.d.ts +25 -2
- package/dist/react/index.js +333 -2
- package/dist/redux/index.cjs +99 -0
- package/dist/redux/index.d.cts +83 -0
- package/dist/redux/index.d.ts +83 -0
- package/dist/redux/index.js +96 -0
- package/dist/zustand/index.cjs +80 -0
- package/dist/zustand/index.d.cts +87 -0
- package/dist/zustand/index.d.ts +87 -0
- package/dist/zustand/index.js +78 -0
- package/package.json +52 -3
package/dist/react/index.cjs
CHANGED
|
@@ -235,8 +235,340 @@ function useTabSyncActions() {
|
|
|
235
235
|
[instance]
|
|
236
236
|
);
|
|
237
237
|
}
|
|
238
|
+
var FONT = "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace";
|
|
239
|
+
function containerStyle(position) {
|
|
240
|
+
const base = {
|
|
241
|
+
position: "fixed",
|
|
242
|
+
zIndex: 99999,
|
|
243
|
+
fontFamily: FONT,
|
|
244
|
+
fontSize: 12,
|
|
245
|
+
color: "#e4e4e7",
|
|
246
|
+
lineHeight: 1.5
|
|
247
|
+
};
|
|
248
|
+
if (position.includes("bottom")) base.bottom = 8;
|
|
249
|
+
else base.top = 8;
|
|
250
|
+
if (position.includes("right")) base.right = 8;
|
|
251
|
+
else base.left = 8;
|
|
252
|
+
return base;
|
|
253
|
+
}
|
|
254
|
+
var PANEL = {
|
|
255
|
+
width: 380,
|
|
256
|
+
maxHeight: 420,
|
|
257
|
+
background: "#18181b",
|
|
258
|
+
border: "1px solid #3f3f46",
|
|
259
|
+
borderRadius: 8,
|
|
260
|
+
overflow: "hidden",
|
|
261
|
+
display: "flex",
|
|
262
|
+
flexDirection: "column",
|
|
263
|
+
boxShadow: "0 8px 32px rgba(0,0,0,.45)"
|
|
264
|
+
};
|
|
265
|
+
var TOGGLE_BTN = {
|
|
266
|
+
background: "#18181b",
|
|
267
|
+
color: "#a1a1aa",
|
|
268
|
+
border: "1px solid #3f3f46",
|
|
269
|
+
borderRadius: 6,
|
|
270
|
+
padding: "4px 12px",
|
|
271
|
+
cursor: "pointer",
|
|
272
|
+
fontFamily: FONT,
|
|
273
|
+
fontSize: 11,
|
|
274
|
+
marginBottom: 4,
|
|
275
|
+
display: "flex",
|
|
276
|
+
alignItems: "center",
|
|
277
|
+
gap: 6
|
|
278
|
+
};
|
|
279
|
+
var TAB_BAR = {
|
|
280
|
+
display: "flex",
|
|
281
|
+
borderBottom: "1px solid #3f3f46",
|
|
282
|
+
background: "#27272a"
|
|
283
|
+
};
|
|
284
|
+
function tabBtnStyle(active) {
|
|
285
|
+
return {
|
|
286
|
+
flex: 1,
|
|
287
|
+
padding: "6px 0",
|
|
288
|
+
background: active ? "#18181b" : "transparent",
|
|
289
|
+
color: active ? "#fafafa" : "#a1a1aa",
|
|
290
|
+
border: "none",
|
|
291
|
+
borderBottom: active ? "2px solid #6366f1" : "2px solid transparent",
|
|
292
|
+
cursor: "pointer",
|
|
293
|
+
fontFamily: FONT,
|
|
294
|
+
fontSize: 11,
|
|
295
|
+
fontWeight: active ? 600 : 400
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
var BODY = {
|
|
299
|
+
flex: 1,
|
|
300
|
+
overflow: "auto",
|
|
301
|
+
padding: 10
|
|
302
|
+
};
|
|
303
|
+
var BADGE = {
|
|
304
|
+
display: "inline-block",
|
|
305
|
+
padding: "1px 5px",
|
|
306
|
+
borderRadius: 4,
|
|
307
|
+
fontSize: 10,
|
|
308
|
+
fontWeight: 600,
|
|
309
|
+
marginLeft: 6
|
|
310
|
+
};
|
|
311
|
+
function TabSyncDevTools({
|
|
312
|
+
position = "bottom-right",
|
|
313
|
+
defaultOpen = false
|
|
314
|
+
}) {
|
|
315
|
+
const instance = react.useContext(TabSyncContext);
|
|
316
|
+
const [is_open, setIsOpen] = react.useState(defaultOpen);
|
|
317
|
+
const [active_tab, setActiveTab] = react.useState("state");
|
|
318
|
+
if (!instance) return null;
|
|
319
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: containerStyle(position), children: [
|
|
320
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
321
|
+
"button",
|
|
322
|
+
{
|
|
323
|
+
type: "button",
|
|
324
|
+
style: TOGGLE_BTN,
|
|
325
|
+
onClick: () => setIsOpen((v) => !v),
|
|
326
|
+
children: [
|
|
327
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#6366f1", fontWeight: 700 }, children: "tab-bridge" }),
|
|
328
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: is_open ? "\u25BC" : "\u25B2" })
|
|
329
|
+
]
|
|
330
|
+
}
|
|
331
|
+
),
|
|
332
|
+
is_open && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: PANEL, children: [
|
|
333
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: TAB_BAR, children: ["state", "tabs", "log"].map((t) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
334
|
+
"button",
|
|
335
|
+
{
|
|
336
|
+
type: "button",
|
|
337
|
+
style: tabBtnStyle(active_tab === t),
|
|
338
|
+
onClick: () => setActiveTab(t),
|
|
339
|
+
children: t === "state" ? "State" : t === "tabs" ? "Tabs" : "Log"
|
|
340
|
+
},
|
|
341
|
+
t
|
|
342
|
+
)) }),
|
|
343
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: BODY, children: [
|
|
344
|
+
active_tab === "state" && /* @__PURE__ */ jsxRuntime.jsx(StatePanel, { instance }),
|
|
345
|
+
active_tab === "tabs" && /* @__PURE__ */ jsxRuntime.jsx(TabsPanel, { instance }),
|
|
346
|
+
active_tab === "log" && /* @__PURE__ */ jsxRuntime.jsx(LogPanel, { instance })
|
|
347
|
+
] })
|
|
348
|
+
] })
|
|
349
|
+
] });
|
|
350
|
+
}
|
|
351
|
+
function StatePanel({
|
|
352
|
+
instance
|
|
353
|
+
}) {
|
|
354
|
+
const [state, setState] = react.useState(() => instance.getAll());
|
|
355
|
+
const [editing, setEditing] = react.useState(false);
|
|
356
|
+
const [draft, setDraft] = react.useState("");
|
|
357
|
+
const [error, setError] = react.useState("");
|
|
358
|
+
react.useEffect(() => {
|
|
359
|
+
return instance.onChange((s) => setState(s));
|
|
360
|
+
}, [instance]);
|
|
361
|
+
const startEdit = react.useCallback(() => {
|
|
362
|
+
setDraft(JSON.stringify(state, null, 2));
|
|
363
|
+
setError("");
|
|
364
|
+
setEditing(true);
|
|
365
|
+
}, [state]);
|
|
366
|
+
const applyEdit = react.useCallback(() => {
|
|
367
|
+
try {
|
|
368
|
+
const parsed = JSON.parse(draft);
|
|
369
|
+
instance.patch(parsed);
|
|
370
|
+
setEditing(false);
|
|
371
|
+
setError("");
|
|
372
|
+
} catch (e) {
|
|
373
|
+
setError(String(e.message));
|
|
374
|
+
}
|
|
375
|
+
}, [draft, instance]);
|
|
376
|
+
if (editing) {
|
|
377
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
378
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
379
|
+
"textarea",
|
|
380
|
+
{
|
|
381
|
+
value: draft,
|
|
382
|
+
onChange: (e) => setDraft(e.target.value),
|
|
383
|
+
style: {
|
|
384
|
+
width: "100%",
|
|
385
|
+
minHeight: 180,
|
|
386
|
+
background: "#09090b",
|
|
387
|
+
color: "#e4e4e7",
|
|
388
|
+
border: "1px solid #3f3f46",
|
|
389
|
+
borderRadius: 4,
|
|
390
|
+
fontFamily: FONT,
|
|
391
|
+
fontSize: 11,
|
|
392
|
+
padding: 6,
|
|
393
|
+
resize: "vertical",
|
|
394
|
+
boxSizing: "border-box"
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
),
|
|
398
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#ef4444", fontSize: 11, marginTop: 4 }, children: error }),
|
|
399
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 6, marginTop: 6 }, children: [
|
|
400
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
401
|
+
"button",
|
|
402
|
+
{
|
|
403
|
+
type: "button",
|
|
404
|
+
onClick: applyEdit,
|
|
405
|
+
style: {
|
|
406
|
+
...TOGGLE_BTN,
|
|
407
|
+
background: "#6366f1",
|
|
408
|
+
color: "#fff",
|
|
409
|
+
border: "none",
|
|
410
|
+
margin: 0
|
|
411
|
+
},
|
|
412
|
+
children: "Apply"
|
|
413
|
+
}
|
|
414
|
+
),
|
|
415
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
416
|
+
"button",
|
|
417
|
+
{
|
|
418
|
+
type: "button",
|
|
419
|
+
onClick: () => setEditing(false),
|
|
420
|
+
style: { ...TOGGLE_BTN, margin: 0 },
|
|
421
|
+
children: "Cancel"
|
|
422
|
+
}
|
|
423
|
+
)
|
|
424
|
+
] })
|
|
425
|
+
] });
|
|
426
|
+
}
|
|
427
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
428
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
429
|
+
"pre",
|
|
430
|
+
{
|
|
431
|
+
style: {
|
|
432
|
+
margin: 0,
|
|
433
|
+
whiteSpace: "pre-wrap",
|
|
434
|
+
wordBreak: "break-all",
|
|
435
|
+
fontSize: 11,
|
|
436
|
+
color: "#a5f3fc"
|
|
437
|
+
},
|
|
438
|
+
children: JSON.stringify(state, null, 2)
|
|
439
|
+
}
|
|
440
|
+
),
|
|
441
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
442
|
+
"button",
|
|
443
|
+
{
|
|
444
|
+
type: "button",
|
|
445
|
+
onClick: startEdit,
|
|
446
|
+
style: { ...TOGGLE_BTN, marginTop: 8, margin: 0 },
|
|
447
|
+
children: "Edit State"
|
|
448
|
+
}
|
|
449
|
+
)
|
|
450
|
+
] });
|
|
451
|
+
}
|
|
452
|
+
function TabsPanel({
|
|
453
|
+
instance
|
|
454
|
+
}) {
|
|
455
|
+
const [tabs, setTabs] = react.useState(() => instance.getTabs());
|
|
456
|
+
react.useEffect(() => {
|
|
457
|
+
return instance.onTabChange((t) => setTabs(t));
|
|
458
|
+
}, [instance]);
|
|
459
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
460
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { color: "#a1a1aa", marginBottom: 6, fontSize: 11 }, children: [
|
|
461
|
+
tabs.length,
|
|
462
|
+
" active tab",
|
|
463
|
+
tabs.length !== 1 ? "s" : ""
|
|
464
|
+
] }),
|
|
465
|
+
tabs.map((tab) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
466
|
+
"div",
|
|
467
|
+
{
|
|
468
|
+
style: {
|
|
469
|
+
padding: "4px 6px",
|
|
470
|
+
marginBottom: 4,
|
|
471
|
+
background: tab.id === instance.id ? "#1e1b4b" : "#27272a",
|
|
472
|
+
borderRadius: 4,
|
|
473
|
+
display: "flex",
|
|
474
|
+
alignItems: "center",
|
|
475
|
+
flexWrap: "wrap",
|
|
476
|
+
gap: 4
|
|
477
|
+
},
|
|
478
|
+
children: [
|
|
479
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
480
|
+
"span",
|
|
481
|
+
{
|
|
482
|
+
style: {
|
|
483
|
+
fontFamily: FONT,
|
|
484
|
+
fontSize: 11,
|
|
485
|
+
color: "#e4e4e7",
|
|
486
|
+
wordBreak: "break-all"
|
|
487
|
+
},
|
|
488
|
+
children: tab.id.slice(0, 8)
|
|
489
|
+
}
|
|
490
|
+
),
|
|
491
|
+
tab.id === instance.id && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { ...BADGE, background: "#6366f1", color: "#fff" }, children: "you" }),
|
|
492
|
+
tab.isLeader && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { ...BADGE, background: "#f59e0b", color: "#000" }, children: "leader" })
|
|
493
|
+
]
|
|
494
|
+
},
|
|
495
|
+
tab.id
|
|
496
|
+
))
|
|
497
|
+
] });
|
|
498
|
+
}
|
|
499
|
+
var MAX_LOG = 200;
|
|
500
|
+
var next_log_id = 0;
|
|
501
|
+
function LogPanel({
|
|
502
|
+
instance
|
|
503
|
+
}) {
|
|
504
|
+
const [entries, setEntries] = react.useState([]);
|
|
505
|
+
const bottom_ref = react.useRef(null);
|
|
506
|
+
const push = react.useCallback((kind, detail) => {
|
|
507
|
+
setEntries((prev) => {
|
|
508
|
+
const entry = {
|
|
509
|
+
id: ++next_log_id,
|
|
510
|
+
time: Date.now(),
|
|
511
|
+
kind,
|
|
512
|
+
detail
|
|
513
|
+
};
|
|
514
|
+
const next = [...prev, entry];
|
|
515
|
+
return next.length > MAX_LOG ? next.slice(-MAX_LOG) : next;
|
|
516
|
+
});
|
|
517
|
+
}, []);
|
|
518
|
+
react.useEffect(() => {
|
|
519
|
+
const unsub_state = instance.onChange(
|
|
520
|
+
(_state, keys, meta) => {
|
|
521
|
+
const src = meta.isLocal ? "local" : `remote(${meta.sourceTabId.slice(0, 8)})`;
|
|
522
|
+
push("state", `${String(keys.join(", "))} [${src}]`);
|
|
523
|
+
}
|
|
524
|
+
);
|
|
525
|
+
const unsub_tabs = instance.onTabChange((tabs) => {
|
|
526
|
+
push("tab", `${tabs.length} tabs`);
|
|
527
|
+
});
|
|
528
|
+
return () => {
|
|
529
|
+
unsub_state();
|
|
530
|
+
unsub_tabs();
|
|
531
|
+
};
|
|
532
|
+
}, [instance, push]);
|
|
533
|
+
react.useEffect(() => {
|
|
534
|
+
bottom_ref.current?.scrollIntoView({ behavior: "smooth" });
|
|
535
|
+
}, [entries.length]);
|
|
536
|
+
const kind_color = {
|
|
537
|
+
state: "#a5f3fc",
|
|
538
|
+
tab: "#86efac",
|
|
539
|
+
leader: "#fde68a"
|
|
540
|
+
};
|
|
541
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { maxHeight: 300, overflow: "auto" }, children: [
|
|
542
|
+
entries.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#71717a", fontSize: 11 }, children: "Waiting for events..." }),
|
|
543
|
+
entries.map((e) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
544
|
+
"div",
|
|
545
|
+
{
|
|
546
|
+
style: { fontSize: 11, marginBottom: 2, display: "flex", gap: 6 },
|
|
547
|
+
children: [
|
|
548
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#71717a", flexShrink: 0 }, children: new Date(e.time).toLocaleTimeString() }),
|
|
549
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
550
|
+
"span",
|
|
551
|
+
{
|
|
552
|
+
style: {
|
|
553
|
+
color: kind_color[e.kind],
|
|
554
|
+
fontWeight: 600,
|
|
555
|
+
flexShrink: 0,
|
|
556
|
+
width: 38
|
|
557
|
+
},
|
|
558
|
+
children: e.kind
|
|
559
|
+
}
|
|
560
|
+
),
|
|
561
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#d4d4d8", wordBreak: "break-all" }, children: e.detail })
|
|
562
|
+
]
|
|
563
|
+
},
|
|
564
|
+
e.id
|
|
565
|
+
)),
|
|
566
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { ref: bottom_ref })
|
|
567
|
+
] });
|
|
568
|
+
}
|
|
238
569
|
|
|
239
570
|
exports.TabSyncContext = TabSyncContext;
|
|
571
|
+
exports.TabSyncDevTools = TabSyncDevTools;
|
|
240
572
|
exports.TabSyncProvider = TabSyncProvider;
|
|
241
573
|
exports.useIsLeader = useIsLeader;
|
|
242
574
|
exports.useLeaderInfo = useLeaderInfo;
|
package/dist/react/index.d.cts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import * as react from 'react';
|
|
3
3
|
import { ReactNode } from 'react';
|
|
4
|
-
import { T as TabSyncOptions
|
|
4
|
+
import { T as TabSyncOptions } from '../options-DmHyGTL0.cjs';
|
|
5
|
+
import { T as TabSyncInstance, R as RPCMap, a as TabInfo } from '../instance-hvEUHx6i.cjs';
|
|
5
6
|
|
|
6
7
|
interface TabSyncProviderProps<TState extends Record<string, unknown>> {
|
|
7
8
|
options: TabSyncOptions<TState>;
|
|
@@ -103,4 +104,26 @@ interface TabSyncActions<TState extends Record<string, unknown>> {
|
|
|
103
104
|
*/
|
|
104
105
|
declare function useTabSyncActions<TState extends Record<string, unknown> = Record<string, unknown>>(): TabSyncActions<TState>;
|
|
105
106
|
|
|
106
|
-
|
|
107
|
+
interface TabSyncDevToolsProps {
|
|
108
|
+
/** Panel position on screen. @default 'bottom-right' */
|
|
109
|
+
position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
|
|
110
|
+
/** Start expanded. @default false */
|
|
111
|
+
defaultOpen?: boolean;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Floating dev-tools panel that visualises tab-bridge state, active tabs,
|
|
115
|
+
* leader info, and an event log. Supports manual state editing.
|
|
116
|
+
*
|
|
117
|
+
* **Tree-shakeable** — if you never import `TabSyncDevTools`, it won't
|
|
118
|
+
* appear in your production bundle.
|
|
119
|
+
*
|
|
120
|
+
* Must be used inside a `<TabSyncProvider>`.
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```tsx
|
|
124
|
+
* {process.env.NODE_ENV === 'development' && <TabSyncDevTools />}
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
declare function TabSyncDevTools({ position, defaultOpen, }: TabSyncDevToolsProps): react_jsx_runtime.JSX.Element | null;
|
|
128
|
+
|
|
129
|
+
export { type TabSyncActions, TabSyncContext, TabSyncDevTools, type TabSyncDevToolsProps, TabSyncProvider, type TabSyncProviderProps, useIsLeader, useLeaderInfo, useTabSync, useTabSyncActions, useTabSyncSelector, useTabSyncValue, useTabs };
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import * as react from 'react';
|
|
3
3
|
import { ReactNode } from 'react';
|
|
4
|
-
import { T as TabSyncOptions
|
|
4
|
+
import { T as TabSyncOptions } from '../options-iN7Rnvwj.js';
|
|
5
|
+
import { T as TabSyncInstance, R as RPCMap, a as TabInfo } from '../instance-hvEUHx6i.js';
|
|
5
6
|
|
|
6
7
|
interface TabSyncProviderProps<TState extends Record<string, unknown>> {
|
|
7
8
|
options: TabSyncOptions<TState>;
|
|
@@ -103,4 +104,26 @@ interface TabSyncActions<TState extends Record<string, unknown>> {
|
|
|
103
104
|
*/
|
|
104
105
|
declare function useTabSyncActions<TState extends Record<string, unknown> = Record<string, unknown>>(): TabSyncActions<TState>;
|
|
105
106
|
|
|
106
|
-
|
|
107
|
+
interface TabSyncDevToolsProps {
|
|
108
|
+
/** Panel position on screen. @default 'bottom-right' */
|
|
109
|
+
position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
|
|
110
|
+
/** Start expanded. @default false */
|
|
111
|
+
defaultOpen?: boolean;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Floating dev-tools panel that visualises tab-bridge state, active tabs,
|
|
115
|
+
* leader info, and an event log. Supports manual state editing.
|
|
116
|
+
*
|
|
117
|
+
* **Tree-shakeable** — if you never import `TabSyncDevTools`, it won't
|
|
118
|
+
* appear in your production bundle.
|
|
119
|
+
*
|
|
120
|
+
* Must be used inside a `<TabSyncProvider>`.
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```tsx
|
|
124
|
+
* {process.env.NODE_ENV === 'development' && <TabSyncDevTools />}
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
declare function TabSyncDevTools({ position, defaultOpen, }: TabSyncDevToolsProps): react_jsx_runtime.JSX.Element | null;
|
|
128
|
+
|
|
129
|
+
export { type TabSyncActions, TabSyncContext, TabSyncDevTools, type TabSyncDevToolsProps, TabSyncProvider, type TabSyncProviderProps, useIsLeader, useLeaderInfo, useTabSync, useTabSyncActions, useTabSyncSelector, useTabSyncValue, useTabs };
|