feat: publish HoloLake model-native living system source

This commit is contained in:
冰朔 2026-08-03 10:04:41 +08:00
commit c395dd3a99
2467 changed files with 615073 additions and 0 deletions

View file

@ -0,0 +1,204 @@
import { invoke } from '@tauri-apps/api/core'
import { isTauri, mockInvoke } from '../../mock-tauri'
import type { SidebarSelection, VaultEntry } from '../../types'
export interface FolderRenameResult {
old_path: string
new_path: string
}
export interface FolderTab {
entry: VaultEntry
content: string
}
export interface ConfirmFolderDeleteState {
path: string
title: string
message: string
confirmLabel: string
}
export const DEFAULT_SELECTION: SidebarSelection = { kind: 'filter', filter: 'all' }
function trimTrailingSlash(path: string): string {
return path.endsWith('/') ? path.slice(0, -1) : path
}
export function folderAbsolutePath(params: { vaultPath: string; folderPath: string }): string {
const normalizedVaultPath = trimTrailingSlash(params.vaultPath)
const normalizedFolderPath = params.folderPath.replace(/^\/+/, '')
return normalizedFolderPath ? `${normalizedVaultPath}/${normalizedFolderPath}` : normalizedVaultPath
}
export function isWithinPrefix(params: { path: string; prefix: string }): boolean {
return params.path === params.prefix || params.path.startsWith(`${params.prefix}/`)
}
export function replaceFolderPrefix(params: {
path: string
oldPrefix: string
newPrefix: string
}): string {
if (!isWithinPrefix({ path: params.path, prefix: params.oldPrefix })) return params.path
return `${params.newPrefix}${params.path.slice(params.oldPrefix.length)}`
}
export function replaceRelativeFolderPrefix(params: {
path: string
oldPrefix: string
newPrefix: string
}): string {
if (!isWithinPrefix({ path: params.path, prefix: params.oldPrefix })) return params.path
return `${params.newPrefix}${params.path.slice(params.oldPrefix.length)}`
}
export function folderLabel(params: { folderPath: string }): string {
return params.folderPath.split('/').filter(Boolean).at(-1) ?? params.folderPath
}
export async function invokeRenameFolder(params: {
vaultPath: string
folderPath: string
newName: string
}): Promise<FolderRenameResult> {
if (isTauri()) {
return invoke<FolderRenameResult>('rename_vault_folder', params)
}
return mockInvoke<FolderRenameResult>('rename_vault_folder', params)
}
export async function invokeDeleteFolder(params: { vaultPath: string; folderPath: string }): Promise<string> {
if (isTauri()) {
return invoke<string>('delete_vault_folder', params)
}
return mockInvoke<string>('delete_vault_folder', params)
}
export function updateSelectionAfterFolderRename(params: {
refreshedEntries: VaultEntry[]
renameResult: FolderRenameResult
selection: SidebarSelection
setSelection: (selection: SidebarSelection) => void
vaultPath: string
}) {
const {
refreshedEntries,
renameResult,
selection,
setSelection,
vaultPath,
} = params
if (selection.kind === 'folder') {
if (!isWithinPrefix({ path: selection.path, prefix: renameResult.old_path })) return
setSelection({
kind: 'folder',
path: replaceRelativeFolderPrefix({
path: selection.path,
oldPrefix: renameResult.old_path,
newPrefix: renameResult.new_path,
}),
})
return
}
if (selection.kind !== 'entity') return
const oldPrefix = folderAbsolutePath({ vaultPath, folderPath: renameResult.old_path })
if (!isWithinPrefix({ path: selection.entry.path, prefix: oldPrefix })) return
const renamedPath = replaceFolderPrefix({
path: selection.entry.path,
oldPrefix,
newPrefix: folderAbsolutePath({ vaultPath, folderPath: renameResult.new_path }),
})
const refreshedEntry = refreshedEntries.find((entry) => entry.path === renamedPath)
setSelection(refreshedEntry ? { kind: 'entity', entry: refreshedEntry } : DEFAULT_SELECTION)
}
export function updateTabsAfterFolderRename(params: {
activeTabPathRef: React.MutableRefObject<string | null>
handleSwitchTab: (path: string) => void
refreshedEntries: VaultEntry[]
renameResult: FolderRenameResult
setTabs: React.Dispatch<React.SetStateAction<FolderTab[]>>
vaultPath: string
}) {
const {
activeTabPathRef,
handleSwitchTab,
refreshedEntries,
renameResult,
setTabs,
vaultPath,
} = params
const oldPrefix = folderAbsolutePath({ vaultPath, folderPath: renameResult.old_path })
const newPrefix = folderAbsolutePath({ vaultPath, folderPath: renameResult.new_path })
setTabs((currentTabs) => currentTabs.map((tab) => {
if (!isWithinPrefix({ path: tab.entry.path, prefix: oldPrefix })) return tab
const nextPath = replaceFolderPrefix({ path: tab.entry.path, oldPrefix, newPrefix })
const refreshedEntry = refreshedEntries.find((entry) => entry.path === nextPath)
return refreshedEntry ? { ...tab, entry: refreshedEntry } : { ...tab, entry: { ...tab.entry, path: nextPath } }
}))
const activeTabPath = activeTabPathRef.current
if (!activeTabPath || !isWithinPrefix({ path: activeTabPath, prefix: oldPrefix })) return
handleSwitchTab(replaceFolderPrefix({ path: activeTabPath, oldPrefix, newPrefix }))
}
export function clearDeletedFolderTabs(params: {
activeTabPathRef: React.MutableRefObject<string | null>
closeAllTabs: () => void
folderPath: string
setTabs: React.Dispatch<React.SetStateAction<FolderTab[]>>
vaultPath: string
}) {
const {
activeTabPathRef,
closeAllTabs,
folderPath,
setTabs,
vaultPath,
} = params
const folderPrefix = folderAbsolutePath({ vaultPath, folderPath })
const hasActiveTabInFolder = !!activeTabPathRef.current
&& isWithinPrefix({ path: activeTabPathRef.current, prefix: folderPrefix })
if (hasActiveTabInFolder) {
closeAllTabs()
return
}
setTabs((currentTabs) => currentTabs.filter((tab) => !isWithinPrefix({ path: tab.entry.path, prefix: folderPrefix })))
}
export function resetSelectionIfFolderDeleted(params: {
folderPath: string
refreshedEntries: VaultEntry[]
selection: SidebarSelection
setSelection: (selection: SidebarSelection) => void
vaultPath: string
}) {
const {
folderPath,
refreshedEntries,
selection,
setSelection,
vaultPath,
} = params
if (selection.kind === 'folder' && isWithinPrefix({ path: selection.path, prefix: folderPath })) {
setSelection(DEFAULT_SELECTION)
return
}
if (selection.kind !== 'entity') return
const folderPrefix = folderAbsolutePath({ vaultPath, folderPath })
if (!isWithinPrefix({ path: selection.entry.path, prefix: folderPrefix })) return
const replacement = refreshedEntries.find((entry) => entry.path === selection.entry.path)
setSelection(replacement ? { kind: 'entity', entry: replacement } : DEFAULT_SELECTION)
}

