Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .env
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Common Environment Variables
NEXT_PUBLIC_YORKIE_VERSION='0.6.48'
NEXT_PUBLIC_YORKIE_JS_VERSION='0.6.48'
NEXT_PUBLIC_YORKIE_VERSION='0.6.49'
NEXT_PUBLIC_YORKIE_JS_VERSION='0.6.49'
NEXT_PUBLIC_YORKIE_IOS_VERSION='0.6.35'
NEXT_PUBLIC_YORKIE_ANDROID_VERSION='0.6.35'
NEXT_PUBLIC_DASHBOARD_PATH='/dashboard'
NEXT_PUBLIC_JS_SDK_URL='https://cdn.jsdelivr.net/npm/@yorkie-js/sdk@0.6.48/dist/yorkie-js-sdk.js'
NEXT_PUBLIC_JS_SDK_URL='https://cdn.jsdelivr.net/npm/@yorkie-js/sdk@0.6.49/dist/yorkie-js-sdk.js'
NEXT_PUBLIC_STATUS_URL='https://stats.uptimerobot.com/otOOggryMR'

# Development Environment Variables
Expand Down
2 changes: 1 addition & 1 deletion examples/nextjs-presence/fileInfo.ts

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion examples/nextjs-scheduler/fileInfo.ts

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion examples/nextjs-todolist/fileInfo.ts

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion examples/profile-stack/fileInfo.ts

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion examples/react-document-limit/fileInfo.ts

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion examples/react-flow/fileInfo.ts

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions examples/react-presence/fileInfo.ts

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions examples/react-revision/fileInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import { DirectoryInfo } from '@/utils/exampleFileUtils';
export const FILE_INFO: DirectoryInfo = {"isFile":false,"name":"react-revision","path":"/","children":[{"isFile":false,"name":"src","path":"/src","children":[{"isFile":true,"isOpen":false,"language":"css","name":"App.css","path":"/src/App.css","content":"* {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n}\n\nbody {\n font-family:\n -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n background: #f5f5f5;\n color: #333;\n padding: 20px;\n}\n\n.app {\n max-width: 1000px;\n margin: 0 auto;\n}\n\nh1 {\n margin-bottom: 20px;\n font-size: 24px;\n}\n\nh2 {\n font-size: 18px;\n margin-bottom: 12px;\n}\n\n.loading,\n.error {\n text-align: center;\n padding: 40px;\n font-size: 16px;\n}\n\n.error {\n color: #d32f2f;\n}\n\n.editor-container {\n display: flex;\n gap: 20px;\n}\n\n.editor-section {\n flex: 1;\n}\n\n.editor {\n width: 100%;\n height: 400px;\n padding: 12px;\n border: 1px solid #ddd;\n border-radius: 6px;\n font-family: inherit;\n font-size: 14px;\n resize: vertical;\n outline: none;\n}\n\n.editor:focus {\n border-color: #1976d2;\n}\n\n.revision-section {\n width: 340px;\n flex-shrink: 0;\n}\n\n.revision-panel {\n background: #fff;\n border: 1px solid #ddd;\n border-radius: 6px;\n padding: 16px;\n}\n\n.create-revision {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-bottom: 16px;\n padding-bottom: 16px;\n border-bottom: 1px solid #eee;\n}\n\n.create-revision input {\n padding: 8px;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-size: 13px;\n}\n\n.create-revision button {\n padding: 8px 12px;\n background: #1976d2;\n color: #fff;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 13px;\n}\n\n.create-revision button:disabled {\n background: #bbb;\n cursor: not-allowed;\n}\n\n.revision-list {\n list-style: none;\n max-height: 400px;\n overflow-y: auto;\n}\n\n.revision-item {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n padding: 10px 0;\n border-bottom: 1px solid #eee;\n}\n\n.revision-info strong {\n display: block;\n font-size: 14px;\n}\n\n.revision-info p {\n font-size: 12px;\n color: #666;\n margin-top: 2px;\n}\n\n.revision-info small {\n font-size: 11px;\n color: #999;\n}\n\n.revision-actions {\n display: flex;\n gap: 4px;\n flex-shrink: 0;\n}\n\n.revision-actions button {\n padding: 4px 8px;\n border: 1px solid #ddd;\n border-radius: 4px;\n background: #fff;\n cursor: pointer;\n font-size: 12px;\n}\n\n.revision-actions button:hover {\n background: #f0f0f0;\n}\n\n.revision-empty {\n text-align: center;\n padding: 20px;\n color: #999;\n font-size: 13px;\n}\n\n.preview-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 100;\n}\n\n.preview-modal {\n background: #fff;\n border-radius: 8px;\n padding: 20px;\n max-width: 600px;\n width: 90%;\n max-height: 80vh;\n overflow-y: auto;\n}\n\n.preview-modal h3 {\n margin-bottom: 12px;\n}\n\n.preview-modal pre {\n background: #f5f5f5;\n padding: 12px;\n border-radius: 4px;\n font-size: 13px;\n overflow-x: auto;\n margin-bottom: 12px;\n}\n\n.preview-modal button {\n padding: 8px 16px;\n background: #666;\n color: #fff;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n}\n"},{"isFile":true,"isOpen":false,"language":"tsx","name":"App.tsx","path":"/src/App.tsx","content":"import { YorkieProvider, DocumentProvider, useDocument } from '@yorkie-js/react';\nimport RevisionPanel from './RevisionPanel';\nimport './App.css';\n\nconst DOC_KEY = `react-revision-${new Date().toISOString().slice(0, 10)}`;\n\ninterface DocType {\n content: string;\n}\n\nfunction NoteEditor() {\n const { root, update, loading, error } = useDocument<DocType>();\n\n if (loading) return <div className=\"loading\">Loading...</div>;\n if (error) return <div className=\"error\">Error: {error.message}</div>;\n\n return (\n <div className=\"app\">\n <h1>Note with Revisions</h1>\n <div className=\"editor-container\">\n <div className=\"editor-section\">\n <h2>Editor</h2>\n <textarea\n className=\"editor\"\n value={root.content}\n onChange={(e) =>\n update((root) => {\n root.content = e.target.value;\n })\n }\n placeholder=\"Start typing your note...\"\n />\n </div>\n <div className=\"revision-section\">\n <RevisionPanel />\n </div>\n </div>\n </div>\n );\n}\n\nexport default function App() {\n return (\n <YorkieProvider\n apiKey={import.meta.env.VITE_YORKIE_API_KEY}\n rpcAddr={import.meta.env.VITE_YORKIE_API_ADDR}\n >\n <DocumentProvider docKey={DOC_KEY} initialRoot={{ content: '' }}>\n <NoteEditor />\n </DocumentProvider>\n </YorkieProvider>\n );\n}\n"},{"isFile":true,"isOpen":false,"language":"tsx","name":"RevisionPanel.tsx","path":"/src/RevisionPanel.tsx","content":"import { useState, useEffect } from 'react';\nimport { useRevisions, RevisionSummary } from '@yorkie-js/react';\n\nexport default function RevisionPanel() {\n const { createRevision, listRevisions, getRevision, restoreRevision } =\n useRevisions();\n\n const [revisions, setRevisions] = useState<RevisionSummary[]>([]);\n const [label, setLabel] = useState('');\n const [description, setDescription] = useState('');\n const [preview, setPreview] = useState<string | null>(null);\n const [isLoading, setIsLoading] = useState(false);\n\n const fetchRevisions = async () => {\n try {\n const revs = await listRevisions({ pageSize: 20, isForward: false });\n setRevisions(revs);\n } catch (err) {\n console.error('Failed to fetch revisions:', err);\n }\n };\n\n useEffect(() => {\n fetchRevisions();\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n\n const handleCreate = async () => {\n if (!label.trim()) return;\n setIsLoading(true);\n try {\n await createRevision(label.trim(), description.trim() || undefined);\n setLabel('');\n setDescription('');\n await fetchRevisions();\n } catch (err) {\n console.error('Failed to create revision:', err);\n } finally {\n setIsLoading(false);\n }\n };\n\n const handlePreview = async (revisionID: string) => {\n try {\n const rev = await getRevision(revisionID);\n setPreview(\n rev.snapshot ? JSON.stringify(JSON.parse(rev.snapshot), null, 2) : null,\n );\n } catch (err) {\n console.error('Failed to get revision:', err);\n }\n };\n\n const handleRestore = async (revisionID: string) => {\n if (!confirm('Restore to this revision? Current state will be replaced.')) {\n return;\n }\n setIsLoading(true);\n try {\n await restoreRevision(revisionID);\n await fetchRevisions();\n } catch (err) {\n console.error('Failed to restore revision:', err);\n } finally {\n setIsLoading(false);\n }\n };\n\n return (\n <div className=\"revision-panel\">\n <h2>Revisions</h2>\n\n <div className=\"create-revision\">\n <input\n type=\"text\"\n placeholder=\"Label (e.g. v1.0)\"\n value={label}\n onChange={(e) => setLabel(e.target.value)}\n />\n <input\n type=\"text\"\n placeholder=\"Description (optional)\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n />\n <button onClick={handleCreate} disabled={isLoading || !label.trim()}>\n Save Revision\n </button>\n </div>\n\n <ul className=\"revision-list\">\n {revisions.map((rev) => (\n <li key={rev.id} className=\"revision-item\">\n <div className=\"revision-info\">\n <strong>{rev.label}</strong>\n {rev.description && <p>{rev.description}</p>}\n <small>{new Date(rev.createdAt).toLocaleString()}</small>\n </div>\n <div className=\"revision-actions\">\n <button onClick={() => handlePreview(rev.id)}>Preview</button>\n <button onClick={() => handleRestore(rev.id)}>Restore</button>\n </div>\n </li>\n ))}\n {revisions.length === 0 && (\n <li className=\"revision-empty\">No revisions yet</li>\n )}\n </ul>\n\n {preview && (\n <div className=\"preview-overlay\" onClick={() => setPreview(null)}>\n <div className=\"preview-modal\" onClick={(e) => e.stopPropagation()}>\n <h3>Snapshot Preview</h3>\n <pre>{preview}</pre>\n <button onClick={() => setPreview(null)}>Close</button>\n </div>\n </div>\n )}\n </div>\n );\n}\n"},{"isFile":true,"isOpen":false,"language":"tsx","name":"main.tsx","path":"/src/main.tsx","content":"import ReactDOM from 'react-dom/client';\nimport App from './App';\nimport { StrictMode } from 'react';\n\nReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(\n <StrictMode>\n <App />\n </StrictMode>,\n);\n"},{"isFile":true,"isOpen":false,"language":"typescript","name":"vite-env.d.ts","path":"/src/vite-env.d.ts","content":"/// <reference types=\"vite/client\" />\n"}]},{"isFile":true,"isOpen":false,"language":"","name":".env","path":"/.env","content":"VITE_YORKIE_API_ADDR='http://localhost:8080'\nVITE_YORKIE_API_KEY=''\n"},{"isFile":true,"isOpen":false,"language":"production","name":".env.production","path":"/.env.production","content":""},{"isFile":true,"isOpen":false,"language":"mjs","name":"eslint.config.mjs","path":"/eslint.config.mjs","content":"import { globalIgnores } from 'eslint/config';\nimport prettierPlugin from 'eslint-plugin-prettier';\nimport reactHooks from 'eslint-plugin-react-hooks';\nimport reactRefresh from 'eslint-plugin-react-refresh';\nimport baseConfig from '../../eslint.config.mjs';\n\nconst eslintConfig = [\n ...baseConfig,\n reactHooks.configs['recommended-latest'],\n reactRefresh.configs.vite,\n {\n plugins: {\n prettier: prettierPlugin,\n },\n rules: {\n 'prettier/prettier': 'error',\n 'jsdoc/require-jsdoc': 'off',\n },\n },\n globalIgnores(['dist/*']),\n];\n\nexport default eslintConfig;\n"},{"isFile":true,"isOpen":false,"language":"markup","name":"index.html","path":"/index.html","content":"<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <link rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>Yorkie React Revision Example</title>\n </head>\n <body>\n <div id=\"root\"></div>\n <script type=\"module\" src=\"/src/main.tsx\"></script>\n </body>\n</html>\n"},{"isFile":true,"isOpen":false,"language":"json","name":"package.json","path":"/package.json","content":"{\n \"name\": \"react-revision\",\n \"private\": true,\n \"version\": \"0.0.0\",\n \"type\": \"module\",\n \"scripts\": {\n \"dev\": \"vite\",\n \"build\": \"tsc && vite build\",\n \"preview\": \"vite preview\"\n },\n \"dependencies\": {\n \"@yorkie-js/react\": \"^0.6.49\",\n \"react\": \"^19.2.1\",\n \"react-dom\": \"^19.2.1\"\n },\n \"devDependencies\": {\n \"@types/react\": \"^19.2.7\",\n \"@types/react-dom\": \"^19.2.3\",\n \"@vitejs/plugin-react\": \"^5.1.1\",\n \"eslint-plugin-react-hooks\": \"^7.0.1\",\n \"eslint-plugin-react-refresh\": \"^0.4.24\",\n \"typescript\": \"^5.9.3\",\n \"vite\": \"^7.2.6\",\n \"vite-tsconfig-paths\": \"^5.1.4\"\n }\n}\n"},{"isFile":true,"isOpen":false,"language":"json","name":"tsconfig.json","path":"/tsconfig.json","content":"{\n \"compilerOptions\": {\n \"target\": \"ESNext\",\n \"useDefineForClassFields\": true,\n \"lib\": [\"DOM\", \"DOM.Iterable\", \"ESNext\"],\n \"allowJs\": false,\n \"skipLibCheck\": true,\n \"esModuleInterop\": false,\n \"allowSyntheticDefaultImports\": true,\n \"strict\": true,\n \"forceConsistentCasingInFileNames\": true,\n \"module\": \"ESNext\",\n \"moduleResolution\": \"Node\",\n \"resolveJsonModule\": true,\n \"isolatedModules\": true,\n \"noEmit\": true,\n \"jsx\": \"react-jsx\",\n \"paths\": {\n \"@yorkie-js/sdk/src/*\": [\"../../packages/sdk/src/*\"]\n }\n },\n \"include\": [\"src\"],\n \"references\": [{ \"path\": \"./tsconfig.node.json\" }]\n}\n"},{"isFile":true,"isOpen":false,"language":"json","name":"tsconfig.node.json","path":"/tsconfig.node.json","content":"{\n \"compilerOptions\": {\n \"composite\": true,\n \"module\": \"ESNext\",\n \"moduleResolution\": \"Node\",\n \"allowSyntheticDefaultImports\": true\n },\n \"include\": [\"vite.config.ts\"]\n}\n"},{"isFile":true,"isOpen":false,"language":"typescript","name":"vite.config.ts","path":"/vite.config.ts","content":"import path from 'path';\nimport { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\nimport tsconfigPaths from 'vite-tsconfig-paths';\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n base: '',\n plugins: [react(), tsconfigPaths()],\n resolve: {\n alias: [\n {\n find: '@yorkie-js/react',\n replacement: path.resolve(__dirname, '../../packages/react/src'),\n },\n {\n find: '@yorkie-js/sdk/src',\n replacement: path.resolve(__dirname, '../../packages/sdk/src'),\n },\n ],\n },\n});\n"}]}
2 changes: 1 addition & 1 deletion examples/react-tldraw/fileInfo.ts

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion examples/react-todomvc/fileInfo.ts

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion examples/simultaneous-cursors/fileInfo.ts

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion examples/vanilla-codemirror6/fileInfo.ts

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion examples/vanilla-document-limit/fileInfo.ts

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion examples/vanilla-prosemirror/fileInfo.ts

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion examples/vanilla-quill/fileInfo.ts

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion examples/vuejs-kanban/fileInfo.ts

Large diffs are not rendered by default.

30 changes: 15 additions & 15 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"dependencies": {
"@jsdevtools/rehype-toc": "^3.0.2",
"@svgr/webpack": "^6.5.0",
"@yorkie-js/sdk": "^0.6.48",
"@yorkie-js/sdk": "^0.6.49",
"classnames": "^2.3.2",
"framer-motion": "^7.6.7",
"gray-matter": "^4.0.3",
Expand Down