@wyxos/vibe 3.0.4 → 3.0.6

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 CHANGED
@@ -65,7 +65,7 @@ async function resolve({ cursor, pageSize }: VibeResolveParams): Promise<VibeRes
65
65
  </template>
66
66
  ```
67
67
 
68
- Optional auto-mode pacing props:
68
+ Optional pacing props:
69
69
 
70
70
  ```vue
71
71
  <VibeLayout
@@ -74,6 +74,7 @@ Optional auto-mode pacing props:
74
74
  :fill-delay-step-ms="1000"
75
75
  :show-end-badge="false"
76
76
  :show-status-badges="false"
77
+ surface-mode="fullscreen"
77
78
  />
78
79
  ```
79
80
 
@@ -81,9 +82,10 @@ Optional auto-mode pacing props:
81
82
  - `fill-delay-step-ms`: extra delay added for each additional chained fill request in the same fill cycle
82
83
  - defaults: `2000` and `1000`
83
84
  - `show-end-badge`: controls the fullscreen `End reached` badge when the feed is exhausted
84
- - `show-status-badges`: controls the built-in loading/end status overlays in list and fullscreen
85
+ - `show-status-badges`: controls the built-in lifecycle status overlays in list and fullscreen
86
+ - `surface-mode`: optionally lets the parent drive the desktop fullscreen/list surface explicitly
85
87
 
86
- Optional auto-mode feed strategy:
88
+ Optional feed strategy:
87
89
 
88
90
  ```vue
89
91
  <VibeLayout
@@ -95,12 +97,30 @@ Optional auto-mode feed strategy:
95
97
  - `dynamic` is the default
96
98
  - `static` reloads the current boundary cursor before advancing when the currently visible boundary page is underfilled
97
99
 
100
+ Optional seeded hydration:
101
+
102
+ ```vue
103
+ <VibeLayout
104
+ :resolve="resolve"
105
+ :initial-state="{
106
+ cursor: 'page-10',
107
+ items: restoredItems,
108
+ nextCursor: 'page-11',
109
+ previousCursor: 'page-9',
110
+ activeIndex: 4,
111
+ }"
112
+ />
113
+ ```
114
+
115
+ - use `initialState` when the app already knows a restored slice of the feed
116
+ - `resolve` is optional if you only need a seeded snapshot
117
+ - when `resolve` is present, Vibe continues paging from the seeded cursors
118
+
98
119
  ## What Vibe does
99
120
 
100
121
  - Desktop masonry list with virtualization and staged page growth
101
122
  - Fullscreen viewer with swipe, wheel, keyboard, and custom media controls
102
- - Auto-loading mode with internal pagination ownership
103
- - Controlled mode for advanced/manual integrations
123
+ - Library-owned loading and pagination, optionally seeded from `initialState`
104
124
  - Remove, restore, and undo by item `id`
105
125
  - Grid customization through slots for icons, overlays, and footer UI
106
126
  - Built-in loading and preload error states, including explicit `404` when known
@@ -134,9 +154,7 @@ Notes:
134
154
  - Grid layout prefers `preview.width/height`, then root `width/height`, then a square fallback tile
135
155
  - `other` is intentionally broad so the consuming app can layer its own subtypes and icon logic on top
136
156
 
137
- ## Loading modes
138
-
139
- ### Auto mode
157
+ ## Loading
140
158
 
141
159
  Use `resolve` when you want Vibe to own the paging loop:
142
160
 
@@ -172,7 +190,7 @@ async function resolve({ cursor, pageSize }: VibeResolveParams): Promise<VibeRes
172
190
  </template>
173
191
  ```
174
192
 
175
- In this mode, Vibe owns:
193
+ Vibe owns:
176
194
 
177
195
  - loaded items
178
196
  - active index
@@ -181,7 +199,7 @@ In this mode, Vibe owns:
181
199
  - duplicate cursor protection
182
200
  - initial retry state
183
201
 
184
- Auto mode also supports two feed strategies:
202
+ Vibe also supports two feed strategies:
185
203
 
186
204
  - `dynamic`:
187
205
  - default behavior
@@ -195,43 +213,19 @@ Auto mode also supports two feed strategies:
195
213
  - if it is, Vibe reloads that same cursor in place first
196
214
  - only once that boundary page is full again will the next edge hit advance to the next or previous cursor
197
215
 
198
- ### Controlled mode
199
-
200
- Use controlled mode when the app needs to own item aggregation or paging policy:
216
+ You can also seed the viewer from a known snapshot:
201
217
 
202
218
  ```vue
