Skip to content

Commit a1dcd2b

Browse files
committed
chore: 🤖 独立 tairl 事件
1 parent d1fe582 commit a1dcd2b

35 files changed

+1457
-379
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"release": "tr release --git"
1313
},
1414
"dependencies": {
15+
"@radix-ui/react-alert-dialog": "^1.1.1",
1516
"@radix-ui/react-checkbox": "^1.0.4",
1617
"@radix-ui/react-dialog": "^1.0.5",
1718
"@radix-ui/react-dropdown-menu": "^2.0.6",
@@ -21,6 +22,7 @@
2122
"@radix-ui/react-select": "^2.0.0",
2223
"@radix-ui/react-slot": "^1.0.2",
2324
"@radix-ui/react-switch": "^1.0.3",
25+
"@radix-ui/react-toast": "^1.2.1",
2426
"@radix-ui/react-toggle": "^1.0.3",
2527
"@radix-ui/react-toggle-group": "^1.0.4",
2628
"@tailwindcss/forms": "^0.5.6",

src/components/CheckUpdate.tsx

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import React, { useState } from 'react'
2+
import { DropdownMenuItem } from '@/components/ui/dropdown-menu'
3+
import { Loader2Icon } from 'lucide-react'
4+
import { check } from '@tauri-apps/plugin-updater'
5+
import { relaunch } from '@tauri-apps/plugin-process'
6+
import { message } from '@tauri-apps/plugin-dialog'
7+
import { useTranslation } from 'react-i18next'
8+
9+
type Props = {}
10+
11+
export default function CheckUpdate({}: Props) {
12+
const { t } = useTranslation()
13+
const [isDownloading, setIsDownloading] = useState(false)
14+
const handleCheckUpdate = async () => {
15+
if (isDownloading) {
16+
return
17+
}
18+
setIsDownloading(true)
19+
try {
20+
const update = await check()
21+
22+
if (update?.available) {
23+
// You could show a dialog asking the user if they want to install the update here.
24+
console.log(`Installing update ${update?.version}`)
25+
26+
// Install the update. This will also restart the app on Windows!
27+
await update.downloadAndInstall()
28+
29+
// On macOS and Linux you will need to restart the app manually.
30+
// You could use this step to display another confirmation dialog.
31+
await relaunch()
32+
} else {
33+
message(t('Already the latest version'), {
34+
title: t('Prompt'),
35+
kind: 'info',
36+
})
37+
}
38+
} catch (error) {
39+
message(error, { title: t('Prompt'), kind: 'info' })
40+
console.error(error)
41+
} finally {
42+
setIsDownloading(false)
43+
}
44+
}
45+
return (
46+
<DropdownMenuItem onClick={handleCheckUpdate}>
47+
{isDownloading ? (
48+
<div className="flex items-center">
49+
<Loader2Icon className="w-4 h-4 mr-1" /> {t('Downloading')}
50+
</div>
51+
) : (
52+
t('Check update')
53+
)}
54+
</DropdownMenuItem>
55+
)
56+
}

src/components/Editor.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import {
1010
registerDocumentFormattingEditProviders,
1111
} from '../monaco/getOrCreateModel'
1212
import { useUpdateEffect } from 'react-use'
13-
import { open } from '@tauri-apps/plugin-shell'
1413
import { listenPaste } from '../monaco/pasteImage'
14+
import { openLink } from '@/lib/bindings'
1515

1616
export default function Editor({
1717
onMount,
@@ -58,9 +58,7 @@ export default function Editor({
5858

5959
disposables.push(editor)
6060

61-
window.open = (url) => {
62-
open(url)
63-
}
61+
window.open = openLink
6462
onMount(editor)
6563

6664
return () => {

src/components/FileTree.tsx

Lines changed: 74 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,23 @@ import {
1818
findPathInTree,
1919
} from './utils/file-tree-util'
2020

21-
import { confirm, message, open } from '@tauri-apps/plugin-dialog'
22-
import { documentDir, resolve } from '@tauri-apps/api/path'
23-
import { getCurrent } from '@tauri-apps/api/window'
21+
import { useToast } from '@/components/ui/use-toast'
22+
import { useTranslation } from 'react-i18next'
2423
import {
24+
fullScreen,
25+
openLink,
26+
searchKeywordInDir,
27+
readDir,
28+
showInFlower,
29+
chooseDir,
30+
resolve,
2531
exists,
2632
mkdir,
2733
rename,
2834
writeTextFile,
2935
readTextFile,
3036
remove,
31-
} from '@tauri-apps/plugin-fs'
32-
import { useTranslation } from 'react-i18next'
33-
import { invoke } from '@tauri-apps/api/core'
34-
import { open as openLink } from '@tauri-apps/plugin-shell'
37+
} from '@/lib/bindings'
3538
import Tree from './Tree'
3639
import SearchList from './SearchList'
3740
import TocList from './TocList'
@@ -48,6 +51,7 @@ import dayjs from 'dayjs'
4851
import { useLocalStorage } from 'react-use'
4952
import { Context } from './Layout'
5053
import { useHotkeys } from 'react-hotkeys-hook'
54+
import { useConfirm } from './ui/confirm'
5155

5256
interface FileNode {
5357
name: string
@@ -91,6 +95,8 @@ const FileTree = forwardRef<TreeRef, Props>(
9195
{ onSelect, selectedPath, setShowPPT, onScroll, dirPath, setDirPath },
9296
ref
9397
) => {
98+
const { toast } = useToast()
99+
const { confirm } = useConfirm()
94100
const { isMacOS } = useContext(Context)
95101
const { t } = useTranslation()
96102
const [scrollLine, setScrollLine] = useState(1)
@@ -108,12 +114,9 @@ const FileTree = forwardRef<TreeRef, Props>(
108114
const [fileTreeData, setTreeData] = useState<FileNode[]>([])
109115

110116
const reloadTree = async () => {
111-
const res = await exists(dirPath)
112-
if (res) {
113-
const result: FileNode = await invoke('read_dir', { path: dirPath })
114-
if (result.children) {
115-
setTreeData(result.children)
116-
}
117+
const result = await readDir<FileNode>(dirPath)
118+
if (result.children) {
119+
setTreeData(result.children)
117120
}
118121
}
119122
useEffect(() => {
@@ -174,8 +177,9 @@ title: ${file}
174177
const content = await readTextFile(filePath)
175178
openMd(fullPath, content.replace(/{{date}}/g, fileName))
176179
} catch (error) {
177-
message(t('The template file does not exist'), {
178-
title: t('Prompt'),
180+
toast({
181+
title: t('Error'),
182+
description: t('The template file does not exist'),
179183
})
180184
}
181185
} else {
@@ -209,15 +213,17 @@ title: ${file}
209213
reloadTree()
210214
onSelect(dirPath + '/' + fileName)
211215
} else {
212-
message(t('Please set the working directory'), {
216+
toast({
213217
title: t('Prompt'),
218+
description: t('Please set the working directory'),
214219
})
215220
}
216221
})
217222

218223
const fileExists = async () => {
219-
await message(t('File already exists'), {
224+
toast({
220225
title: t('Prompt'),
226+
description: t('File already exists'),
221227
})
222228
setAction({
223229
path: '',
@@ -247,10 +253,10 @@ title: ${file}
247253
if (e.keyCode !== 13) {
248254
return
249255
}
250-
const result: SearchResultItem[] = await invoke('search_keyword_in_dir', {
251-
keyword: value.trim(),
252-
path: dirPath,
253-
})
256+
const result = await searchKeywordInDir<SearchResultItem[]>(
257+
value.trim(),
258+
dirPath
259+
)
254260
console.log('search', result)
255261
setSearchList(result)
256262
setSearchValue(value)
@@ -490,7 +496,7 @@ title: ${file}
490496
const handleOpenFinder = async () => {
491497
setMenuStyle({ display: 'none' })
492498
const path = selectedKeys[0]
493-
await invoke('show_in_folder', { path })
499+
showInFlower(path)
494500
}
495501

496502
const handleRefresh = async () => {
@@ -504,30 +510,27 @@ title: ${file}
504510
return
505511
}
506512
const name = getCurrentFolderName(path)
507-
const confirmed = await confirm(
508-
`${t('Are you sure you want to delete')}'${name}'?`,
509-
{
510-
title: t('Delete confirmation'),
511-
kind: 'warning',
512-
}
513-
)
514-
if (confirmed) {
515-
if (supportFile(path)) {
516-
await remove(path)
517-
} else {
518-
remove(path, {
519-
recursive: true,
520-
})
521-
}
522-
reloadTree()
523-
}
513+
514+
confirm({
515+
title: t('Delete confirmation'),
516+
description: `${t('Are you sure you want to delete')} ${name}?`,
517+
onOk: async () => {
518+
if (supportFile(path)) {
519+
await remove(path)
520+
} else {
521+
remove(path, {
522+
recursive: true,
523+
})
524+
}
525+
reloadTree()
526+
},
527+
})
524528
}, [selectedKeys, dirPath])
525529

526530
const handlePPT = async () => {
527531
setMenuStyle({ display: 'none' })
528532
setShowPPT()
529-
const appWindow = getCurrent()
530-
await appWindow.setFullscreen(true)
533+
fullScreen()
531534
}
532535

533536
let menu: MenuItemProps[] = [
@@ -552,10 +555,7 @@ title: ${file}
552555
menu = dirItemMenu
553556
}
554557
const handleChooseDir = async () => {
555-
const selected = await open({
556-
directory: true,
557-
defaultPath: await documentDir(),
558-
})
558+
const selected = await chooseDir()
559559
if (selected) {
560560
setDirPath(selected)
561561
setExpandedKeys([])
@@ -567,38 +567,40 @@ title: ${file}
567567
<div
568568
data-tauri-drag-region
569569
className={clsx(
570-
'px-4 pb-3 bg-white dark:bg-gray-900 sticky top-0 left-0 z-10 border-b border-gray-200 dark:border-gray-800 flex items-center flex-none',
571-
isMacOS ? 'pt-7' : 'pt-4'
570+
'px-4 pb-3 bg-white dark:bg-gray-900 sticky top-0 left-0 z-10 border-b flex-none dark:shadow-highlight/4',
571+
isMacOS ? 'pt-6' : 'pt-4'
572572
)}
573573
>
574-
<div className="flex items-center w-full text-left px-2 h-8 mt-[2px] bg-white ring-1 ring-slate-900/10 hover:ring-slate-300 focus:outline-none focus:ring-2 focus:ring-sky-500 shadow-sm rounded-lg text-slate-400 dark:bg-slate-800 dark:ring-0 dark:text-slate-300 dark:highlight-white/5 dark:hover:bg-slate-700">
575-
<SearchIcon className="w-4 h-4 flex-none text-slate-300 dark:text-slate-400" />
576-
<input
577-
className="flex-auto bg-transparent w-full px-2 focus:ring-0 outline-none h-full border-0 text-slate-900 dark:text-slate-200"
578-
aria-label="search"
579-
ref={searchInputRef}
580-
onKeyDown={onChange}
581-
/>
582-
{searchValue && (
583-
<XCircleIcon
584-
onClick={() => {
585-
setSearchValue('')
586-
if (searchInputRef.current) {
587-
searchInputRef.current.value = ''
588-
}
589-
}}
590-
className="w-4 h-4 cursor-pointer flex-none text-slate-300 dark:text-slate-400"
574+
<div className="h-9 flex items-center">
575+
<div className="flex items-center w-full text-left px-2 h-8 mt-[2px] bg-white ring-1 ring-slate-900/10 hover:ring-slate-300 focus:outline-none focus:ring-2 focus:ring-sky-500 shadow-sm rounded-lg text-slate-400 dark:bg-slate-800 dark:ring-0 dark:text-slate-300 dark:highlight-white/5 dark:hover:bg-slate-700">
576+
<SearchIcon className="w-4 h-4 flex-none text-slate-300 dark:text-slate-400" />
577+
<input
578+
className="flex-auto bg-transparent w-full px-2 focus:ring-0 outline-none h-full border-0 text-slate-900 dark:text-slate-200"
579+
aria-label="search"
580+
ref={searchInputRef}
581+
onKeyDown={onChange}
591582
/>
583+
{searchValue && (
584+
<XCircleIcon
585+
onClick={() => {
586+
setSearchValue('')
587+
if (searchInputRef.current) {
588+
searchInputRef.current.value = ''
589+
}
590+
}}
591+
className="w-4 h-4 cursor-pointer flex-none text-slate-300 dark:text-slate-400"
592+
/>
593+
)}
594+
</div>
595+
{!searchValue && (
596+
<span
597+
onClick={() => setShowToc(!showToc)}
598+
className="pl-2 cursor-pointer text-slate-700 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-300"
599+
>
600+
{showToc ? <ListIcon /> : <ListTreeIcon />}
601+
</span>
592602
)}
593603
</div>
594-
{!searchValue && (
595-
<span
596-
onClick={() => setShowToc(!showToc)}
597-
className="pl-2 cursor-pointer text-slate-700 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-300"
598-
>
599-
{showToc ? <ListIcon /> : <ListTreeIcon />}
600-
</span>
601-
)}
602604
</div>
603605
<SearchList
604606
value={searchList}

src/components/Header.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ export default function Header({ children, logo }) {
1212
<div
1313
data-tauri-drag-region
1414
className={clsx(
15-
'relative z-20 flex-none pb-3 pl-5 pr-3 sm:pl-6 sm:pr-4 md:pr-3.5 lg:px-6 flex items-center justify-between antialiased select-none',
16-
isMacOS ? 'pt-6' : 'pt-3'
15+
'sticky top-0 z-20 flex-none pb-3 px-5 flex items-center justify-between antialiased select-none border-b dark:shadow-lg',
16+
isMacOS ? 'pt-6' : 'pt-4'
1717
)}
1818
style={{ fontFeatureSettings: '"cv02", "cv03", "cv04", "cv11"' }}
1919
>
@@ -22,9 +22,7 @@ export default function Header({ children, logo }) {
2222
</div>
2323
<div className="flex items-center space-x-2">
2424
{children}
25-
2625
<div className="block mx-2 w-px h-6 bg-gray-200 dark:bg-gray-700" />
27-
2826
<Button size="icon" onClick={toggleTheme} variant="outline">
2927
<Sun className="w-5 h-5 stroke-primary fill-sky-100 dark:fill-sky-400/50 hidden dark:block" />
3028
<MoonStar className="w-5 h-5 stroke-primary fill-sky-100 dark:fill-sky-400/50 dark:hidden" />

src/components/ImagePreview.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
/* eslint-disable @next/next/no-img-element */
12
import React from 'react'
2-
import { convertFileSrc } from '@tauri-apps/api/core'
3+
import { convertImageFileSrc } from '@/lib/bindings'
34

45
type Props = {
56
path: string
@@ -9,7 +10,11 @@ export default function ImagePreview({ path }: Props) {
910
return (
1011
<div className="h-full flex overflow-auto">
1112
<div className="p-8 flex justify-center items-center m-auto">
12-
<img style={{ maxWidth: '100%' }} src={convertFileSrc('') + path} />
13+
<img
14+
alt=""
15+
style={{ maxWidth: '100%' }}
16+
src={convertImageFileSrc(path)}
17+
/>
1318
</div>
1419
</div>
1520
)

0 commit comments

Comments
 (0)