View file

@ -0,0 +1,92 @@
import { useCallback, useState } from 'react'
import type { SidebarSelection, VaultEntry } from '../../types'
import {
clearDeletedFolderTabs,
type ConfirmFolderDeleteState,
type FolderTab,
folderLabel,
invokeDeleteFolder,
resetSelectionIfFolderDeleted,
} from './folderActionUtils'
interface UseFolderDeleteInput {
activeTabPathRef: React.MutableRefObject<string | null>
clearFolderRename: () => void
closeAllTabs: () => void
reloadFolders: () => Promise<unknown>
reloadVault: () => Promise<VaultEntry[]>
selection: SidebarSelection
setSelection: (selection: SidebarSelection) => void
setTabs: React.Dispatch<React.SetStateAction<FolderTab[]>>
setToastMessage: (message: string | null) => void
vaultPath: string
}
export function useFolderDelete({
activeTabPathRef,
clearFolderRename,
closeAllTabs,
reloadFolders,
reloadVault,
selection,
setSelection,
setTabs,
setToastMessage,
vaultPath,
}: UseFolderDeleteInput) {
const [confirmDeleteFolder, setConfirmDeleteFolder] = useState<ConfirmFolderDeleteState | null>(null)
const requestDeleteFolder = useCallback((folderPath: string) => {
clearFolderRename()
setConfirmDeleteFolder({
path: folderPath,
title: `Delete "${folderLabel({ folderPath })}" and everything inside it?`,
message: 'This permanently removes the folder, all nested folders, and every note or file inside it. This cannot be undone.',
confirmLabel: 'Delete folder',
})
}, [clearFolderRename])
const cancelDeleteFolder = useCallback(() => setConfirmDeleteFolder(null), [])
const confirmDeleteSelectedFolder = useCallback(async () => {
if (!confirmDeleteFolder) return
const folderPath = confirmDeleteFolder.path
try {
setConfirmDeleteFolder(null)
await invokeDeleteFolder({ vaultPath, folderPath })
clearDeletedFolderTabs({
activeTabPathRef,
closeAllTabs,
folderPath,
setTabs,
vaultPath,
})
await reloadFolders()
const refreshedEntries = await reloadVault()
resetSelectionIfFolderDeleted({
folderPath,
refreshedEntries,
selection,
setSelection,
vaultPath,
})
setToastMessage(`Deleted folder "${folderLabel({ folderPath })}"`)
} catch (error) {
setToastMessage(`Failed to delete folder: ${error}`)
}
}, [activeTabPathRef, closeAllTabs, confirmDeleteFolder, reloadFolders, reloadVault, selection, setSelection, setTabs, setToastMessage, vaultPath])
const deleteSelectedFolder = useCallback(() => {
if (selection.kind !== 'folder' || !selection.path) return
requestDeleteFolder(selection.path)
}, [requestDeleteFolder, selection])
return {
cancelDeleteFolder,
confirmDeleteFolder,
confirmDeleteSelectedFolder,
deleteSelectedFolder,
requestDeleteFolder,
}
}