203
- <script setup lang="ts">
204
- import { ref } from 'vue'
205
- import { VibeLayout, type VibeViewerItem } from '@wyxos/vibe'
206
-
207
- const items = ref<VibeViewerItem[]>([])
208
- const activeIndex = ref(0)
209
- const loading = ref(false)
210
- const hasNextPage = ref(true)
211
- const hasPreviousPage = ref(false)
212
-
213
- async function requestNextPage() {
214
- // app-owned fetch and append
215
- }
216
-
217
- async function requestPreviousPage() {
218
- // app-owned fetch and prepend
219
- }
220
- </script>
221
-
222
- <template>
223
- <VibeLayout
224
- :items="items"
225
- :active-index="activeIndex"
226
- :loading="loading"
227
- :has-next-page="hasNextPage"
228
- :has-previous-page="hasPreviousPage"
229
- pagination-detail="P10-12 · V11"
230
- :request-next-page="requestNextPage"
231
- :request-previous-page="requestPreviousPage"
232
- @update:active-index="activeIndex = $event"
233
- />
234
- </template>
219
+ <VibeLayout
220
+ :resolve="resolve"
221
+ :initial-state="{
222
+ cursor: 'page-10',
223
+ items: restoredItems,
224
+ nextCursor: 'page-11',
225
+ previousCursor: 'page-9',
226
+ activeIndex: 4,
227
+ }"
228
+ />
235
229
  ```
236
230
 
237
231
  ## Slots
@@ -329,14 +323,14 @@ type VibeStatus = {
329
323
  fillTargetCount: number | null
330
324
  hasNextPage: boolean
331
325
  hasPreviousPage: boolean
332
- isAutoMode: boolean
333
326
  itemCount: number
334
327
  loadState: 'failed' | 'loaded' | 'loading'
335
- mode: 'dynamic' | 'static' | null
328
+ mode: 'dynamic' | 'static'
336
329
  nextCursor: string | null
337
- phase: 'failed' | 'filling' | 'idle' | 'loading' | 'reloading'
330
+ phase: 'failed' | 'filling' | 'idle' | 'initializing' | 'loading' | 'refreshing'
338
331
  previousCursor: string | null
339
332
  removedCount: number
333
+ removedIds: readonly string[]
340
334
  surfaceMode: 'fullscreen' | 'list'
341
335
  }
342
336
  ```
@@ -347,6 +341,7 @@ Example:
347
341
  vibe.value?.remove(['item-2', 'item-5'])
348
342
  vibe.value?.undo()
349
343
  console.log(vibe.value?.status.itemCount)
