@tanstack/query-devtools 5.21.3 → 5.23.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/build/index.js CHANGED
@@ -66,7 +66,7 @@ var TanstackQueryDevtools = class {
66
66
  if (this.#Component) {
67
67
  Devtools = this.#Component;
68
68
  } else {
69
- Devtools = lazy(() => import('./Devtools/SUV2KM2F.js'));
69
+ Devtools = lazy(() => import('./Devtools/QUGYIDRL.js'));
70
70
  this.#Component = Devtools;
71
71
  }
72
72
  setupStyleSheet(this.#styleNonce);
package/build/index.jsx CHANGED
@@ -70,7 +70,7 @@ var TanstackQueryDevtools = class {
70
70
  if (this.#Component) {
71
71
  Devtools = this.#Component;
72
72
  } else {
73
- Devtools = lazy(() => import("./Devtools/VAJS2OS4.jsx"));
73
+ Devtools = lazy(() => import("./Devtools/6EDF3GZQ.jsx"));
74
74
  this.#Component = Devtools;
75
75
  }
76
76
  setupStyleSheet(this.#styleNonce);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/query-devtools",
3
- "version": "5.21.3",
3
+ "version": "5.23.0",
4
4
  "description": "Developer tools to interact with and visualize the TanStack Query cache",
5
5
  "author": "tannerlinsley",
6
6
  "license": "MIT",
@@ -50,7 +50,7 @@
50
50
  "superjson": "^1.13.3",
51
51
  "tsup-preset-solid": "^2.2.0",
52
52
  "vite-plugin-solid": "^2.9.1",
53
- "@tanstack/query-core": "5.21.2"
53
+ "@tanstack/query-core": "5.22.2"
54
54
  },
55
55
  "scripts": {
56
56
  "clean": "rimraf ./build && rimraf ./coverage",
package/src/Devtools.tsx CHANGED
@@ -2,12 +2,14 @@ import {
2
2
  For,
3
3
  Show,
4
4
  batch,
5
+ createContext,
5
6
  createEffect,
6
7
  createMemo,
7
8
  createSignal,
8
9
  on,
9
10
  onCleanup,
10
11
  onMount,
12
+ useContext,
11
13
  } from 'solid-js'
12
14
  import { rankItem } from '@tanstack/match-sorter-utils'
13
15
  import { css } from 'goober'
@@ -17,6 +19,7 @@ import { Key } from '@solid-primitives/keyed'
17
19
  import { createLocalStorage } from '@solid-primitives/storage'
18
20
  import { createResizeObserver } from '@solid-primitives/resize-observer'
19
21
  import { DropdownMenu, RadioGroup } from '@kobalte/core'
22
+ import { Portal, delegateEvents } from 'solid-js/web'
20
23
  import { tokens } from './theme'
21
24
  import {
22
25
  convertRemToPixels,
@@ -42,6 +45,7 @@ import {
42
45
  Moon,
43
46
  Offline,
44
47
  PauseCircle,
48
+ PiPIcon,
45
49
  Search,
46
50
  Settings,
47
51
  Sun,
@@ -109,6 +113,161 @@ const [panelWidth, setPanelWidth] = createSignal(0)
109
113
 
110
114
  export type DevtoolsComponentType = Component<QueryDevtoolsProps>
111
115
 
116
+ interface PiPProviderProps {
117
+ children: JSX.Element
118
+ }
119
+
120
+ type PiPContextType = {
121
+ pipWindow: Window | null
122
+ requestPipWindow: (width: number, height: number) => Promise<void>
123
+ closePipWindow: () => void
124
+ }
125
+
126
+ const PiPContext = createContext<Accessor<PiPContextType> | undefined>(
127
+ undefined,
128
+ )
129
+
130
+ const PiPProvider = (props: PiPProviderProps) => {
131
+ // Expose pipWindow that is currently active
132
+ const [pipWindow, setPipWindow] = createSignal<Window | null>(null)
133
+
134
+ // Close pipWindow programmatically
135
+ const closePipWindow = () => {
136
+ const w = pipWindow()
137
+ if (w != null) {
138
+ w.close()
139
+ setPipWindow(null)
140
+ }
141
+ }
142
+
143
+ // Open new pipWindow
144
+ const requestPipWindow = async (width: number, height: number) => {
145
+ // We don't want to allow multiple requests.
146
+ if (pipWindow() != null) {
147
+ return
148
+ }
149
+
150
+ const pip = window.open('', '', `width=${width},height=${height},popup`)
151
+ if (!pip) {
152
+ throw new Error(
153
+ 'Failed to open popup. Please allow popups for this site to view the devtools in picture-in-picture mode.',
154
+ )
155
+ }
156
+ pip.document.title = 'TanStack Query Devtools'
157
+ pip.document.body.style.margin = '0'
158
+
159
+ // Detect when window is closed by user
160
+ pip.addEventListener('pagehide', () => {
161
+ setPipWindow(null)
162
+ })
163
+
164
+ // It is important to copy all parent window styles. Otherwise, there would be no CSS available at all
165
+ // https://developer.chrome.com/docs/web-platform/document-picture-in-picture/#copy-style-sheets-to-the-picture-in-picture-window
166
+ ;[...document.styleSheets].forEach((styleSheet) => {
167
+ try {
168
+ const cssRules = [...styleSheet.cssRules]
169
+ .map((rule) => rule.cssText)
170
+ .join('')
171
+ const style = document.createElement('style')
172
+ const style_node = styleSheet.ownerNode
173
+ let style_id = ''
174
+
175
+ if (style_node && 'id' in style_node) {
176
+ style_id = style_node.id
177
+ }
178
+
179
+ if (style_id) {
180
+ style.setAttribute('id', style_id)
181
+ }
182
+ style.textContent = cssRules
183
+ pip.document.head.appendChild(style)
184
+ } catch (e) {
185
+ const link = document.createElement('link')
186
+ if (styleSheet.href == null) {
187
+ return
188
+ }
189
+
190
+ link.rel = 'stylesheet'
191
+ link.type = styleSheet.type
192
+ link.media = styleSheet.media.toString()
193
+ link.href = styleSheet.href
194
+ pip.document.head.appendChild(link)
195
+ }
196
+ })
197
+ delegateEvents(
198
+ [
199
+ 'focusin',
200
+ 'focusout',
201
+ 'pointermove',
202
+ 'keydown',
203
+ 'pointerdown',
204
+ 'pointerup',
205
+ 'click',
206
+ 'mousedown',
207
+ 'input',
208
+ ],
209
+ pip.document,
210
+ )
211
+ setPipWindow(pip)
212
+ }
213
+
214
+ createEffect(() => {
215
+ // Close pipWindow when the main window is closed
216
+ const closePipWindowOnUnload = () => {
217
+ closePipWindow()
218
+ }
219
+
220
+ window.addEventListener('beforeunload', closePipWindowOnUnload)
221
+
222
+ onCleanup(() => {
223
+ window.removeEventListener('beforeunload', closePipWindowOnUnload)
224
+ })
225
+ })
226
+
227
+ createEffect(() => {
228
+ // Setup mutation observer for goober styles with id `_goober
229
+ const gooberStyles = document.getElementById('_goober')
230
+ const w = pipWindow()
231
+ if (gooberStyles && w) {
232
+ const observer = new MutationObserver(() => {
233
+ const pip_style = w.document.getElementById('_goober')
234
+ if (pip_style) {
235
+ pip_style.textContent = gooberStyles.textContent
236
+ }
237
+ })
238
+ observer.observe(gooberStyles, {
239
+ childList: true, // observe direct children
240
+ subtree: true, // and lower descendants too
241
+ characterDataOldValue: true, // pass old data to callback
242
+ })
243
+ onCleanup(() => {
244
+ observer.disconnect()
245
+ })
246
+ }
247
+ })
248
+
249
+ const value = createMemo(() => ({
250
+ pipWindow: pipWindow(),
251
+ requestPipWindow,
252
+ closePipWindow,
253
+ }))
254
+
255
+ return (
256
+ <PiPContext.Provider value={value}>{props.children}</PiPContext.Provider>
257
+ )
258
+ }
259
+
260
+ const usePiPWindow = () => {
261
+ const context = createMemo(() => {
262
+ const ctx = useContext(PiPContext)
263
+ if (!ctx) {
264
+ throw new Error('usePiPWindow must be used within a PiPProvider')
265
+ }
266
+ return ctx()
267
+ })
268
+ return context
269
+ }
270
+
112
271
  const DevtoolsComponent: DevtoolsComponentType = (props) => {
113
272
  const [localStore, setLocalStore] = createLocalStorage({
114
273
  prefix: 'TanstackQueryDevtools',
@@ -127,9 +286,11 @@ const DevtoolsComponent: DevtoolsComponentType = (props) => {
127
286
 
128
287
  return (
129
288
  <QueryDevtoolsContext.Provider value={props}>
130
- <ThemeContext.Provider value={theme}>
131
- <Devtools localStore={localStore} setLocalStore={setLocalStore} />
132
- </ThemeContext.Provider>
289
+ <PiPProvider>
290
+ <ThemeContext.Provider value={theme}>
291
+ <Devtools localStore={localStore} setLocalStore={setLocalStore} />
292
+ </ThemeContext.Provider>
293
+ </PiPProvider>
133
294
  </QueryDevtoolsContext.Provider>
134
295
  )
135
296
  }
@@ -142,6 +303,8 @@ const Devtools: Component<DevtoolsPanelProps> = (props) => {
142
303
  return theme() === 'dark' ? darkStyles : lightStyles
143
304
  })
144
305
 
306
+ const pip = usePiPWindow()
307
+
145
308
  const buttonPosition = createMemo(() => {
146
309
  return useQueryDevtoolsContext().buttonPosition || BUTTON_POSITION
147
310
  })
@@ -196,76 +359,156 @@ const Devtools: Component<DevtoolsPanelProps> = (props) => {
196
359
  })
197
360
 
198
361
  return (
199
- <div
200
- // styles for animating the panel in and out
201
- class={cx(
202
- css`
203
- & .tsqd-panel-transition-exit-active,
204
- & .tsqd-panel-transition-enter-active {
205
- transition:
206
- opacity 0.3s,
207
- transform 0.3s;
208
- }
362
+ <>
363
+ <Show when={pip().pipWindow}>
364
+ <Portal mount={pip().pipWindow?.document.body}>
365
+ <PiPPanel>
366
+ <ContentView
367
+ localStore={props.localStore}
368
+ setLocalStore={props.setLocalStore}
369
+ />
370
+ </PiPPanel>
371
+ </Portal>
372
+ </Show>
373
+ <div
374
+ // styles for animating the panel in and out
375
+ class={cx(
376
+ css`
377
+ & .tsqd-panel-transition-exit-active,
378
+ & .tsqd-panel-transition-enter-active {
379
+ transition:
380
+ opacity 0.3s,
381
+ transform 0.3s;
382
+ }
209
383
 
210
- & .tsqd-panel-transition-exit-to,
211
- & .tsqd-panel-transition-enter {
212
- ${position() === 'top' || position() === 'bottom'
213
- ? `transform: translateY(var(--tsqd-panel-height));`
214
- : `transform: translateX(var(--tsqd-panel-width));`}
215
- }
384
+ & .tsqd-panel-transition-exit-to,
385
+ & .tsqd-panel-transition-enter {
386
+ ${position() === 'top' || position() === 'bottom'
387
+ ? `transform: translateY(var(--tsqd-panel-height));`
388
+ : `transform: translateX(var(--tsqd-panel-width));`}
389
+ }
216
390
 
217
- & .tsqd-button-transition-exit-active,
218
- & .tsqd-button-transition-enter-active {
219
- transition:
220
- opacity 0.3s,
221
- transform 0.3s;
222
- opacity: 1;
223
- }
391
+ & .tsqd-button-transition-exit-active,
392
+ & .tsqd-button-transition-enter-active {
393
+ transition:
394
+ opacity 0.3s,
395
+ transform 0.3s;
396
+ opacity: 1;
397
+ }
224
398
 
225
- & .tsqd-button-transition-exit-to,
226
- & .tsqd-button-transition-enter {
227
- transform: ${buttonPosition() === 'relative'
228
- ? `none;`
229
- : buttonPosition() === 'top-left'
230
- ? `translateX(-72px);`
231
- : buttonPosition() === 'top-right'
232
- ? `translateX(72px);`
233
- : `translateY(72px);`};
234
- opacity: 0;
235
- }
236
- `,
237
- 'tsqd-transitions-container',
399
+ & .tsqd-button-transition-exit-to,
400
+ & .tsqd-button-transition-enter {
401
+ transform: ${buttonPosition() === 'relative'
402
+ ? `none;`
403
+ : buttonPosition() === 'top-left'
404
+ ? `translateX(-72px);`
405
+ : buttonPosition() === 'top-right'
406
+ ? `translateX(72px);`
407
+ : `translateY(72px);`};
408
+ opacity: 0;
409
+ }
410
+ `,
411
+ 'tsqd-transitions-container',
412
+ )}
413
+ ref={transitionsContainerRef}
414
+ >
415
+ <TransitionGroup name="tsqd-panel-transition">
416
+ <Show when={isOpen() && !pip().pipWindow}>
417
+ <DevtoolsPanel
418
+ localStore={props.localStore}
419
+ setLocalStore={props.setLocalStore}
420
+ />
421
+ </Show>
422
+ </TransitionGroup>
423
+ <TransitionGroup name="tsqd-button-transition">
424
+ <Show when={!isOpen()}>
425
+ <div
426
+ class={cx(
427
+ styles().devtoolsBtn,
428
+ styles()[`devtoolsBtn-position-${buttonPosition()}`],
429
+ 'tsqd-open-btn-container',
430
+ )}
431
+ >
432
+ <div aria-hidden="true">
433
+ <TanstackLogo />
434
+ </div>
435
+ <button
436
+ aria-label="Open Tanstack query devtools"
437
+ onClick={() => props.setLocalStore('open', 'true')}
438
+ class="tsqd-open-btn"
439
+ >
440
+ <TanstackLogo />
441
+ </button>
442
+ </div>
443
+ </Show>
444
+ </TransitionGroup>
445
+ </div>
446
+ </>
447
+ )
448
+ }
449
+
450
+ const PiPPanel: Component<{
451
+ children: JSX.Element
452
+ }> = (props) => {
453
+ const pip = usePiPWindow()
454
+ const theme = useTheme()
455
+ const styles = createMemo(() => {
456
+ return theme() === 'dark' ? darkStyles : lightStyles
457
+ })
458
+
459
+ const getPanelDynamicStyles = () => {
460
+ const { colors } = tokens
461
+ const t = (light: string, dark: string) =>
462
+ theme() === 'dark' ? dark : light
463
+ if (panelWidth() < secondBreakpoint) {
464
+ return css`
465
+ flex-direction: column;
466
+ background-color: ${t(colors.gray[300], colors.gray[600])};
467
+ `
468
+ }
469
+ return css`
470
+ flex-direction: row;
471
+ background-color: ${t(colors.gray[200], colors.darkGray[900])};
472
+ `
473
+ }
474
+
475
+ createEffect(() => {
476
+ const win = pip().pipWindow
477
+ const resizeCB = () => {
478
+ if (!win) return
479
+ setPanelWidth(win.innerWidth)
480
+ }
481
+ if (win) {
482
+ win.addEventListener('resize', resizeCB)
483
+ }
484
+
485
+ onCleanup(() => {
486
+ if (win) {
487
+ win.removeEventListener('resize', resizeCB)
488
+ }
489
+ })
490
+ })
491
+
492
+ return (
493
+ <div
494
+ style={{
495
+ '--tsqd-font-size': '16px',
496
+ 'max-height': '100vh',
497
+ height: '100vh',
498
+ width: '100vw',
499
+ }}
500
+ class={cx(
501
+ styles().panel,
502
+ getPanelDynamicStyles(),
503
+ {
504
+ [css`
505
+ min-width: min-content;
506
+ `]: panelWidth() < thirdBreakpoint,
507
+ },
508
+ 'tsqd-main-panel',
238
509
  )}
239
- ref={transitionsContainerRef}
240
510
  >
241
- <TransitionGroup name="tsqd-panel-transition">
242
- <Show when={isOpen()}>
243
- <DevtoolsPanel
244
- localStore={props.localStore}
245
- setLocalStore={props.setLocalStore}
246
- />
247
- </Show>
248
- </TransitionGroup>
249
- <TransitionGroup name="tsqd-button-transition">
250
- <Show when={!isOpen()}>
251
- <div
252
- class={cx(
253
- styles().devtoolsBtn,
254
- styles()[`devtoolsBtn-position-${buttonPosition()}`],
255
- )}
256
- >
257
- <div aria-hidden="true">
258
- <TanstackLogo />
259
- </div>
260
- <button
261
- aria-label="Open Tanstack query devtools"
262
- onClick={() => props.setLocalStore('open', 'true')}
263
- >
264
- <TanstackLogo />
265
- </button>
266
- </div>
267
- </Show>
268
- </TransitionGroup>
511
+ {props.children}
269
512
  </div>
270
513
  )
271
514
  }
@@ -478,6 +721,8 @@ const ContentView: Component<DevtoolsPanelProps> = (props) => {
478
721
  return theme() === 'dark' ? darkStyles : lightStyles
479
722
  })
480
723
 
724
+ const pip = usePiPWindow()
725
+
481
726
  const [selectedView, setSelectedView] = createSignal<'queries' | 'mutations'>(
482
727
  'queries',
483
728
  )
@@ -616,7 +861,11 @@ const ContentView: Component<DevtoolsPanelProps> = (props) => {
616
861
  <div class={styles().logoAndToggleContainer}>
617
862
  <button
618
863
  class={cx(styles().logo, 'tsqd-text-logo-container')}
619
- onClick={() => props.setLocalStore('open', 'false')}
864
+ onClick={() => {
865
+ if (!pip().pipWindow) {
866
+ props.setLocalStore('open', 'false')
867
+ }
868
+ }}
620
869
  aria-label="Close Tanstack query devtools"
621
870
  >
622
871
  <span
@@ -691,7 +940,7 @@ const ContentView: Component<DevtoolsPanelProps> = (props) => {
691
940
  props.setLocalStore('mutationFilter', e.currentTarget.value)
692
941
  }
693
942
  }}
694
- class="tsqd-query-filter-textfield"
943
+ class={cx('tsqd-query-filter-textfield')}
695
944
  name="tsqd-query-filter-input"
696
945
  value={
697
946
  selectedView() === 'queries'
@@ -831,6 +1080,25 @@ const ContentView: Component<DevtoolsPanelProps> = (props) => {
831
1080
  >
832
1081
  {offline() ? <Offline /> : <Wifi />}
833
1082
  </button>
1083
+ <Show when={!pip().pipWindow}>
1084
+ <button
1085
+ onClick={() => {
1086
+ pip().requestPipWindow(
1087
+ Number(window.innerWidth),
1088
+ Number(props.localStore.height ?? 500),
1089
+ )
1090
+ }}
1091
+ class={cx(
1092
+ styles().actionsBtn,
1093
+ 'tsqd-actions-btn',
1094
+ 'tsqd-action-open-pip',
1095
+ )}
1096
+ aria-label="Open in picture-in-picture mode"
1097
+ title={`Open in picture-in-picture mode`}
1098
+ >
1099
+ <PiPIcon />
1100
+ </button>
1101
+ </Show>
834
1102
 
835
1103
  <DropdownMenu.Root gutter={4}>
836
1104
  <DropdownMenu.Trigger
@@ -844,6 +1112,11 @@ const ContentView: Component<DevtoolsPanelProps> = (props) => {
844
1112
  </DropdownMenu.Trigger>
845
1113
  <DropdownMenu.Portal
846
1114
  ref={(el) => setComputedVariables(el as HTMLDivElement)}
1115
+ mount={
1116
+ pip().pipWindow
1117
+ ? pip().pipWindow!.document.body
1118
+ : document.body
1119
+ }
847
1120
  >
848
1121
  <DropdownMenu.Content
849
1122
  class={cx(styles().settingsMenu, 'tsqd-settings-menu')}
@@ -869,6 +1142,11 @@ const ContentView: Component<DevtoolsPanelProps> = (props) => {
869
1142
  </DropdownMenu.SubTrigger>
870
1143
  <DropdownMenu.Portal
871
1144
  ref={(el) => setComputedVariables(el as HTMLDivElement)}
1145
+ mount={
1146
+ pip().pipWindow
1147
+ ? pip().pipWindow!.document.body
1148
+ : document.body
1149
+ }
872
1150
  >
873
1151
  <DropdownMenu.SubContent
874
1152
  class={cx(
@@ -948,6 +1226,11 @@ const ContentView: Component<DevtoolsPanelProps> = (props) => {
948
1226
  </DropdownMenu.SubTrigger>
949
1227
  <DropdownMenu.Portal
950
1228
  ref={(el) => setComputedVariables(el as HTMLDivElement)}
1229
+ mount={
1230
+ pip().pipWindow
1231
+ ? pip().pipWindow!.document.body
1232
+ : document.body
1233
+ }
951
1234
  >
952
1235
  <DropdownMenu.SubContent
953
1236
  class={cx(
@@ -2420,7 +2703,7 @@ const stylesFactory = (theme: 'light' | 'dark') => {
2420
2703
  display: flex;
2421
2704
  flex-direction: column;
2422
2705
  & * {
2423
- font-family: 'Inter', sans-serif;
2706
+ font-family: ui-sans-serif, Inter, system-ui, sans-serif, sans-serif;
2424
2707
  }
2425
2708
  `,
2426
2709
  dragHandle: css`
@@ -2600,8 +2883,8 @@ const stylesFactory = (theme: 'light' | 'dark') => {
2600
2883
  gap: ${tokens.size[2]};
2601
2884
  & > button {
2602
2885
  cursor: pointer;
2603
- padding: ${tokens.size[0.5]} ${tokens.size[2]};
2604
- padding-right: ${tokens.size[1.5]};
2886
+ padding: ${tokens.size[0.5]} ${tokens.size[1.5]} ${tokens.size[0.5]}
2887
+ ${tokens.size[2]};
2605
2888
  border-radius: ${tokens.border.radius.sm};
2606
2889
  background-color: ${t(colors.gray[100], colors.darkGray[400])};
2607
2890
  border: 1px solid ${t(colors.gray[300], colors.darkGray[200])};
@@ -2785,7 +3068,8 @@ const stylesFactory = (theme: 'light' | 'dark') => {
2785
3068
  min-height: ${tokens.size[6]};
2786
3069
  flex: 1;
2787
3070
  padding: ${tokens.size[1]} ${tokens.size[2]};
2788
- font-family: 'Menlo', 'Fira Code', monospace;
3071
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
3072
+ 'Liberation Mono', 'Courier New', monospace;
2789
3073
  border-bottom: 1px solid ${t(colors.gray[300], colors.darkGray[400])};
2790
3074
  text-align: left;
2791
3075
  text-overflow: clip;
@@ -2810,7 +3094,7 @@ const stylesFactory = (theme: 'light' | 'dark') => {
2810
3094
  flex: 1 1 700px;
2811
3095
  background-color: ${t(colors.gray[50], colors.darkGray[700])};
2812
3096
  color: ${t(colors.gray[700], colors.gray[300])};
2813
- font-family: 'Inter', sans-serif;
3097
+ font-family: ui-sans-serif, Inter, system-ui, sans-serif, sans-serif;
2814
3098
  display: flex;
2815
3099
  flex-direction: column;
2816
3100
  overflow-y: auto;
@@ -2818,7 +3102,7 @@ const stylesFactory = (theme: 'light' | 'dark') => {
2818
3102
  text-align: left;
2819
3103
  `,
2820
3104
  detailsHeader: css`
2821
- font-family: 'Inter', sans-serif;
3105
+ font-family: ui-sans-serif, Inter, system-ui, sans-serif, sans-serif;
2822
3106
  position: sticky;
2823
3107
  top: 0;
2824
3108
  z-index: 2;
@@ -2850,7 +3134,8 @@ const stylesFactory = (theme: 'light' | 'dark') => {
2850
3134
  }
2851
3135
 
2852
3136
  & code {
2853
- font-family: 'Menlo', 'Fira Code', monospace;
3137
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
3138
+ 'Liberation Mono', 'Courier New', monospace;
2854
3139
  margin: 0;
2855
3140
  font-size: ${font.size.xs};
2856
3141
  line-height: ${font.lineHeight.xs};
@@ -2875,7 +3160,7 @@ const stylesFactory = (theme: 'light' | 'dark') => {
2875
3160
  gap: ${tokens.size[2]};
2876
3161
  padding: 0px ${tokens.size[2]};
2877
3162
  & > button {
2878
- font-family: 'Inter', sans-serif;
3163
+ font-family: ui-sans-serif, Inter, system-ui, sans-serif, sans-serif;
2879
3164
  font-size: ${font.size.xs};
2880
3165
  padding: ${tokens.size[1]} ${tokens.size[2]};
2881
3166
  display: flex;
@@ -2960,7 +3245,7 @@ const stylesFactory = (theme: 'light' | 'dark') => {
2960
3245
  settingsMenu: css`
2961
3246
  display: flex;
2962
3247
  & * {
2963
- font-family: 'Inter', sans-serif;
3248
+ font-family: ui-sans-serif, Inter, system-ui, sans-serif, sans-serif;
2964
3249
  }
2965
3250
  flex-direction: column;
2966
3251
  gap: ${size[0.5]};
package/src/Explorer.tsx CHANGED
@@ -489,7 +489,8 @@ const stylesFactory = (theme: 'light' | 'dark') => {
489
489
  entry: css`
490
490
  & * {
491
491
  font-size: ${font.size.xs};
492
- font-family: 'Menlo', 'Fira Code', monospace;
492
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
493
+ 'Liberation Mono', 'Courier New', monospace;
493
494
  }
494
495
  position: relative;
495
496
  outline: none;
@@ -268,6 +268,26 @@ export function Settings() {
268
268
  )
269
269
  }
270
270
 
271
+ export function PiPIcon() {
272
+ return (
273
+ <svg
274
+ width="24"
275
+ height="24"
276
+ viewBox="0 0 24 24"
277
+ fill="none"
278
+ xmlns="http://www.w3.org/2000/svg"
279
+ >
280
+ <path
281
+ d="M16 21H16.2C17.8802 21 18.7202 21 19.362 20.673C19.9265 20.3854 20.3854 19.9265 20.673 19.362C21 18.7202 21 17.8802 21 16.2V7.8C21 6.11984 21 5.27976 20.673 4.63803C20.3854 4.07354 19.9265 3.6146 19.362 3.32698C18.7202 3 17.8802 3 16.2 3H7.8C6.11984 3 5.27976 3 4.63803 3.32698C4.07354 3.6146 3.6146 4.07354 3.32698 4.63803C3 5.27976 3 6.11984 3 7.8V8M11.5 12.5L17 7M17 7H12M17 7V12M6.2 21H8.8C9.9201 21 10.4802 21 10.908 20.782C11.2843 20.5903 11.5903 20.2843 11.782 19.908C12 19.4802 12 18.9201 12 17.8V15.2C12 14.0799 12 13.5198 11.782 13.092C11.5903 12.7157 11.2843 12.4097 10.908 12.218C10.4802 12 9.92011 12 8.8 12H6.2C5.0799 12 4.51984 12 4.09202 12.218C3.71569 12.4097 3.40973 12.7157 3.21799 13.092C3 13.5198 3 14.0799 3 15.2V17.8C3 18.9201 3 19.4802 3.21799 19.908C3.40973 20.2843 3.71569 20.5903 4.09202 20.782C4.51984 21 5.07989 21 6.2 21Z"
282
+ stroke="currentColor"
283
+ stroke-width="2"
284
+ stroke-linecap="round"
285
+ stroke-linejoin="round"
286
+ />
287
+ </svg>
288
+ )
289
+ }
290
+
271
291
  export function Copier() {
272
292
  return (
273
293
  <svg