@rimori/react-client 0.4.0 → 0.4.2-next.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/.github/workflows/create-release-branch.yml +12 -0
- package/.github/workflows/release.yml +21 -0
- package/dist/components/audio/Playbutton.js +1 -1
- package/dist/providers/PluginProvider.d.ts +4 -1
- package/dist/providers/PluginProvider.js +18 -3
- package/package.json +3 -3
- package/src/components/audio/Playbutton.tsx +8 -2
- package/src/providers/PluginProvider.tsx +31 -5
- package/tsconfig.json +1 -0
- package/.github/workflows/pre-release.yml +0 -149
- package/dist/components/MarkdownEditor.d.ts +0 -8
- package/dist/components/MarkdownEditor.js +0 -48
- package/dist/components/Spinner.d.ts +0 -1
- package/dist/components/Spinner.js +0 -1
- package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.d.ts +0 -11
- package/dist/components/ai/EmbeddedAssistent/VoiceRecoder.js +0 -95
- package/dist/plugin/ThemeSetter.d.ts +0 -2
- package/dist/plugin/ThemeSetter.js +0 -31
- package/dist/utils/FullscreenUtils.d.ts +0 -2
- package/dist/utils/FullscreenUtils.js +0 -23
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
name: Create Release Branch
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
schedule:
|
|
6
|
+
- cron: '0 11 * * 1' # Monday at 11:00 UTC
|
|
7
|
+
- cron: '0 11 * * 4' # Thursday at 11:00 UTC
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
create-release:
|
|
11
|
+
uses: rimori-org/pipeline-templates/.github/workflows/package-create-release-branch.yml@main
|
|
12
|
+
secrets: inherit
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
name: Publish Rimori React Client
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- dev
|
|
7
|
+
- main
|
|
8
|
+
paths:
|
|
9
|
+
- '**'
|
|
10
|
+
- '!.github/workflows/**'
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
pre-release:
|
|
14
|
+
if: github.ref == 'refs/heads/dev'
|
|
15
|
+
uses: rimori-org/pipeline-templates/.github/workflows/package-pre-release.yml@main
|
|
16
|
+
secrets: inherit
|
|
17
|
+
|
|
18
|
+
release:
|
|
19
|
+
if: github.ref == 'refs/heads/main'
|
|
20
|
+
uses: rimori-org/pipeline-templates/.github/workflows/package-release-on-merge.yml@main
|
|
21
|
+
secrets: inherit
|
|
@@ -125,7 +125,7 @@ export const AudioPlayer = ({ text, voice, language, hide, playListenerEvent, in
|
|
|
125
125
|
// console.log("playOnMount", playOnMount);
|
|
126
126
|
togglePlayback();
|
|
127
127
|
}, [playOnMount]);
|
|
128
|
-
return (_jsx("div", { className: "group relative", children: _jsxs("div", { className: "flex flex-row items-end", children: [!hide && (_jsx("button", { className: "text-gray-
|
|
128
|
+
return (_jsx("div", { className: "group relative", children: _jsxs("div", { className: "flex flex-row items-end", children: [!hide && (_jsx("button", { className: "text-gray-400", onClick: togglePlayback, disabled: isLoading, children: isLoading ? (_jsx(Spinner, { size: "25px" })) : isPlaying ? (_jsx(FaStopCircle, { size: "25px" })) : (_jsx(FaPlayCircle, { size: "25px" })) })), enableSpeedAdjustment && (_jsxs("div", { className: "ml-1 opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex flex-row text-sm text-gray-500", children: [_jsx("span", { className: "pr-1", children: "Speed: " }), _jsx("select", { value: speed, className: "appearance-none cursor-pointer pr-0 p-0 rounded shadow leading-tight focus:outline-none focus:bg-gray-800 focus:ring bg-transparent border-0", onChange: (e) => setSpeed(parseFloat(e.target.value)), disabled: isLoading, children: AudioPlayOptions.map((s) => (_jsx("option", { value: s, children: s }, s))) })] }))] }) }));
|
|
129
129
|
};
|
|
130
130
|
const Spinner = ({ text, className, size = '30px' }) => {
|
|
131
131
|
return (_jsxs("div", { className: 'flex items-center space-x-2 ' + className, children: [_jsxs("svg", { style: { width: size, height: size }, className: "animate-spin -ml-1 mr-3 h-5 w-5 text-white", xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", children: [_jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }), _jsx("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })] }), text && _jsx("span", { className: "", children: text })] }));
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { ReactNode } from 'react';
|
|
2
2
|
import { RimoriClient } from '@rimori/client';
|
|
3
|
+
import type { UserInfo } from '@rimori/client';
|
|
3
4
|
interface PluginProviderProps {
|
|
4
5
|
children: ReactNode;
|
|
5
6
|
pluginId: string;
|
|
@@ -8,5 +9,7 @@ interface PluginProviderProps {
|
|
|
8
9
|
};
|
|
9
10
|
}
|
|
10
11
|
export declare const PluginProvider: React.FC<PluginProviderProps>;
|
|
11
|
-
export declare const useRimori: () => RimoriClient
|
|
12
|
+
export declare const useRimori: () => RimoriClient & {
|
|
13
|
+
userInfo: UserInfo;
|
|
14
|
+
};
|
|
12
15
|
export {};
|
|
@@ -18,6 +18,7 @@ export const PluginProvider = ({ children, pluginId, settings }) => {
|
|
|
18
18
|
const [standaloneClient, setStandaloneClient] = useState(false);
|
|
19
19
|
const [applicationMode, setApplicationMode] = useState(null);
|
|
20
20
|
const [theme, setTheme] = useState(null);
|
|
21
|
+
const [userInfo, setUserInfo] = useState(null);
|
|
21
22
|
useTheme(theme);
|
|
22
23
|
const isSidebar = applicationMode === 'sidebar';
|
|
23
24
|
const isSettings = applicationMode === 'settings';
|
|
@@ -33,6 +34,8 @@ export const PluginProvider = ({ children, pluginId, settings }) => {
|
|
|
33
34
|
if ((!standaloneDetected && !client) || (standaloneDetected && standaloneClient === true)) {
|
|
34
35
|
void RimoriClient.getInstance(pluginId).then((client) => {
|
|
35
36
|
setClient(client);
|
|
37
|
+
// Set initial userInfo
|
|
38
|
+
setUserInfo(client.plugin.getUserInfo());
|
|
36
39
|
// Get applicationMode and theme from MessageChannel query params
|
|
37
40
|
if (!standaloneDetected) {
|
|
38
41
|
const mode = client.getQueryParam('applicationMode');
|
|
@@ -44,6 +47,16 @@ export const PluginProvider = ({ children, pluginId, settings }) => {
|
|
|
44
47
|
});
|
|
45
48
|
}
|
|
46
49
|
}, [pluginId, standaloneClient, client]);
|
|
50
|
+
// Listen for RimoriInfo updates and update userInfo
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (!client)
|
|
53
|
+
return;
|
|
54
|
+
const unsubscribe = client.plugin.onRimoriInfoUpdate((info) => {
|
|
55
|
+
console.log('[PluginProvider] Received RimoriInfo update, updating userInfo');
|
|
56
|
+
setUserInfo(info.profile);
|
|
57
|
+
});
|
|
58
|
+
return () => unsubscribe();
|
|
59
|
+
}, [client]);
|
|
47
60
|
useEffect(() => {
|
|
48
61
|
if (!client)
|
|
49
62
|
return;
|
|
@@ -63,17 +76,19 @@ export const PluginProvider = ({ children, pluginId, settings }) => {
|
|
|
63
76
|
setStandaloneClient(true);
|
|
64
77
|
}) }));
|
|
65
78
|
}
|
|
66
|
-
if (!client) {
|
|
79
|
+
if (!client || !userInfo) {
|
|
67
80
|
return '';
|
|
68
81
|
}
|
|
69
|
-
return (_jsxs(PluginContext.Provider, { value: client, children: [!(settings === null || settings === void 0 ? void 0 : settings.disableContextMenu) && !isSidebar && !isSettings && _jsx(ContextMenu, { client: client }), children] }));
|
|
82
|
+
return (_jsxs(PluginContext.Provider, { value: { client, userInfo }, children: [!(settings === null || settings === void 0 ? void 0 : settings.disableContextMenu) && !isSidebar && !isSettings && _jsx(ContextMenu, { client: client }), children] }));
|
|
70
83
|
};
|
|
71
84
|
export const useRimori = () => {
|
|
72
85
|
const context = useContext(PluginContext);
|
|
73
86
|
if (context === null) {
|
|
74
87
|
throw new Error('useRimori must be used within an PluginProvider');
|
|
75
88
|
}
|
|
76
|
-
|
|
89
|
+
// Return client with userInfo at root level for easy access
|
|
90
|
+
// Maintains backwards compatibility - all client properties are still accessible
|
|
91
|
+
return Object.assign(context.client, { userInfo: context.userInfo });
|
|
77
92
|
};
|
|
78
93
|
function getUrlParam(name) {
|
|
79
94
|
// First try to get from URL hash query params (for compatibility)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rimori/react-client",
|
|
3
|
-
"version": "0.4.0",
|
|
3
|
+
"version": "0.4.2-next.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"format": "prettier --write ."
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
|
-
"@rimori/client": "^2.
|
|
26
|
+
"@rimori/client": "^2.5.3",
|
|
27
27
|
"react": "^18.1.0",
|
|
28
28
|
"react-dom": "^18.1.0"
|
|
29
29
|
},
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@eslint/js": "^9.37.0",
|
|
37
|
-
"@rimori/client": "^2.
|
|
37
|
+
"@rimori/client": "^2.5.3",
|
|
38
38
|
"@types/react": "^18.3.21",
|
|
39
39
|
"eslint-config-prettier": "^10.1.8",
|
|
40
40
|
"eslint-plugin-prettier": "^5.5.4",
|
|
@@ -152,8 +152,14 @@ export const AudioPlayer: React.FC<AudioPlayerProps> = ({
|
|
|
152
152
|
<div className="group relative">
|
|
153
153
|
<div className="flex flex-row items-end">
|
|
154
154
|
{!hide && (
|
|
155
|
-
<button className="text-gray-
|
|
156
|
-
{isLoading ?
|
|
155
|
+
<button className="text-gray-400" onClick={togglePlayback} disabled={isLoading}>
|
|
156
|
+
{isLoading ? (
|
|
157
|
+
<Spinner size="25px" />
|
|
158
|
+
) : isPlaying ? (
|
|
159
|
+
<FaStopCircle size="25px" />
|
|
160
|
+
) : (
|
|
161
|
+
<FaPlayCircle size="25px" />
|
|
162
|
+
)}
|
|
157
163
|
</button>
|
|
158
164
|
)}
|
|
159
165
|
{enableSpeedAdjustment && (
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { createContext, useContext, ReactNode, useEffect, useState } from 'react';
|
|
2
2
|
import { EventBusHandler, RimoriClient, StandaloneClient } from '@rimori/client';
|
|
3
|
+
import type { UserInfo } from '@rimori/client';
|
|
3
4
|
import ContextMenu from '../components/ContextMenu';
|
|
4
5
|
import { useTheme } from '../hooks/ThemeSetter';
|
|
5
6
|
|
|
@@ -11,13 +12,19 @@ interface PluginProviderProps {
|
|
|
11
12
|
};
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
interface PluginContextValue {
|
|
16
|
+
client: RimoriClient;
|
|
17
|
+
userInfo: UserInfo;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const PluginContext = createContext<PluginContextValue | null>(null);
|
|
15
21
|
|
|
16
22
|
export const PluginProvider: React.FC<PluginProviderProps> = ({ children, pluginId, settings }) => {
|
|
17
23
|
const [client, setClient] = useState<RimoriClient | null>(null);
|
|
18
24
|
const [standaloneClient, setStandaloneClient] = useState<StandaloneClient | boolean>(false);
|
|
19
25
|
const [applicationMode, setApplicationMode] = useState<string | null>(null);
|
|
20
26
|
const [theme, setTheme] = useState<string | null>(null);
|
|
27
|
+
const [userInfo, setUserInfo] = useState<UserInfo | null>(null);
|
|
21
28
|
|
|
22
29
|
useTheme(theme);
|
|
23
30
|
|
|
@@ -39,6 +46,9 @@ export const PluginProvider: React.FC<PluginProviderProps> = ({ children, plugin
|
|
|
39
46
|
if ((!standaloneDetected && !client) || (standaloneDetected && standaloneClient === true)) {
|
|
40
47
|
void RimoriClient.getInstance(pluginId).then((client) => {
|
|
41
48
|
setClient(client);
|
|
49
|
+
// Set initial userInfo
|
|
50
|
+
setUserInfo(client.plugin.getUserInfo());
|
|
51
|
+
|
|
42
52
|
// Get applicationMode and theme from MessageChannel query params
|
|
43
53
|
if (!standaloneDetected) {
|
|
44
54
|
const mode = client.getQueryParam('applicationMode');
|
|
@@ -51,6 +61,18 @@ export const PluginProvider: React.FC<PluginProviderProps> = ({ children, plugin
|
|
|
51
61
|
}
|
|
52
62
|
}, [pluginId, standaloneClient, client]);
|
|
53
63
|
|
|
64
|
+
// Listen for RimoriInfo updates and update userInfo
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
if (!client) return;
|
|
67
|
+
|
|
68
|
+
const unsubscribe = client.plugin.onRimoriInfoUpdate((info) => {
|
|
69
|
+
console.log('[PluginProvider] Received RimoriInfo update, updating userInfo');
|
|
70
|
+
setUserInfo(info.profile);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
return (): void => unsubscribe();
|
|
74
|
+
}, [client]);
|
|
75
|
+
|
|
54
76
|
useEffect(() => {
|
|
55
77
|
if (!client) return;
|
|
56
78
|
if (isSidebar) return; //sidebar pages should not report url changes
|
|
@@ -74,24 +96,26 @@ export const PluginProvider: React.FC<PluginProviderProps> = ({ children, plugin
|
|
|
74
96
|
);
|
|
75
97
|
}
|
|
76
98
|
|
|
77
|
-
if (!client) {
|
|
99
|
+
if (!client || !userInfo) {
|
|
78
100
|
return '';
|
|
79
101
|
}
|
|
80
102
|
|
|
81
103
|
return (
|
|
82
|
-
<PluginContext.Provider value={client}>
|
|
104
|
+
<PluginContext.Provider value={{ client, userInfo }}>
|
|
83
105
|
{!settings?.disableContextMenu && !isSidebar && !isSettings && <ContextMenu client={client} />}
|
|
84
106
|
{children}
|
|
85
107
|
</PluginContext.Provider>
|
|
86
108
|
);
|
|
87
109
|
};
|
|
88
110
|
|
|
89
|
-
export const useRimori = () => {
|
|
111
|
+
export const useRimori = (): RimoriClient & { userInfo: UserInfo } => {
|
|
90
112
|
const context = useContext(PluginContext);
|
|
91
113
|
if (context === null) {
|
|
92
114
|
throw new Error('useRimori must be used within an PluginProvider');
|
|
93
115
|
}
|
|
94
|
-
|
|
116
|
+
// Return client with userInfo at root level for easy access
|
|
117
|
+
// Maintains backwards compatibility - all client properties are still accessible
|
|
118
|
+
return Object.assign(context.client, { userInfo: context.userInfo }) as RimoriClient & { userInfo: UserInfo };
|
|
95
119
|
};
|
|
96
120
|
|
|
97
121
|
function getUrlParam(name: string): string | null {
|
|
@@ -193,3 +217,5 @@ function StandaloneAuth({ onLogin }: { onLogin: (user: string, password: string)
|
|
|
193
217
|
</div>
|
|
194
218
|
);
|
|
195
219
|
}
|
|
220
|
+
|
|
221
|
+
// test 123
|
package/tsconfig.json
CHANGED
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
name: Pre-Release Rimori React Client
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: [dev]
|
|
6
|
-
paths:
|
|
7
|
-
- '**'
|
|
8
|
-
- '!.github/workflows/**'
|
|
9
|
-
|
|
10
|
-
jobs:
|
|
11
|
-
pre-release:
|
|
12
|
-
runs-on: ubuntu-latest
|
|
13
|
-
permissions:
|
|
14
|
-
contents: write
|
|
15
|
-
id-token: write
|
|
16
|
-
|
|
17
|
-
steps:
|
|
18
|
-
- name: Checkout repository
|
|
19
|
-
uses: actions/checkout@v4
|
|
20
|
-
with:
|
|
21
|
-
token: ${{ secrets.GITHUB_TOKEN }}
|
|
22
|
-
fetch-depth: 0
|
|
23
|
-
|
|
24
|
-
- name: Setup Node.js
|
|
25
|
-
uses: actions/setup-node@v4
|
|
26
|
-
with:
|
|
27
|
-
node-version: '20'
|
|
28
|
-
registry-url: 'https://registry.npmjs.org'
|
|
29
|
-
cache: 'yarn'
|
|
30
|
-
cache-dependency-path: yarn.lock
|
|
31
|
-
|
|
32
|
-
- name: Update npm
|
|
33
|
-
run: npm install -g npm@latest
|
|
34
|
-
|
|
35
|
-
- name: Get latest @rimori/client@next version
|
|
36
|
-
id: client-version
|
|
37
|
-
run: |
|
|
38
|
-
VERSION=$(npm view @rimori/client@next version 2>/dev/null || echo "")
|
|
39
|
-
if [ -z "$VERSION" ]; then
|
|
40
|
-
echo "⚠️ Warning: No @rimori/client@next version found. Using current dependency version."
|
|
41
|
-
VERSION=$(node -p "require('./package.json').peerDependencies['@rimori/client'] || require('./package.json').devDependencies['@rimori/client']")
|
|
42
|
-
# Remove ^ prefix if present
|
|
43
|
-
VERSION="${VERSION#^}"
|
|
44
|
-
fi
|
|
45
|
-
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
|
46
|
-
echo "Using @rimori/client version: $VERSION"
|
|
47
|
-
|
|
48
|
-
- name: Update @rimori/client dependency
|
|
49
|
-
run: |
|
|
50
|
-
# Update both peerDependencies and devDependencies
|
|
51
|
-
yarn add "@rimori/client@${{ steps.client-version.outputs.version }}" --dev --exact
|
|
52
|
-
# Also update peerDependencies using node
|
|
53
|
-
node -e "const fs = require('fs'); const pkg = JSON.parse(fs.readFileSync('package.json')); if (pkg.peerDependencies && pkg.peerDependencies['@rimori/client']) { pkg.peerDependencies['@rimori/client'] = '${{ steps.client-version.outputs.version }}'; fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n'); }"
|
|
54
|
-
echo "Updated @rimori/client to ${{ steps.client-version.outputs.version }}"
|
|
55
|
-
|
|
56
|
-
- name: Install dependencies
|
|
57
|
-
run: yarn install
|
|
58
|
-
|
|
59
|
-
- name: Build react-client (TypeScript verification)
|
|
60
|
-
run: yarn build
|
|
61
|
-
|
|
62
|
-
- name: Calculate next pre-release version
|
|
63
|
-
id: version
|
|
64
|
-
run: |
|
|
65
|
-
# Read current version from package.json (may be base or pre-release)
|
|
66
|
-
CURRENT_VERSION=$(node -p "require('./package.json').version")
|
|
67
|
-
|
|
68
|
-
# Extract base version (strip any pre-release suffix)
|
|
69
|
-
# Examples: "0.3.0" -> "0.3.0", "0.3.0-next.5" -> "0.3.0"
|
|
70
|
-
if [[ "$CURRENT_VERSION" =~ ^([0-9]+\.[0-9]+\.[0-9]+) ]]; then
|
|
71
|
-
BASE_VERSION="${BASH_REMATCH[1]}"
|
|
72
|
-
else
|
|
73
|
-
BASE_VERSION="$CURRENT_VERSION"
|
|
74
|
-
fi
|
|
75
|
-
|
|
76
|
-
# Try to get latest next version from npm
|
|
77
|
-
PACKAGE_NAME="@rimori/react-client"
|
|
78
|
-
LATEST_NEXT=$(npm view ${PACKAGE_NAME}@next version 2>/dev/null || echo "none")
|
|
79
|
-
|
|
80
|
-
if [ "$LATEST_NEXT" != "none" ]; then
|
|
81
|
-
# Extract base version and pre-release number from latest next version
|
|
82
|
-
# Example: "0.3.0-next.5" -> extract "0.3.0" and "5"
|
|
83
|
-
if [[ "$LATEST_NEXT" =~ ^([0-9]+\.[0-9]+\.[0-9]+)-next\.([0-9]+)$ ]]; then
|
|
84
|
-
LATEST_BASE="${BASH_REMATCH[1]}"
|
|
85
|
-
PRERELEASE_NUM="${BASH_REMATCH[2]}"
|
|
86
|
-
|
|
87
|
-
# If base version changed, reset to 1, otherwise increment
|
|
88
|
-
if [ "$LATEST_BASE" != "$BASE_VERSION" ]; then
|
|
89
|
-
NEW_NUM=1
|
|
90
|
-
else
|
|
91
|
-
NEW_NUM=$((PRERELEASE_NUM + 1))
|
|
92
|
-
fi
|
|
93
|
-
else
|
|
94
|
-
# Fallback: if format doesn't match, start at 1
|
|
95
|
-
NEW_NUM=1
|
|
96
|
-
fi
|
|
97
|
-
else
|
|
98
|
-
# First pre-release
|
|
99
|
-
NEW_NUM=1
|
|
100
|
-
fi
|
|
101
|
-
|
|
102
|
-
NEW_VERSION="${BASE_VERSION}-next.${NEW_NUM}"
|
|
103
|
-
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
|
|
104
|
-
echo "Base version: $BASE_VERSION"
|
|
105
|
-
echo "Calculated next version: $NEW_VERSION"
|
|
106
|
-
|
|
107
|
-
- name: Update package.json version
|
|
108
|
-
run: |
|
|
109
|
-
# Use node to update version directly (yarn version creates git tags)
|
|
110
|
-
node -e "const fs = require('fs'); const pkg = JSON.parse(fs.readFileSync('package.json')); pkg.version = '${{ steps.version.outputs.new_version }}'; fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');"
|
|
111
|
-
|
|
112
|
-
- name: Publish to npm
|
|
113
|
-
run: npm publish --tag next --access public
|
|
114
|
-
# Uses OIDC token automatically (no NODE_AUTH_TOKEN needed)
|
|
115
|
-
# Requires npm 11.5.1+ and id-token: write permission (already set)
|
|
116
|
-
|
|
117
|
-
- name: Output published version
|
|
118
|
-
run: |
|
|
119
|
-
echo "✅ Published @rimori/react-client@${{ steps.version.outputs.new_version }} to npm with @next tag"
|
|
120
|
-
echo "Using @rimori/client@${{ steps.client-version.outputs.version }}"
|
|
121
|
-
|
|
122
|
-
- name: Create git tag
|
|
123
|
-
run: |
|
|
124
|
-
git config --local user.email "action@github.com"
|
|
125
|
-
git config --local user.name "GitHub Action"
|
|
126
|
-
git tag "v${{ steps.version.outputs.new_version }}" -m "Pre-release v${{ steps.version.outputs.new_version }}"
|
|
127
|
-
git push origin "v${{ steps.version.outputs.new_version }}"
|
|
128
|
-
echo "🏷️ Created and pushed tag v${{ steps.version.outputs.new_version }}"
|
|
129
|
-
|
|
130
|
-
- name: Notify Slack
|
|
131
|
-
if: always()
|
|
132
|
-
uses: slackapi/slack-github-action@v1.24.0
|
|
133
|
-
with:
|
|
134
|
-
channel-id: ${{ secrets.SLACK_CHANNEL_ID }}
|
|
135
|
-
payload: |
|
|
136
|
-
{
|
|
137
|
-
"text": "Pre-Release Pipeline Status",
|
|
138
|
-
"blocks": [
|
|
139
|
-
{
|
|
140
|
-
"type": "section",
|
|
141
|
-
"text": {
|
|
142
|
-
"type": "mrkdwn",
|
|
143
|
-
"text": "📦 *@rimori/react-client Pre-Release*\n\n*Branch:* ${{ github.ref_name }}\n*Version:* ${{ steps.version.outputs.new_version }}\n*Using @rimori/client:* ${{ steps.client-version.outputs.version }}\n*Author:* ${{ github.actor }}\n*Pipeline:* <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Run>\n\n${{ job.status == 'success' && '✅ Successfully published to npm with @next tag!' || '❌ Pipeline failed. Check the logs for details.' }}"
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
]
|
|
147
|
-
}
|
|
148
|
-
env:
|
|
149
|
-
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Markdown } from 'tiptap-markdown';
|
|
3
|
-
import StarterKit from '@tiptap/starter-kit';
|
|
4
|
-
import { PiCodeBlock } from 'react-icons/pi';
|
|
5
|
-
import { TbBlockquote } from 'react-icons/tb';
|
|
6
|
-
import { GoListOrdered } from 'react-icons/go';
|
|
7
|
-
import { AiOutlineUnorderedList } from 'react-icons/ai';
|
|
8
|
-
import { EditorProvider, useCurrentEditor } from '@tiptap/react';
|
|
9
|
-
import { LuHeading1, LuHeading2, LuHeading3 } from 'react-icons/lu';
|
|
10
|
-
import { FaBold, FaCode, FaItalic, FaParagraph, FaStrikethrough } from 'react-icons/fa';
|
|
11
|
-
const EditorButton = ({ action, isActive, label, disabled }) => {
|
|
12
|
-
const { editor } = useCurrentEditor();
|
|
13
|
-
if (!editor) {
|
|
14
|
-
return null;
|
|
15
|
-
}
|
|
16
|
-
if (action.includes('heading')) {
|
|
17
|
-
const level = parseInt(action[action.length - 1]);
|
|
18
|
-
return (_jsx("button", { onClick: () => editor.chain().focus().toggleHeading({ level: level }).run(), className: `pl-2 ${isActive ? 'is-active' : ''}`, children: label }));
|
|
19
|
-
}
|
|
20
|
-
return (_jsx("button", { onClick: () => editor.chain().focus()[action]().run(), disabled: disabled ? !editor.can().chain().focus()[action]().run() : false, className: `pl-2 ${isActive ? 'is-active' : ''}`, children: label }));
|
|
21
|
-
};
|
|
22
|
-
const MenuBar = () => {
|
|
23
|
-
const { editor } = useCurrentEditor();
|
|
24
|
-
if (!editor) {
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
return (_jsxs("div", { className: "bg-gray-400 dark:bg-gray-800 dark:text-white text-lg flex flex-row flex-wrap items-center p-1", children: [_jsx(EditorButton, { action: "toggleBold", isActive: editor.isActive('bold'), label: _jsx(FaBold, {}), disabled: true }), _jsx(EditorButton, { action: "toggleItalic", isActive: editor.isActive('italic'), label: _jsx(FaItalic, {}), disabled: true }), _jsx(EditorButton, { action: "toggleStrike", isActive: editor.isActive('strike'), label: _jsx(FaStrikethrough, {}), disabled: true }), _jsx(EditorButton, { action: "toggleCode", isActive: editor.isActive('code'), label: _jsx(FaCode, {}), disabled: true }), _jsx(EditorButton, { action: "setParagraph", isActive: editor.isActive('paragraph'), label: _jsx(FaParagraph, {}) }), _jsx(EditorButton, { action: "setHeading1", isActive: editor.isActive('heading', { level: 1 }), label: _jsx(LuHeading1, { size: '24px' }) }), _jsx(EditorButton, { action: "setHeading2", isActive: editor.isActive('heading', { level: 2 }), label: _jsx(LuHeading2, { size: '24px' }) }), _jsx(EditorButton, { action: "setHeading3", isActive: editor.isActive('heading', { level: 3 }), label: _jsx(LuHeading3, { size: '24px' }) }), _jsx(EditorButton, { action: "toggleBulletList", isActive: editor.isActive('bulletList'), label: _jsx(AiOutlineUnorderedList, { size: '24px' }) }), _jsx(EditorButton, { action: "toggleOrderedList", isActive: editor.isActive('orderedList'), label: _jsx(GoListOrdered, { size: '24px' }) }), _jsx(EditorButton, { action: "toggleCodeBlock", isActive: editor.isActive('codeBlock'), label: _jsx(PiCodeBlock, { size: '24px' }) }), _jsx(EditorButton, { action: "toggleBlockquote", isActive: editor.isActive('blockquote'), label: _jsx(TbBlockquote, { size: '24px' }) })] }));
|
|
28
|
-
};
|
|
29
|
-
const extensions = [
|
|
30
|
-
StarterKit.configure({
|
|
31
|
-
bulletList: {
|
|
32
|
-
HTMLAttributes: {
|
|
33
|
-
class: 'list-disc list-inside dark:text-white p-1 mt-1 [&_li]:mb-1 [&_p]:inline m-0',
|
|
34
|
-
},
|
|
35
|
-
},
|
|
36
|
-
orderedList: {
|
|
37
|
-
HTMLAttributes: {
|
|
38
|
-
className: 'list-decimal list-inside dark:text-white p-1 mt-1 [&_li]:mb-1 [&_p]:inline m-0',
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
}),
|
|
42
|
-
Markdown,
|
|
43
|
-
];
|
|
44
|
-
export const MarkdownEditor = (props) => {
|
|
45
|
-
return (_jsx("div", { className: 'text-md border border-gray-800 overflow-hidden ' + props.className, style: { borderWidth: props.editable ? 1 : 0 }, children: _jsx(EditorProvider, { slotBefore: props.editable ? _jsx(MenuBar, {}) : null, extensions: extensions, content: props.content, editable: props.editable, onUpdate: (e) => {
|
|
46
|
-
props.onUpdate && props.onUpdate(e.editor.storage.markdown.getMarkdown());
|
|
47
|
-
} }, (props.editable ? 'editable' : 'readonly') + props.content) }));
|
|
48
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
interface Props {
|
|
2
|
-
iconSize?: string;
|
|
3
|
-
className?: string;
|
|
4
|
-
disabled?: boolean;
|
|
5
|
-
loading?: boolean;
|
|
6
|
-
enablePushToTalk?: boolean;
|
|
7
|
-
onRecordingStatusChange: (running: boolean) => void;
|
|
8
|
-
onVoiceRecorded: (message: string) => void;
|
|
9
|
-
}
|
|
10
|
-
export declare const VoiceRecorder: import("react").ForwardRefExoticComponent<Props & import("react").RefAttributes<unknown>>;
|
|
11
|
-
export {};
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
11
|
-
import { useRimori } from '../../../providers/PluginProvider';
|
|
12
|
-
import { FaMicrophone, FaSpinner } from 'react-icons/fa6';
|
|
13
|
-
import { AudioController } from '@rimori/client';
|
|
14
|
-
import { useState, useRef, forwardRef, useImperativeHandle, useEffect } from 'react';
|
|
15
|
-
export const VoiceRecorder = forwardRef(({ onVoiceRecorded, iconSize, className, disabled, loading, onRecordingStatusChange, enablePushToTalk = false, }, ref) => {
|
|
16
|
-
const [isRecording, setIsRecording] = useState(false);
|
|
17
|
-
const [internalIsProcessing, setInternalIsProcessing] = useState(false);
|
|
18
|
-
const audioControllerRef = useRef(null);
|
|
19
|
-
const { ai, plugin } = useRimori();
|
|
20
|
-
// Ref for latest onVoiceRecorded callback
|
|
21
|
-
const onVoiceRecordedRef = useRef(onVoiceRecorded);
|
|
22
|
-
useEffect(() => {
|
|
23
|
-
onVoiceRecordedRef.current = onVoiceRecorded;
|
|
24
|
-
}, [onVoiceRecorded]);
|
|
25
|
-
const startRecording = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
26
|
-
try {
|
|
27
|
-
if (!audioControllerRef.current) {
|
|
28
|
-
audioControllerRef.current = new AudioController(plugin.pluginId);
|
|
29
|
-
}
|
|
30
|
-
yield audioControllerRef.current.startRecording();
|
|
31
|
-
setIsRecording(true);
|
|
32
|
-
onRecordingStatusChange(true);
|
|
33
|
-
}
|
|
34
|
-
catch (error) {
|
|
35
|
-
console.error('Failed to start recording:', error);
|
|
36
|
-
// Handle permission denied or other errors
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
const stopRecording = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
40
|
-
try {
|
|
41
|
-
if (audioControllerRef.current && isRecording) {
|
|
42
|
-
const audioResult = yield audioControllerRef.current.stopRecording();
|
|
43
|
-
// console.log("audioResult: ", audioResult);
|
|
44
|
-
setInternalIsProcessing(true);
|
|
45
|
-
// Play the recorded audio from the Blob
|
|
46
|
-
// const blobUrl = URL.createObjectURL(audioResult.recording);
|
|
47
|
-
// const audioRef = new Audio(blobUrl);
|
|
48
|
-
// audioRef.onended = () => URL.revokeObjectURL(blobUrl);
|
|
49
|
-
// audioRef.play().catch((e) => console.error('Playback error:', e));
|
|
50
|
-
// console.log("audioBlob: ", audioResult.recording);
|
|
51
|
-
const text = yield ai.getTextFromVoice(audioResult.recording);
|
|
52
|
-
// console.log("stt result", text);
|
|
53
|
-
// throw new Error("test");
|
|
54
|
-
setInternalIsProcessing(false);
|
|
55
|
-
onVoiceRecordedRef.current(text);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
catch (error) {
|
|
59
|
-
console.error('Failed to stop recording:', error);
|
|
60
|
-
}
|
|
61
|
-
finally {
|
|
62
|
-
setIsRecording(false);
|
|
63
|
-
onRecordingStatusChange(false);
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
useImperativeHandle(ref, () => ({
|
|
67
|
-
startRecording,
|
|
68
|
-
stopRecording,
|
|
69
|
-
}));
|
|
70
|
-
// push to talk feature
|
|
71
|
-
const spacePressedRef = useRef(false);
|
|
72
|
-
useEffect(() => {
|
|
73
|
-
if (!enablePushToTalk)
|
|
74
|
-
return;
|
|
75
|
-
const handleKeyDown = (event) => __awaiter(void 0, void 0, void 0, function* () {
|
|
76
|
-
if (event.code === 'Space' && !spacePressedRef.current) {
|
|
77
|
-
spacePressedRef.current = true;
|
|
78
|
-
yield startRecording();
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
const handleKeyUp = (event) => {
|
|
82
|
-
if (event.code === 'Space' && spacePressedRef.current) {
|
|
83
|
-
spacePressedRef.current = false;
|
|
84
|
-
stopRecording();
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
window.addEventListener('keydown', handleKeyDown);
|
|
88
|
-
window.addEventListener('keyup', handleKeyUp);
|
|
89
|
-
return () => {
|
|
90
|
-
window.removeEventListener('keydown', handleKeyDown);
|
|
91
|
-
window.removeEventListener('keyup', handleKeyUp);
|
|
92
|
-
};
|
|
93
|
-
}, [enablePushToTalk]);
|
|
94
|
-
return (_jsx("button", { className: 'flex flex-row justify-center items-center rounded-full mx-auto disabled:opacity-50 ' + className, onClick: isRecording ? stopRecording : startRecording, disabled: disabled || loading || internalIsProcessing, children: loading || internalIsProcessing ? (_jsx(FaSpinner, { className: "animate-spin" })) : (_jsx(FaMicrophone, { size: iconSize, className: isRecording ? 'text-red-600' : '' })) }));
|
|
95
|
-
});
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react';
|
|
2
|
-
export function useTheme(theme) {
|
|
3
|
-
const [isDark, setIsDark] = useState(false);
|
|
4
|
-
useEffect(() => {
|
|
5
|
-
const root = document.documentElement;
|
|
6
|
-
const nextIsDark = isDarkTheme(theme);
|
|
7
|
-
setIsDark(nextIsDark);
|
|
8
|
-
root.classList.add('dark:text-gray-200');
|
|
9
|
-
if (nextIsDark) {
|
|
10
|
-
root.setAttribute('data-theme', 'dark');
|
|
11
|
-
root.classList.add('dark', 'dark:bg-gray-950');
|
|
12
|
-
root.style.background = 'hsl(var(--background))';
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
root.removeAttribute('data-theme');
|
|
16
|
-
root.classList.remove('dark', 'dark:bg-gray-950');
|
|
17
|
-
root.style.background = '';
|
|
18
|
-
}, [theme]);
|
|
19
|
-
return isDark;
|
|
20
|
-
}
|
|
21
|
-
export function isDarkTheme(theme) {
|
|
22
|
-
// If no theme provided, try to get from URL as fallback (for standalone mode)
|
|
23
|
-
if (!theme) {
|
|
24
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
25
|
-
theme = urlParams.get('theme');
|
|
26
|
-
}
|
|
27
|
-
if (!theme || theme === 'system') {
|
|
28
|
-
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
29
|
-
}
|
|
30
|
-
return theme === 'dark';
|
|
31
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
export function isFullscreen() {
|
|
2
|
-
return !!document.fullscreenElement;
|
|
3
|
-
}
|
|
4
|
-
export function triggerFullscreen(onStateChange, selector) {
|
|
5
|
-
document.addEventListener('fullscreenchange', () => {
|
|
6
|
-
onStateChange(isFullscreen());
|
|
7
|
-
});
|
|
8
|
-
try {
|
|
9
|
-
const ref = document.querySelector(selector || '#root');
|
|
10
|
-
if (!isFullscreen()) {
|
|
11
|
-
// @ts-ignore
|
|
12
|
-
void (ref.requestFullscreen() || ref.webkitRequestFullscreen());
|
|
13
|
-
}
|
|
14
|
-
else {
|
|
15
|
-
// @ts-ignore
|
|
16
|
-
void (document.exitFullscreen() || document.webkitExitFullscreen());
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
catch (error) {
|
|
20
|
-
console.error('Failed to enter fullscreen', error.message);
|
|
21
|
-
}
|
|
22
|
-
onStateChange(isFullscreen());
|
|
23
|
-
}
|