344
+ console.log(vibe.value?.status.removedIds)
350
345
  ```
351
346
 
352
347
  ## Events
@@ -354,6 +349,7 @@ console.log(vibe.value?.status.itemCount)
354
349
  `VibeLayout` emits:
355
350
 
356
351
  - `update:activeIndex`
352
+ - `update:surfaceMode`
357
353
  - `asset-loads`
358
354
  - `asset-errors`
359
355
 
@@ -1,16 +1,27 @@
1
1
  import type { Component } from 'vue';
2
2
  import type { VibeViewerItem } from './viewer';
3
3
  import type { VibeAssetErrorReporter, VibeAssetLoadReporter } from './viewer-core/assetErrors';
4
- import type { VibeFullscreenStatusSlotProps, VibeSurfaceSlotProps } from './viewer-core/surfaceSlots';
5
- import type { VibeControlledProps } from './viewer-core/useViewer';
4
+ import type { VibeEmptyStateMode, VibeEmptyStateSlotProps, VibeFullscreenStatusSlotProps, VibeSurfaceSlotProps } from './viewer-core/surfaceSlots';
5
+ import type { VibeLoadPhase } from './viewer-core/useViewer';
6
6
  import './viewer-core/fullscreenMediaBar.css';
7
- type __VLS_Props = VibeControlledProps & {
7
+ interface FullscreenSurfaceProps {
8
8
  active?: boolean;
9
+ activeIndex?: number;
10
+ emptyStateMode?: VibeEmptyStateMode;
11
+ errorMessage?: string | null;
12
+ hasNextPage?: boolean;
13
+ items: VibeViewerItem[];
14
+ loading?: boolean;
15
+ paginationDetail?: string | null;
16
+ phase?: VibeLoadPhase | null;
9
17
  reportAssetError?: VibeAssetErrorReporter | null;
10
18
  reportAssetLoad?: VibeAssetLoadReporter | null;
11
19
  showBackToList?: boolean;
12
- };
20
+ showEndBadge?: boolean;
21
+ showStatusBadges?: boolean;
22
+ }
13
23
  type __VLS_Slots = {
24
+ 'empty-state'?: (props: VibeEmptyStateSlotProps) => unknown;
14
25
  'fullscreen-aside'?: (props: VibeSurfaceSlotProps) => unknown;
15
26
  'fullscreen-header-actions'?: (props: VibeSurfaceSlotProps) => unknown;
16
27
  'fullscreen-overlay'?: (props: VibeSurfaceSlotProps) => unknown;
@@ -20,19 +31,22 @@ type __VLS_Slots = {
20
31
  item: VibeViewerItem;
21
32
  }) => unknown;
22
33
  };
23
- declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
34
+ declare const __VLS_base: import("vue").DefineComponent<FullscreenSurfaceProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
24
35
  "update:activeIndex": (value: number) => any;
25
36
  "back-to-list": () => any;
26
- }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
37
+ }, string, import("vue").PublicProps, Readonly<FullscreenSurfaceProps> & Readonly<{
27
38
  "onUpdate:activeIndex"?: ((value: number) => any) | undefined;
28
39
  "onBack-to-list"?: (() => any) | undefined;
29
40
  }>, {
30
41
  loading: boolean;
31
- activeIndex: number;
32
- hasNextPage: boolean;
42
+ emptyStateMode: VibeEmptyStateMode;
33
43
  paginationDetail: string | null;
34
44
  showEndBadge: boolean;
35
45
  showStatusBadges: boolean;
46
+ activeIndex: number;
47
+ errorMessage: string | null;
48
+ hasNextPage: boolean;
49
+ phase: VibeLoadPhase | null;
36
50
  showBackToList: boolean;
37
51
  active: boolean;
38
52
  reportAssetError: VibeAssetErrorReporter | null;
@@ -1,9 +1,10 @@
1
1
  import { type Component } from 'vue';
2
2
  import type { VibeViewerItem } from './viewer';
3
3
  import { type VibeAssetErrorEvent, type VibeAssetLoadEvent } from './viewer-core/assetErrors';
4
- import type { VibeFullscreenStatusSlotProps, VibeGridStatusSlotProps, VibeSurfaceSlotProps } from './viewer-core/surfaceSlots';
5
- import type { VibeHandle } from './viewer-core/useViewer';
4
+ import type { VibeEmptyStateSlotProps, VibeFullscreenStatusSlotProps, VibeGridStatusSlotProps, VibeSurfaceSlotProps } from './viewer-core/surfaceSlots';
5
+ import type { VibeHandle, VibeProps } from './viewer-core/useViewer';
6
6
  type __VLS_Slots = {
7
+ 'empty-state'?: (props: VibeEmptyStateSlotProps) => unknown;
7
8
  'fullscreen-aside'?: (props: VibeSurfaceSlotProps) => unknown;
8
9
  'fullscreen-header-actions'?: (props: VibeSurfaceSlotProps) => unknown;
9
10
  'fullscreen-overlay'?: (props: VibeSurfaceSlotProps) => unknown;
@@ -23,12 +24,14 @@ type __VLS_Slots = {
23
24
  item: VibeViewerItem;
24
25
  }) => unknown;
25
26
  };
26
- declare const __VLS_base: import("vue").DefineComponent<import("..").VibeControlledProps | import("..").VibeAutoProps, VibeHandle, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
27
+ declare const __VLS_base: import("vue").DefineComponent<VibeProps, VibeHandle, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
27
28
  "update:activeIndex": (value: number) => any;
29
+ "update:surfaceMode": (value: "fullscreen" | "list") => any;
28
30
  "asset-errors": (errors: VibeAssetErrorEvent[]) => any;
29
31
  "asset-loads": (loads: VibeAssetLoadEvent[]) => any;
30
- }, string, import("vue").PublicProps, Readonly<import("..").VibeControlledProps | import("..").VibeAutoProps> & Readonly<{
32
+ }, string, import("vue").PublicProps, Readonly<VibeProps> & Readonly<{
31
33
  "onUpdate:activeIndex"?: ((value: number) => any) | undefined;
34
+ "onUpdate:surfaceMode"?: ((value: "fullscreen" | "list") => any) | undefined;
32
35
  "onAsset-errors"?: ((errors: VibeAssetErrorEvent[]) => any) | undefined;
33
36
  "onAsset-loads"?: ((loads: VibeAssetLoadEvent[]) => any) | undefined;
34
37
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -1,26 +1,30 @@
1
1
  import type { Component } from 'vue';
2
2
  import type { VibeViewerItem } from './viewer';
3
3
  import type { VibeAssetErrorReporter, VibeAssetLoadReporter } from './viewer-core/assetErrors';
4
- import type { VibeGridStatusSlotProps } from './viewer-core/surfaceSlots';
4
+ import type { VibeEmptyStateMode, VibeEmptyStateSlotProps, VibeGridStatusSlotProps } from './viewer-core/surfaceSlots';
5
+ import type { VibeLoadPhase } from './viewer-core/useViewer';
5
6
  type __VLS_Props = {
6
7
  active?: boolean;
7
8
  activeIndex?: number;
8
9
  allowExhaustedNextPageRefresh?: boolean;
9
10
  commitPendingAppend?: (() => void | Promise<void>) | null;
11
+ emptyStateMode?: VibeEmptyStateMode;
12
+ errorMessage?: string | null;
10
13
  hasNextPage?: boolean;
11
14
  hasPreviousPage?: boolean;
12
15
  items: VibeViewerItem[];
13
16
  loading?: boolean;
14
17
  pendingAppendItems?: VibeViewerItem[];
15
18
  paginationDetail?: string | null;
19
+ phase?: VibeLoadPhase | null;
16
20
  reportAssetError?: VibeAssetErrorReporter | null;
17
21
  reportAssetLoad?: VibeAssetLoadReporter | null;
18
22
  requestNextPage?: (() => void | Promise<void>) | null;
19
23
  requestPreviousPage?: (() => void | Promise<void>) | null;
20
- restoreToken: number;
21
24
  showStatusBadges?: boolean;
22
25
  };
23
26
  type __VLS_Slots = {
27
+ 'empty-state'?: (props: VibeEmptyStateSlotProps) => unknown;
24
28
  'grid-footer'?: () => unknown;
25
29
  'grid-item-overlay'?: (props: {
26
30
  active: boolean;
@@ -44,19 +48,22 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {},
44
48
  "onOpen-fullscreen"?: ((index: number) => any) | undefined;
45
49
  }>, {
46
50
  loading: boolean;
51
+ emptyStateMode: VibeEmptyStateMode;
52
+ paginationDetail: string | null;
53
+ showStatusBadges: boolean;
47
54
  activeIndex: number;
55
+ errorMessage: string | null;
48
56
  hasNextPage: boolean;
57
+ phase: VibeLoadPhase | null;
49
58
  hasPreviousPage: boolean;
50
- paginationDetail: string | null;
51
- requestNextPage: (() => void | Promise<void>) | null;
52
- requestPreviousPage: (() => void | Promise<void>) | null;
53
- showStatusBadges: boolean;
54
59
  active: boolean;
55
60
  reportAssetError: VibeAssetErrorReporter | null;
56
61
  reportAssetLoad: VibeAssetLoadReporter | null;
57
62
  allowExhaustedNextPageRefresh: boolean;
58
63
  commitPendingAppend: (() => void | Promise<void>) | null;
59
64
  pendingAppendItems: VibeViewerItem[];
65
+ requestNextPage: (() => void | Promise<void>) | null;
66
+ requestPreviousPage: (() => void | Promise<void>) | null;
60
67
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
61
68
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
62
69
  declare const _default: typeof __VLS_export;
@@ -0,0 +1,21 @@
1
+ import type { VibeEmptyStateSlotProps } from './viewer-core/surfaceSlots';
2
+ type __VLS_Props = {
3
+ message: string;
4
+ mode: VibeEmptyStateSlotProps['mode'];
5
+ surface: VibeEmptyStateSlotProps['surface'];
6
+ };
7
+ declare var __VLS_1: {}, __VLS_3: {};
8
+ type __VLS_Slots = {} & {
9
+ default?: (props: typeof __VLS_1) => any;
10
+ } & {
11
+ default?: (props: typeof __VLS_3) => any;
12
+ };
13
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
14
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
15
+ declare const _default: typeof __VLS_export;
16
+ export default _default;
17
+ type __VLS_WithSlots<T, S> = T & {
18
+ new (): {
19
+ $slots: S;
20
+ };
21
+ };
@@ -13,7 +13,7 @@ export type ResolveFn = (params: {
13
13
  previousPage?: string | null;
14
14
  }>;
15
15
  export type VibeAutoDirection = 'backward' | 'forward';
16
- export declare function isActiveLoadPhase(phase: VibeLoadPhase): phase is "filling" | "loading" | "reloading";
16
+ export declare function isActiveLoadPhase(phase: VibeLoadPhase): phase is "filling" | "initializing" | "refreshing" | "loading";
17
17
  export declare function normalizePageSize(value: number | undefined): number;
18
18
  export declare function getCursorKey(cursor: string | null): string;
19
19
  export declare function clamp(value: number, min: number, max: number): number;
@@ -2,7 +2,8 @@ export interface VibeRemoveResult {
2
2
  ids: string[];
3
3
  }
4
4
  export type VibeFeedMode = 'dynamic' | 'static';
5
- export type VibeLoadPhase = 'failed' | 'filling' | 'idle' | 'loading' | 'reloading';
5
+ export type VibeLoadPhase = 'failed' | 'filling' | 'idle' | 'initializing' | 'loading' | 'refreshing';
6
+ export type VibeSurfaceMode = 'fullscreen' | 'list';
6
7
  export interface VibeStatus {
7
8
  activeIndex: number;
8
9
  currentCursor: string | null;
@@ -12,15 +13,15 @@ export interface VibeStatus {
12
13
  fillTargetCount: number | null;
13
14
  hasNextPage: boolean;
14
15
  hasPreviousPage: boolean;
15
- isAutoMode: boolean;
16
16
  itemCount: number;
17
17
  loadState: 'failed' | 'loaded' | 'loading';
18
- mode: VibeFeedMode | null;
18
+ mode: VibeFeedMode;
19
19
  nextCursor: string | null;
20
20
  phase: VibeLoadPhase;
21
21
  previousCursor: string | null;
22
22
  removedCount: number;
23
- surfaceMode: 'fullscreen' | 'list';
23
+ removedIds: readonly string[];
24
+ surfaceMode: VibeSurfaceMode;
24
25
  }
25
26
  export interface VibeHandle {
26
27
  cancel: () => void;
@@ -7,7 +7,15 @@ export type VibeSurfaceSlotProps = {
7
7
  paginationDetail: string | null;
8
8
  total: number;
9
9
  };
10
- export type VibeSurfaceStatusKind = 'end' | 'loading-more';
10
+ export type VibeEmptyStateMode = 'inline' | 'badge' | 'hidden';
11
+ export type VibeEmptyStateSlotProps = {
12
+ loading: boolean;
13
+ message: string;
14
+ mode: Exclude<VibeEmptyStateMode, 'hidden'>;
15
+ surface: 'fullscreen' | 'grid';
16
+ total: number;
17
+ };
18
+ export type VibeSurfaceStatusKind = 'end' | 'failed' | 'filling' | 'initializing' | 'loading-more' | 'refreshing';
11
19
  export type VibeFullscreenStatusSlotProps = VibeSurfaceSlotProps & {
12
20
  kind: VibeSurfaceStatusKind;
13
21
  message: string;
@@ -0,0 +1,18 @@
1
+ import type { VibeLoadPhase } from './removalState';
2
+ import type { VibeSurfaceStatusKind } from './surfaceSlots';
3
+ export interface VibeSurfaceStatus {
4
+ kind: VibeSurfaceStatusKind;
5
+ message: string;
6
+ }
7
+ export declare function resolveVibeSurfacePhase(options: {
8
+ itemCount: number;
9
+ loading: boolean;
10
+ phase?: VibeLoadPhase | null;
11
+ }): VibeLoadPhase;
12
+ export declare function getVibeSurfaceStatus(options: {
13
+ errorMessage?: string | null;
14
+ hasItems: boolean;
15
+ hasNextPage: boolean;
16
+ phase: VibeLoadPhase;
17
+ surface: 'fullscreen' | 'grid';
18
+ }): VibeSurfaceStatus | null;
@@ -1,10 +1,9 @@
1
+ import type { VibeSurfaceMode } from './removalState';
1
2
  import { type VibeEmit, type VibeProps } from './useDataSource';
2
3
  export declare const DESKTOP_BREAKPOINT_PX = 1024;
3
- type VibeDesktopSurface = 'fullscreen' | 'list';
4
4
  export declare function useController(props: Readonly<VibeProps>, emit: VibeEmit): {
5
5
  cancel: () => void;
6
6
  isDesktop: import("vue").ComputedRef<boolean>;
7
- listRestoreToken: import("vue").Ref<number, number>;
8
7
  loadNext: () => Promise<void>;
9
8
  loadPrevious: () => Promise<void>;
10
9
  openFullscreen: (index: number) => void;
@@ -20,38 +19,37 @@ export declare function useController(props: Readonly<VibeProps>, emit: VibeEmit
20
19
  readonly fillTargetCount: number | null;
21
20
  readonly hasNextPage: boolean;
22
21
  readonly hasPreviousPage: boolean;
23
- readonly isAutoMode: boolean;
24
22
  readonly itemCount: number;
25
23
  readonly loadState: "failed" | "loaded" | "loading";
26
- readonly mode: import("./removalState").VibeFeedMode | null;
24
+ readonly mode: import("./removalState").VibeFeedMode;
27
25
  readonly nextCursor: string | null;
28
26
  readonly phase: import("./removalState").VibeLoadPhase;
29
27
  readonly previousCursor: string | null;
30
28
  readonly removedCount: number;
31
- readonly surfaceMode: "fullscreen" | "list";
29
+ readonly removedIds: readonly string[];
30
+ readonly surfaceMode: VibeSurfaceMode;
32
31
  };
33
- surfaceMode: import("vue").ComputedRef<VibeDesktopSurface>;
32
+ surfaceMode: import("vue").ComputedRef<VibeSurfaceMode>;
34
33
  activeIndex: import("vue").ComputedRef<number>;
35
34
  canRefreshExhaustedNextPage: import("vue").ComputedRef<boolean>;
36
35
  canRetryInitialLoad: import("vue").ComputedRef<boolean>;
37
36
  clearRemoved: () => void;
38
37
  commitPendingAppend: () => Promise<void>;
39
38
  currentCursor: import("vue").ComputedRef<string | null>;
40
- errorMessage: import("vue").ComputedRef<string | null>;
41
- fillCollectedCount: import("vue").ComputedRef<number | null>;
42
- fillDelayRemainingMs: import("vue").ComputedRef<number | null>;
43
- fillTargetCount: import("vue").ComputedRef<number | null>;
39
+ errorMessage: import("vue").Ref<string | null, string | null>;
40
+ fillCollectedCount: import("vue").Ref<number | null, number | null>;
41
+ fillDelayRemainingMs: import("vue").Ref<number | null, number | null>;
42
+ fillTargetCount: import("vue").Ref<number | null, number | null>;
44
43
  getRemovedIds: () => string[];
45
44
  hasNextPage: import("vue").ComputedRef<boolean>;
46
45
  hasPreviousPage: import("vue").ComputedRef<boolean>;
47
- isAutoMode: import("vue").ComputedRef<boolean>;
48
46
  items: import("vue").ComputedRef<import("../viewer").VibeViewerItem[]>;
49
47
  loading: import("vue").ComputedRef<boolean>;
50
- mode: import("vue").ComputedRef<import("./removalState").VibeFeedMode | null>;
48
+ mode: import("vue").ComputedRef<import("./removalState").VibeFeedMode>;
51
49
  nextCursor: import("vue").ComputedRef<string | null>;
52
50
  paginationDetail: import("vue").ComputedRef<string | null>;
53
51
  pendingAppendItems: import("vue").ComputedRef<import("../viewer").VibeViewerItem[]>;
54
- phase: import("vue").ComputedRef<import("./removalState").VibeLoadPhase>;
52
+ phase: import("vue").Ref<import("./removalState").VibeLoadPhase, import("./removalState").VibeLoadPhase>;
55
53
  prefetchNextPage: () => Promise<void>;
56
54
  prefetchPreviousPage: () => Promise<void>;
57
55
  previousCursor: import("vue").ComputedRef<string | null>;
@@ -69,4 +67,3 @@ export declare function useController(props: Readonly<VibeProps>, emit: VibeEmit
69
67
  ids: string[];
70
68
  } | null;
71
69
  };
72
- export {};
@@ -1,6 +1,7 @@
1
1
  import type { VibeViewerItem } from '../viewer';
2
+ import type { VibeEmptyStateMode } from './surfaceSlots';
2
3
  export type { VibeHandle, VibeRemoveResult } from './removalState';
3
- export type { VibeFeedMode, VibeLoadPhase } from './removalState';
4
+ export type { VibeFeedMode, VibeLoadPhase, VibeSurfaceMode } from './removalState';
4
5
  export interface VibeResolveParams {
5
6
  cursor: string | null;
6
7
  pageSize: number;
@@ -18,46 +19,24 @@ export interface VibeInitialState {
18
19
  previousCursor?: string | null;
19
20
  activeIndex?: number;
20
21
  }
21
- interface VibeSharedProps {
22
- hasPreviousPage?: boolean;
23
- paginationDetail?: string | null;
24
- requestNextPage?: (() => void | Promise<void>) | null;
25
- requestPreviousPage?: (() => void | Promise<void>) | null;
26
- showEndBadge?: boolean;
27
- showStatusBadges?: boolean;
28
- }
29
- export interface VibeControlledProps extends VibeSharedProps {
30
- items: VibeViewerItem[];
31
- activeIndex?: number;
32
- fillDelayMs?: never;
33
- fillDelayStepMs?: never;
34
- initialState?: never;
35
- loading?: boolean;
36
- hasNextPage?: boolean;
37
- mode?: never;
38
- resolve?: never;
39
- initialCursor?: never;
40
- pageSize?: never;
41
- }
42
- export interface VibeAutoProps extends VibeSharedProps {
43
- resolve: (params: VibeResolveParams) => Promise<VibeResolveResult>;
22
+ export interface VibeProps {
23
+ emptyStateMode?: VibeEmptyStateMode;
44
24
  fillDelayMs?: number;
45
25
  fillDelayStepMs?: number;
46
26
  initialCursor?: string | null;
47
27
  initialState?: VibeInitialState;
48
28
  mode?: import('./removalState').VibeFeedMode;
49
29
  pageSize?: number;
50
- items?: never;
51
- activeIndex?: never;
52
- hasPreviousPage?: never;
53
- loading?: never;
54
- hasNextPage?: never;
55
- paginationDetail?: never;
56
- requestNextPage?: never;
57
- requestPreviousPage?: never;
30
+ paginationDetail?: string | null;
31
+ resolve?: (params: VibeResolveParams) => Promise<VibeResolveResult>;
32
+ showEndBadge?: boolean;
33
+ showStatusBadges?: boolean;
34
+ surfaceMode?: import('./removalState').VibeSurfaceMode;
35
+ }
36
+ export interface VibeEmit {
37
+ (event: 'update:activeIndex', value: number): void;
38
+ (event: 'update:surfaceMode', value: import('./removalState').VibeSurfaceMode): void;
58
39
  }
59
- export type VibeProps = VibeControlledProps | VibeAutoProps;
60
- export type VibeEmit = (event: 'update:activeIndex', value: number) => void;
61
40
  export declare function useDataSource(props: Readonly<VibeProps>, emit: VibeEmit): {
62
41
  activeIndex: import("vue").ComputedRef<number>;
63
42
  canRefreshExhaustedNextPage: import("vue").ComputedRef<boolean>;
@@ -66,23 +45,22 @@ export declare function useDataSource(props: Readonly<VibeProps>, emit: VibeEmit
66
45
  clearRemoved: () => void;
67
46
  commitPendingAppend: () => Promise<void>;
68
47
  currentCursor: import("vue").ComputedRef<string | null>;
69
- errorMessage: import("vue").ComputedRef<string | null>;
70
- fillCollectedCount: import("vue").ComputedRef<number | null>;
71
- fillDelayRemainingMs: import("vue").ComputedRef<number | null>;
72
- fillTargetCount: import("vue").ComputedRef<number | null>;
48
+ errorMessage: import("vue").Ref<string | null, string | null>;
49
+ fillCollectedCount: import("vue").Ref<number | null, number | null>;
50
+ fillDelayRemainingMs: import("vue").Ref<number | null, number | null>;
51
+ fillTargetCount: import("vue").Ref<number | null, number | null>;
73
52
  getRemovedIds: () => string[];
74
53
  hasNextPage: import("vue").ComputedRef<boolean>;
75
54
  hasPreviousPage: import("vue").ComputedRef<boolean>;
76
- isAutoMode: import("vue").ComputedRef<boolean>;
77
55
  items: import("vue").ComputedRef<VibeViewerItem[]>;
78
56
  loading: import("vue").ComputedRef<boolean>;
79
57
  loadNext: () => Promise<void>;
80
58
  loadPrevious: () => Promise<void>;
81
- mode: import("vue").ComputedRef<import("./removalState").VibeFeedMode | null>;
59
+ mode: import("vue").ComputedRef<import("./removalState").VibeFeedMode>;
82
60
  nextCursor: import("vue").ComputedRef<string | null>;
83
61
  paginationDetail: import("vue").ComputedRef<string | null>;
84
62
  pendingAppendItems: import("vue").ComputedRef<VibeViewerItem[]>;
85
- phase: import("vue").ComputedRef<import("./removalState").VibeLoadPhase>;
63
+ phase: import("vue").Ref<import("./removalState").VibeLoadPhase, import("./removalState").VibeLoadPhase>;
86
64
  prefetchNextPage: () => Promise<void>;
87
65
  prefetchPreviousPage: () => Promise<void>;
88
66
  previousCursor: import("vue").ComputedRef<string | null>;
@@ -2,6 +2,7 @@ import { type Ref } from 'vue';
2
2
  export type VibeMasonryEdgeDirection = 'bottom' | 'top';
3
3
  export declare function useEdgeBoundary(options: {
4
4
  direction: VibeMasonryEdgeDirection;
5
+ interactionLocked?: Ref<boolean>;
5
6
  hasPage: Ref<boolean>;
6
7
  isAtBoundary: () => boolean;
7
8
  loading: Ref<boolean>;
@@ -0,0 +1,23 @@
1
+ import type { Ref } from 'vue';
2
+ import type { VibeViewerItem } from '../viewer';
3
+ export declare function useFullscreenSurfaceMedia(options: {
4
+ active: Ref<boolean | undefined>;
5
+ resolvedActiveIndex: Ref<number>;
6
+ viewer: {
7
+ getAssetErrorKind: (id: string) => unknown;
8
+ getAssetErrorLabel: (id: string) => string | null | undefined;
9
+ getImageSource: (item: VibeViewerItem) => string | undefined;
10
+ isImageReady: (id: string) => boolean;
11
+ isMediaReady: (id: string) => boolean;
12
+ };
13
+ }): {
14
+ getAssetErrorKind: (item: VibeViewerItem) => unknown;
15
+ getAssetErrorLabel: (item: VibeViewerItem) => string;
16
+ getFullscreenImageSource: (index: number, item: VibeViewerItem) => string | undefined;
17
+ getFullscreenMediaSource: (index: number, item: VibeViewerItem) => string | undefined;
18
+ getItemKey: (item: VibeViewerItem) => string;
19
+ getMediaActionLabel: (action: "Play" | "Pause", item: VibeViewerItem) => string;
20
+ isAssetErrored: (index: number, item: VibeViewerItem) => boolean;
21
+ isAssetLoading: (index: number, item: VibeViewerItem) => boolean;
22
+ shouldPreloadSlideAsset: (index: number) => boolean;
23
+ };