@rlse/widget 0.1.4 → 0.2.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/dist/types.d.ts CHANGED
@@ -1,54 +1,2 @@
1
- export interface WidgetConfig {
2
- /** Organization slug (required) */
3
- orgSlug: string;
4
- /** Optional app slug to filter changes */
5
- appSlug?: string;
6
- /** Trigger behavior: 'auto', 'manual', or 'both' */
7
- trigger?: 'auto' | 'manual' | 'both';
8
- /** Days since last visit to auto-show (for 'auto' or 'both') */
9
- autoShowAfter?: number;
10
- /** Maximum changes to display */
11
- limit?: number;
12
- /** Show status badges */
13
- showStatus?: boolean;
14
- /** Show creation dates */
15
- showDates?: boolean;
16
- /** Button position for manual trigger */
17
- position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
18
- /** Theme: 'light', 'dark', or 'auto' */
19
- theme?: 'light' | 'dark' | 'auto';
20
- /** Primary accent color (hex) */
21
- primaryColor?: string;
22
- /** Button text */
23
- triggerLabel?: string;
24
- /** Modal title */
25
- modalTitle?: string;
26
- /** Base URL for the server (optional, defaults to https://rlse.dev) */
27
- baseUrl?: string;
28
- }
29
- export interface Change {
30
- _id: string;
31
- _creationTime: number;
32
- changeName: string;
33
- changeSummary: string;
34
- changeDescription: string;
35
- currentStatus: string;
36
- appName?: string;
37
- appSlug?: string;
38
- }
39
- export interface WidgetChangesResponse {
40
- org: {
41
- orgName: string;
42
- orgSlug: string;
43
- };
44
- app?: {
45
- appName: string;
46
- appSlug: string;
47
- };
48
- changes: Change[];
49
- meta: {
50
- total: number;
51
- limit: number;
52
- };
53
- }
54
- export declare const DEFAULT_CONFIG: Partial<WidgetConfig>;
1
+ export type { WidgetConfig, Change, WidgetChangesResponse, } from '@rlse/widget-core';
2
+ export { DEFAULT_CONFIG } from '@rlse/widget-core';
package/dist/types.js CHANGED
@@ -1,12 +1 @@
1
- export const DEFAULT_CONFIG = {
2
- trigger: 'manual',
3
- limit: 10,
4
- showStatus: true,
5
- showDates: true,
6
- position: 'bottom-right',
7
- theme: 'auto',
8
- triggerLabel: "What's New",
9
- modalTitle: 'Release Notes',
10
- baseUrl: 'https://rlse.dev',
11
- autoShowAfter: 7,
12
- };
1
+ export { DEFAULT_CONFIG } from '@rlse/widget-core';
@@ -1,10 +1,11 @@
1
1
  import { useState, useEffect, useCallback, useRef } from 'react';