View file

@ -0,0 +1,86 @@
import { useCallback, useState } from 'react'
import type { SidebarSelection, VaultEntry } from '../../types'
import {
folderLabel,
invokeRenameFolder,
type FolderTab,
updateSelectionAfterFolderRename,
updateTabsAfterFolderRename,
} from './folderActionUtils'
interface UseFolderRenameInput {
activeTabPathRef: React.MutableRefObject<string | null>
handleSwitchTab: (path: string) => void
reloadFolders: () => Promise<unknown>
reloadVault: () => Promise<VaultEntry[]>
selection: SidebarSelection
setSelection: (selection: SidebarSelection) => void
setTabs: React.Dispatch<React.SetStateAction<FolderTab[]>>
setToastMessage: (message: string | null) => void
vaultPath: string
}
export function useFolderRename({
activeTabPathRef,
handleSwitchTab,
reloadFolders,
reloadVault,
selection,
setSelection,
setTabs,
setToastMessage,
vaultPath,
}: UseFolderRenameInput) {
const [renamingFolderPath, setRenamingFolderPath] = useState<string | null>(null)
const cancelFolderRename = useCallback(() => setRenamingFolderPath(null), [])
const startFolderRename = useCallback((folderPath: string) => setRenamingFolderPath(folderPath), [])
const renameFolder = useCallback(async (folderPath: string, nextName: string) => {
const trimmedName = nextName.trim()
if (trimmedName === folderLabel({ folderPath })) {
setRenamingFolderPath(null)
return true
}
try {
const renameResult = await invokeRenameFolder({ vaultPath, folderPath, newName: trimmedName })
setRenamingFolderPath(null)
await reloadFolders()
const refreshedEntries = await reloadVault()
updateTabsAfterFolderRename({
activeTabPathRef,
handleSwitchTab,
refreshedEntries,
renameResult,
setTabs,
vaultPath,
})
updateSelectionAfterFolderRename({
refreshedEntries,
renameResult,
selection,
setSelection,
vaultPath,
})
setToastMessage(`Renamed folder to "${trimmedName}"`)
return true
} catch (error) {
setToastMessage(`Failed to rename folder: ${error}`)
return false
}
}, [activeTabPathRef, handleSwitchTab, reloadFolders, reloadVault, selection, setSelection, setTabs, setToastMessage, vaultPath])
const renameSelectedFolder = useCallback(() => {
if (selection.kind !== 'folder' || !selection.path) return
startFolderRename(selection.path)
}, [selection, startFolderRename])
return {
cancelFolderRename,
renameFolder,
renameSelectedFolder,
renamingFolderPath,
startFolderRename,
}
}