@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.
@@ -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-500", onClick: togglePlayback, disabled: isLoading, children: isLoading ? _jsx(Spinner, {}) : 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))) })] }))] }) }));
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
- return context;
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.4.0",
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.4.0",
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-500" onClick={togglePlayback} disabled={isLoading}>
156
- {isLoading ? <Spinner /> : isPlaying ? <FaStopCircle size={'25px'} /> : <FaPlayCircle size={'25px'} />}
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
- const PluginContext = createContext<RimoriClient | null>(null);
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
- return context;
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
@@ -4,6 +4,7 @@
4
4
  "rootDir": "src",
5
5
  "module": "ESNext",
6
6
  "target": "ES6",
7
+ "lib": ["ES2019", "DOM"],
7
8
  "declaration": true,
8
9
  "jsx": "react-jsx",
9
10
  "moduleResolution": "node",
@@ -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,8 +0,0 @@
1
- interface Props {
2
- content?: string;
3
- editable: boolean;
4
- className?: string;
5
- onUpdate?: (content: string) => void;
6
- }
7
- export declare const MarkdownEditor: (props: Props) => import("react/jsx-runtime").JSX.Element;
8
- export {};
@@ -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,2 +0,0 @@
1
- export declare function useTheme(theme?: string | null): boolean;
2
- export declare function isDarkTheme(theme?: string | null): boolean;
@@ -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,2 +0,0 @@
1
- export declare function isFullscreen(): boolean;
2
- export declare function triggerFullscreen(onStateChange: (isFullscreen: boolean) => void, selector?: string): void;
@@ -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
- }