Skip to content

Commit

Permalink
feat(joplin-batch): 实现 replace all
Browse files Browse the repository at this point in the history
  • Loading branch information
rxliuli committed Aug 7, 2024
1 parent b7e5505 commit 78f237e
Show file tree
Hide file tree
Showing 41 changed files with 2,837 additions and 578 deletions.
17 changes: 17 additions & 0 deletions packages/joplin-batch/components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "src/webview/styles.css",
"baseColor": "slate",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils"
}
}
10 changes: 8 additions & 2 deletions packages/joplin-batch/jpl.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import path from 'path'
import { defineConfig } from '../jpl-vite/dist'
import { preact } from '@preact/preset-vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
id: 'rxliuli.joplin-batch',
Expand All @@ -15,6 +16,11 @@ export default defineConfig({
categories: ['productivity'],
icons: {},
vite: {
plugins: [preact() as any],
plugins: [react() as any],
resolve: {
alias: {
'@': path.resolve(__dirname, './src/webview'),
},
},
},
})
31 changes: 27 additions & 4 deletions packages/joplin-batch/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,41 @@
"plugin.config.json"
],
"devDependencies": {
"@preact/preset-vite": "^2.8.1",
"@preact/signals": "^1.2.2",
"@types/lodash-es": "^4.17.12",
"@types/node": "^18.7.13",
"@types/react": "^18.3.3",
"@vitejs/plugin-react": "^4.3.1",
"autoprefixer": "^10.4.17",
"joplin-api": "workspace:^",
"joplin-plugin-api": "workspace:^",
"jpl-vite": "workspace:^",
"lodash-es": "^4.17.21",
"postcss": "^8.4.35",
"react": "^18.3.1",
"react-use": "^17.5.1",
"tailwindcss": "^3.4.1",
"typescript": "^5.4.5",
"vite": "^5.1.3",
"vite-node": "^1.6.0",
"preact": "^10.19.5"
"vite-node": "^1.6.0"
},
"dependencies": {
"@liuli-util/react-router": "^0.9.4",
"@preact/signals-react": "^2.1.0",
"@radix-ui/react-dialog": "^1.1.1",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-scroll-area": "^1.1.0",
"@radix-ui/react-separator": "^1.1.0",
"@radix-ui/react-slot": "^1.1.0",
"@types/react-dom": "^18.0.6",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"deepsignal": "^1.5.0",
"lucide-react": "^0.424.0",
"react-dom": "^18.2.0",
"react-string-replace": "^1.1.1",
"react-zoom-pan-pinch": "^3.6.1",
"tailwind-merge": "^2.4.0",
"tailwindcss-animate": "^1.0.7",
"vaul": "^0.9.1"
}
}
6 changes: 6 additions & 0 deletions packages/joplin-batch/postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
64 changes: 45 additions & 19 deletions packages/joplin-batch/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,69 @@
import joplin from 'joplin-plugin-api'
import { IProtocol, onMessage } from './message'
import joplin, { MenuItemLocation, ViewHandle } from 'joplin-plugin-api'
import { IProtocol } from './message'
import { defineExtensionMessaging } from 'jpl-vite/messaging'
import { joplinDataApi } from 'joplin-api'
import { get } from 'lodash-es'

joplin.plugins.register({
onStart: async function () {
await registerSettings()
// 实现这个命令的功能,生成 markdown 文件并使用 git 推送到远端仓库
await registerCommands()
await registerMenus()
},
})

async function registerCommands() {
const dataApi = joplinDataApi({ type: 'plugin' })

let handler: ViewHandle
await joplin.commands.register({
name: 'webviewTest',
label: 'Webview Test',
name: 'joplin-batch.visible',
label: 'Joplin Batch',
async execute() {
const dialogs = joplin.views.dialogs
const handler = await dialogs.create(new Date().toISOString())
await dialogs.addScript(handler, '/webview/index.js')
await dialogs.addScript(handler, '/webview/index.css')
await dialogs.setButtons(handler, [
{
id: 'close',
title: 'Close',
},
])
const { onMessage, sendMessage } = defineExtensionMessaging<IProtocol>(handler)
const panels = joplin.views.panels
if (handler) {
const isVisible = await panels.visible(handler)
await panels.show(handler, !isVisible)
return
}
handler = await panels.create('Batch Utils')
await panels.addScript(handler, '/webview/index.js')
await panels.addScript(handler, '/webview/style.css')
const { onMessage } = defineExtensionMessaging<IProtocol>(handler)
let last = 0
onMessage('add', (a, b) => {
last = a + b
return last
})
onMessage('value', () => last)
await dialogs.open(handler)
onMessage('invokeDataApi', (method, ...args) => {
const f = deepGet(dataApi, method)
if (!f) {
throw new Error(`Method ${method} not found`)
}
return f(...args)
})
await panels.visible(handler)
},
})
}