2
+ import { fetchChanges } from '@rlse/widget-core';
2
3
  export function useWidgetData(orgSlug, appSlug, limit, baseUrl) {
3
4
  const [changes, setChanges] = useState([]);
4
5
  const [isLoading, setIsLoading] = useState(true);
5
6
  const [error, setError] = useState(null);
6
7
  const abortControllerRef = useRef(null);
7
- const fetchChanges = useCallback(async () => {
8
+ const doFetch = useCallback(async () => {
8
9
  // Cancel any in-flight request
9
10
  if (abortControllerRef.current) {
10
11
  abortControllerRef.current.abort();
@@ -12,52 +13,30 @@ export function useWidgetData(orgSlug, appSlug, limit, baseUrl) {
12
13
  abortControllerRef.current = new AbortController();
13
14
  setIsLoading(true);
14
15
  setError(null);
15
- try {
16
- // Validate baseUrl
17
- if (!baseUrl || typeof baseUrl !== 'string') {
18
- throw new Error(`Invalid baseUrl: ${baseUrl}`);
19
- }
20
- let url;
21
- try {
22
- url = new URL(`${baseUrl}/api/widget/changes`);
23
- }
24
- catch {
25
- throw new Error(`Invalid baseUrl for widget API: ${baseUrl}`);
26
- }
27
- url.searchParams.set('orgSlug', orgSlug);
28
- if (appSlug) {
29
- url.searchParams.set('appSlug', appSlug);
30
- }
31
- url.searchParams.set('limit', limit.toString());
32
- const response = await fetch(url.toString(), {
33
- signal: abortControllerRef.current.signal,
34
- });
35
- if (!response.ok) {
36
- throw new Error(`Failed to fetch changes: ${response.status}`);
37
- }
38
- const data = await response.json();
39
- setChanges(data.changes);
40
- }
41
- catch (err) {
42
- // Don't update state if request was aborted
43
- if (err instanceof Error && err.name === 'AbortError') {
44
- return;
45
- }
46
- setError(err instanceof Error ? err : new Error('Unknown error'));
16
+ const result = await fetchChanges({
17
+ orgSlug,
18
+ appSlug,
19
+ limit,
20
+ baseUrl,
21
+ signal: abortControllerRef.current.signal,
22
+ });
23
+ if (result.error && result.error.name !== 'AbortError') {
24
+ setError(result.error);
47
25
  setChanges([]);
48
26
  }
49
- finally {
50
- setIsLoading(false);
27
+ else {
28
+ setChanges(result.changes);
51
29
  }
30
+ setIsLoading(false);
52
31
  }, [orgSlug, appSlug, limit, baseUrl]);
53
32
  useEffect(() => {
54
- fetchChanges();
33
+ doFetch();
55
34
  return () => {
56
35
  // Cleanup: abort in-flight request on unmount
57
36
  if (abortControllerRef.current) {
58
37
  abortControllerRef.current.abort();
59
38
  }
60
39
  };
61
- }, [fetchChanges]);
62
- return { changes, isLoading, error, refetch: fetchChanges };
40
+ }, [doFetch]);
41
+ return { changes, isLoading, error, refetch: doFetch };
63
42
  }
@@ -0,0 +1,3 @@
1
+ export { default as RlseWidget } from './RlseWidget.vue';
2
+ export { default as RlseWidgetEmbed } from './RlseWidgetEmbed.vue';
3
+ export { default as RlseWidgetMenu } from './RlseWidgetMenu.vue';
@@ -0,0 +1,5 @@
1
+ // Vue Single File Components
2
+ // Note: Components are .vue files and should be imported directly
3
+ export { default as RlseWidget } from './RlseWidget.vue';
4
+ export { default as RlseWidgetEmbed } from './RlseWidgetEmbed.vue';
5
+ export { default as RlseWidgetMenu } from './RlseWidgetMenu.vue';
@@ -0,0 +1,2 @@
1
+ export { useWidgetData } from './useWidgetData';
2
+ export { useStorage } from './useStorage';
@@ -0,0 +1,2 @@
1
+ export { useWidgetData } from './useWidgetData';
2
+ export { useStorage } from './useStorage';
@@ -0,0 +1,13 @@
1
+ import { type ComputedRef } from 'vue';
2
+ import type { Change } from '@rlse/widget-core';
3
+ export interface UseStorageOptions {
4
+ orgSlug: string;
5
+ }
6
+ export interface UseStorageReturn {
7
+ lastVisit: ComputedRef<number | null>;
8
+ unreadCount: (changes: Change[]) => number;
9
+ shouldAutoShow: (autoShowAfter: number) => boolean;
10
+ markAllAsSeen: (changeIds: string[]) => void;
11
+ updateLastVisit: () => void;
12
+ }
13
+ export declare function useStorage(options: UseStorageOptions): UseStorageReturn;
@@ -0,0 +1,12 @@
1
+ import { computed } from 'vue';
2
+ import { getLastVisit, setLastVisit, markAllChangesAsSeen, getUnreadCount, shouldAutoShow, } from '@rlse/widget-core';
3
+ export function useStorage(options) {
4
+ const lastVisit = computed(() => getLastVisit(options.orgSlug));
5
+ return {
6
+ lastVisit,
7
+ unreadCount: (changes) => getUnreadCount(options.orgSlug, changes),
8
+ shouldAutoShow: (autoShowAfter) => shouldAutoShow(options.orgSlug, autoShowAfter),
9
+ markAllAsSeen: (changeIds) => markAllChangesAsSeen(options.orgSlug, changeIds),
10
+ updateLastVisit: () => setLastVisit(options.orgSlug),
11
+ };
12
+ }
@@ -0,0 +1,16 @@
1
+ import { type Ref } from 'vue';
2
+ import type { Change } from '@rlse/widget-core';
3
+ export interface UseWidgetDataOptions {
4
+ orgSlug: string;
5
+ appSlug?: string;
6
+ limit: number;
7
+ baseUrl: string;
8
+ }
9
+ export interface UseWidgetDataReturn {
10
+ changes: Ref<Change[]>;
11
+ isLoading: Ref<boolean>;
12
+ error: Ref<Error | null>;
13
+ unreadCount: Ref<number>;
14
+ refetch: () => Promise<void>;
15
+ }
16
+ export declare function useWidgetData(options: UseWidgetDataOptions): UseWidgetDataReturn;
@@ -0,0 +1,42 @@
1
+ import { ref, computed, onMounted, onUnmounted } from 'vue';
2
+ import { fetchChanges, WidgetDataFetcher } from '@rlse/widget-core';
3
+ export function useWidgetData(options) {
4
+ const changes = ref([]);
5
+ const isLoading = ref(true);
6
+ const error = ref(null);
7
+ const fetcher = new WidgetDataFetcher();
8
+ const fetchData = async () => {
9
+ isLoading.value = true;
10
+ error.value = null;
11
+ const result = await fetchChanges({
12
+ orgSlug: options.orgSlug,
13
+ appSlug: options.appSlug,
14
+ limit: options.limit,
15
+ baseUrl: options.baseUrl,
16
+ });
17
+ if (result.error && result.error.name !== 'AbortError') {
18
+ error.value = result.error;
19
+ changes.value = [];
20
+ }
21
+ else {
22
+ changes.value = result.changes;
23
+ }
24
+ isLoading.value = false;
25
+ };
26
+ onMounted(() => {
27
+ fetchData();
28
+ });
29
+ onUnmounted(() => {
30
+ fetcher.destroy();
31
+ });
32
+ const refetch = async () => {
33
+ await fetchData();
34
+ };
35
+ return {
36
+ changes,
37
+ isLoading,
38
+ error,
39
+ unreadCount: computed(() => changes.value.length),
40
+ refetch,
41
+ };
42
+ }
@@ -0,0 +1,6 @@
1
+ export { useWidgetData, useStorage } from './composables';
2
+ export { default as RlseWidget } from './components/RlseWidget.vue';
3
+ export { default as RlseWidgetEmbed } from './components/RlseWidgetEmbed.vue';
4
+ export { default as RlseWidgetMenu } from './components/RlseWidgetMenu.vue';
5
+ export type { WidgetConfig, Change, WidgetChangesResponse, } from '@rlse/widget-core';
6
+ export { DEFAULT_CONFIG } from '@rlse/widget-core';
@@ -0,0 +1,8 @@
1
+ // Vue 3 Composables
2
+ export { useWidgetData, useStorage } from './composables';
3
+ // Vue 3 Components (SFC)
4
+ // Import .vue files directly in your Vue projects
5
+ export { default as RlseWidget } from './components/RlseWidget.vue';
6
+ export { default as RlseWidgetEmbed } from './components/RlseWidgetEmbed.vue';
7
+ export { default as RlseWidgetMenu } from './components/RlseWidgetMenu.vue';
8
+ export { DEFAULT_CONFIG } from '@rlse/widget-core';
package/package.json CHANGED
@@ -1,13 +1,33 @@
1
1
  {
2
2
  "name": "@rlse/widget",
3
- "version": "0.1.4",
4
- "description": "React release notes widget for rlse.dev",
3
+ "version": "0.2.0",
4
+ "description": "Multi-framework release notes widget for rlse.dev - React, Vue, Angular, Svelte",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "exports": {
8
8
  ".": {
9
9
  "types": "./dist/index.d.ts",
10
10
  "default": "./dist/index.js"
11
+ },
12
+ "./react": {
13
+ "types": "./dist/index.d.ts",
14
+ "default": "./dist/index.js"
15
+ },
16
+ "./vue": {
17
+ "types": "./dist/vue/index.d.ts",
18
+ "default": "./dist/vue/index.js"
19
+ },
20
+ "./angular": {
21
+ "types": "./dist/angular/index.d.ts",
22
+ "default": "./dist/angular/index.js"
23
+ },
24
+ "./svelte": {
25
+ "types": "./dist/svelte/index.d.ts",
26
+ "default": "./dist/svelte/index.js"
27
+ },
28
+ "./vanilla": {
29
+ "types": "./dist/vanilla/index.d.ts",
30
+ "default": "./dist/vanilla/index.js"
11
31
  }
12
32
  },
13
33
  "files": [
@@ -25,11 +45,34 @@
25
45
  },
26
46
  "peerDependencies": {
27
47
  "react": "^18.0.0 || ^19.0.0",
28
- "react-dom": "^18.0.0 || ^19.0.0"
48
+ "react-dom": "^18.0.0 || ^19.0.0",
49
+ "vue": "^3.0.0",
50
+ "@angular/core": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0",
51
+ "svelte": "^4.0.0 || ^5.0.0"
52
+ },
53
+ "peerDependenciesMeta": {
54
+ "react": {
55
+ "optional": true
56
+ },
57
+ "react-dom": {
58
+ "optional": true
59
+ },
60
+ "vue": {
61
+ "optional": true
62
+ },
63
+ "@angular/core": {
64
+ "optional": true
65
+ },
66
+ "svelte": {
67
+ "optional": true
68
+ }
29
69
  },
30
70
  "devDependencies": {
31
71
  "@types/react": "^19.0.0",
32
72
  "@types/react-dom": "^19.0.0",
73
+ "@angular/core": "^18.0.0",
74
+ "rxjs": "^7.8.0",
75
+ "vue": "^3.4.0",
33
76
  "bun-types": "latest",
34
77
  "typescript": "^5.0.0"
35
78
  },
@@ -38,7 +81,13 @@
38
81
  "changelog",
39
82
  "widget",
40
83
  "react",
84
+ "vue",
85
+ "angular",
86
+ "svelte",
41
87
  "embed"
42
88
  ],
43
- "license": "MIT"
89
+ "license": "MIT",
90
+ "dependencies": {
91
+ "@rlse/widget-core": "../widget-core"
92
+ }
44
93
  }