async function registerMenus() {}
async function registerMenus() {
await joplin.views.menuItems.create('joplin-batch.visible', 'joplin-batch.visible', MenuItemLocation.Tools)
}

function deepGet(obj: any, path: string): Function | undefined {
let result = get(obj, path)

async function registerSettings() {}
// 如果结果是一个函数,我们需要绑定 this
if (typeof result === 'function') {
// 获取父对象
const parentPath = path.split('.').slice(0, -1).join('.')
const parent = parentPath ? get(obj, parentPath) : obj

// 绑定函数到父对象
result = result.bind(parent)
}

return result
}
2 changes: 2 additions & 0 deletions packages/joplin-batch/src/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export interface StorageValues {
export interface IProtocol {
add(a: number, b: number): number
value(): number

invokeDataApi(method: string, ...args: any[]): any
}

export const { sendMessage, onMessage } = defineExtensionMessaging<IProtocol>('joplin-batch')
126 changes: 111 additions & 15 deletions packages/joplin-batch/src/webview/App.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,119 @@
import { useSignal } from '@preact/signals'
import { useInterval } from 'react-use'
import { sendMessage } from '../message'
import { Button } from './components/ui/button'
import { Link, RouteConfig, RouterView, useLocation, useMatch } from '@liuli-util/react-router'
import { DemoView } from './views/DemoView'
import { CheckUnusedResourceView } from './views/UnusedResourceView'
import { SettingsView } from './views/SettingsView'
import { Menu } from 'lucide-react'
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger } from './components/ui/sheet'
import { useDeepSignal } from 'deepsignal/react'
import { useEffect } from 'react'
import { ReplaceAllView } from './views/ReplaceAllView'
import { Separator } from './components/ui/separator'

export default function App() {
const count = useSignal(new Date())

useInterval(() => {
count.value = new Date()
}, 100)
function HomeView() {
return <div>home</div>
}

async function onPostMessage() {
console.log(await sendMessage('add', 1, 2))
type MenuConfig = RouteConfig & {
meta?: {
title: string
}
}
export const routeList: MenuConfig[] = [
{
path: '/',
component: CheckUnusedResourceView,
meta: {
title: 'Unused Resources',
},
},
{
path: '/replace-all',
component: ReplaceAllView,
meta: {
title: 'Replace All',
},
},
{
path: '/settings',
component: SettingsView,
meta: {
title: 'Settings',
},
},
// {
// path: '/demo',
// component: DemoView,
// meta: {
// title: 'Demo',
// },
// },
]

function MenuItem(route: Pick<MenuConfig, 'path' | 'meta'>) {
const loc = useLocation()
return (
<Link to={route.path} key={route.path}>
<Button variant={loc.pathname === route.path ? 'secondary' : 'ghost'} className="w-full justify-start">
{route.meta?.title}
</Button>
</Link>
)
}

function Sidebar() {
const loc = useLocation()
return (
<aside className="space-y-1">
{routeList
.filter((it) => it.meta)
.map((route) => (
<Link to={route.path} key={route.path}>
<Button variant={loc.pathname === route.path ? 'secondary' : 'ghost'} className="w-full justify-start">
{route.meta!.title}
</Button>
</Link>
))}
</aside>
)
}

export default function App() {
const isOpen = useDeepSignal({
value: false,
})
useEffect(() => {
isOpen.value = false
}, [useLocation()])
return (
<div>
<h1>Joplin Batch</h1>
<div>{count.value.toJSON()}</div>
<button onClick={onPostMessage}>postMessage</button>
<div className="min-h-screen flex flex-col">
<header className="border-b flex justify-between items-center">
<div className="flex items-center">
<Sheet open={isOpen.value} onOpenChange={(value) => (isOpen.value = value)}>
<SheetTrigger asChild>
<Button variant="ghost" size="icon" onClick={() => (isOpen.value = !isOpen.value)}>
<Menu className={'h-6 w-6'} />
</Button>
</SheetTrigger>
<SheetContent side={'left'}>
<SheetHeader>
<SheetTitle>
<Link to={'/'} className={'px-4 block'}>
Joplin Batch
</Link>
</SheetTitle>
<SheetDescription></SheetDescription>
</SheetHeader>
<Sidebar />
</SheetContent>
</Sheet>
</div>
</header>
<div className="flex-grow">
<div className="h-full px-4 py-6 lg:px-8">
<RouterView />
</div>
</div>
</div>
)
}
Loading

0 comments on commit 78f237e

Please sign in to comment.