feat: support mounted vault workspaces
This commit is contained in:
@@ -1,15 +1,40 @@
|
||||
import type { VaultEntry, WorkspaceIdentity } from '../types'
|
||||
import type { VaultOption } from '../components/status-bar/types'
|
||||
import { ACCENT_COLOR_PICKER_KEYS } from './typeColors'
|
||||
|
||||
export const WORKSPACE_COLORS = ['blue', 'green', 'purple', 'orange', 'red', 'yellow'] as const
|
||||
export const WORKSPACE_COLORS = ACCENT_COLOR_PICKER_KEYS
|
||||
export type WorkspaceColor = typeof WORKSPACE_COLORS[number]
|
||||
|
||||
interface WorkspaceIdentityOptions {
|
||||
defaultWorkspacePath?: string | null
|
||||
}
|
||||
|
||||
function slugifyWorkspaceAlias(value: string): string {
|
||||
const normalized = value
|
||||
interface WorkspaceLabelInput {
|
||||
label: string
|
||||
}
|
||||
|
||||
interface WorkspaceGraphOptions<T> {
|
||||
defaultVaultPath: string
|
||||
enabled: boolean
|
||||
vaults: T[]
|
||||
}
|
||||
|
||||
interface WorkspaceSetOptions<T> {
|
||||
defaultVaultPath: string
|
||||
vaults: T[]
|
||||
}
|
||||
|
||||
interface WritableWorkspaceOptions<T> {
|
||||
defaultVaultPath: string
|
||||
graphVaults: T[] | undefined
|
||||
}
|
||||
|
||||
function stringValue(value: unknown): string {
|
||||
return typeof value === 'string' ? value : ''
|
||||
}
|
||||
|
||||
function slugifyWorkspaceAlias({ label }: WorkspaceLabelInput): string {
|
||||
const normalized = label
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, '-')
|
||||
@@ -17,38 +42,51 @@ function slugifyWorkspaceAlias(value: string): string {
|
||||
return normalized || 'workspace'
|
||||
}
|
||||
|
||||
export function labelFromWorkspacePath(path: string): string {
|
||||
return path.split(/[\\/]/).filter(Boolean).pop() || 'Workspace'
|
||||
export function labelFromWorkspacePath(path: string | null | undefined): string {
|
||||
return stringValue(path).split(/[\\/]/).filter(Boolean).pop() || 'Workspace'
|
||||
}
|
||||
|
||||
function shortLabelFromLabel(label: string): string {
|
||||
function shortLabelFromLabel({ label }: WorkspaceLabelInput): string {
|
||||
const words = label.trim().split(/\s+/).filter(Boolean)
|
||||
if (words.length === 0) return 'W'
|
||||
if (words.length === 1) return words[0].slice(0, 2).toUpperCase()
|
||||
return words.slice(0, 2).map((word) => word[0]?.toUpperCase() ?? '').join('')
|
||||
}
|
||||
|
||||
function workspaceShortLabelFromVault(vault: Pick<VaultOption, 'shortLabel'>, label: string): string {
|
||||
const customShortLabel = stringValue(vault.shortLabel).trim().toUpperCase().slice(0, 3)
|
||||
return customShortLabel || shortLabelFromLabel({ label })
|
||||
}
|
||||
|
||||
export function workspaceAliasFromOption(vault: Pick<VaultOption, 'alias' | 'label' | 'path'>): string {
|
||||
return slugifyWorkspaceAlias(vault.alias || vault.label || labelFromWorkspacePath(vault.path))
|
||||
const alias = stringValue(vault.alias).trim()
|
||||
return slugifyWorkspaceAlias({
|
||||
label: alias || labelFromWorkspacePath(vault.path),
|
||||
})
|
||||
}
|
||||
|
||||
export function workspaceLabelFromVault(vault: Pick<VaultOption, 'label' | 'path'>): string {
|
||||
return stringValue(vault.label).trim() || labelFromWorkspacePath(vault.path)
|
||||
}
|
||||
|
||||
export function workspaceIdentityFromVault(
|
||||
vault: VaultOption,
|
||||
options: WorkspaceIdentityOptions = {},
|
||||
): WorkspaceIdentity {
|
||||
const label = vault.label.trim() || labelFromWorkspacePath(vault.path)
|
||||
const path = stringValue(vault.path)
|
||||
const label = workspaceLabelFromVault(vault)
|
||||
const alias = workspaceAliasFromOption({ ...vault, label })
|
||||
return {
|
||||
id: alias,
|
||||
label,
|
||||
alias,
|
||||
path: vault.path,
|
||||
shortLabel: shortLabelFromLabel(label),
|
||||
path,
|
||||
shortLabel: workspaceShortLabelFromVault(vault, label),
|
||||
color: vault.color ?? null,
|
||||
icon: vault.icon ?? null,
|
||||
mounted: vault.mounted !== false,
|
||||
available: vault.available !== false,
|
||||
defaultForNewNotes: options.defaultWorkspacePath === vault.path,
|
||||
defaultForNewNotes: options.defaultWorkspacePath === path,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +98,10 @@ export function workspacePathForEntry(entry: Pick<VaultEntry, 'workspace'>): str
|
||||
return entry.workspace?.path
|
||||
}
|
||||
|
||||
export function vaultPathForEntry(entry: Pick<VaultEntry, 'workspace'>, fallbackVaultPath: string): string {
|
||||
return workspacePathForEntry(entry) ?? fallbackVaultPath
|
||||
}
|
||||
|
||||
export function workspaceLabelForEntry(entry: Pick<VaultEntry, 'workspace'>): string | null {
|
||||
return entry.workspace?.label ?? null
|
||||
}
|
||||
@@ -75,12 +117,88 @@ export function mountedWorkspacePaths(vaults: VaultOption[]): string[] {
|
||||
.map((vault) => vault.path)
|
||||
}
|
||||
|
||||
export function workspacesMountedInGraph<T extends { path: string; available?: boolean; mounted?: boolean; managedDefault?: boolean }>(
|
||||
vaults: T[],
|
||||
activeVaultPath: string,
|
||||
): T[] {
|
||||
function uniqueWorkspacePaths(paths: string[]): string[] {
|
||||
return [...new Set(paths.filter((path) => path.trim().length > 0))]
|
||||
}
|
||||
|
||||
export function workspacesMountedInGraph<T extends { path: string; available?: boolean; mounted?: boolean; managedDefault?: boolean }>({
|
||||
defaultVaultPath,
|
||||
vaults,
|
||||
}: WorkspaceSetOptions<T>): T[] {
|
||||
return vaults.filter((vault) => {
|
||||
if (vault.path === activeVaultPath) return true
|
||||
return vault.available !== false && vault.mounted !== false && vault.managedDefault !== true
|
||||
if (vault.path === defaultVaultPath) return true
|
||||
return vault.available !== false && vault.mounted !== false
|
||||
})
|
||||
}
|
||||
|
||||
export function graphWorkspaceVaults<T extends { path: string; available?: boolean; mounted?: boolean; managedDefault?: boolean }>({
|
||||
defaultVaultPath,
|
||||
enabled,
|
||||
vaults,
|
||||
}: WorkspaceGraphOptions<T>): T[] | undefined {
|
||||
if (!enabled) return undefined
|
||||
return workspacesMountedInGraph({ defaultVaultPath, vaults })
|
||||
}
|
||||
|
||||
function shouldLoadGraphWorkspace(vault: { path: string; available?: boolean; managedDefault?: boolean }): boolean {
|
||||
if (!vault.path.trim()) return false
|
||||
if (vault.available === false) return false
|
||||
return true
|
||||
}
|
||||
|
||||
function mountedGraphWorkspace<T extends { path: string }>(vault: T): T & { mounted: true } {
|
||||
return { ...vault, mounted: true }
|
||||
}
|
||||
|
||||
export function graphWorkspaceVaultsForLoading<T extends { path: string; available?: boolean; mounted?: boolean; managedDefault?: boolean }>({
|
||||
defaultVaultPath,
|
||||
enabled,
|
||||
vaults,
|
||||
}: WorkspaceGraphOptions<T>): Array<T & { mounted: true }> | undefined {
|
||||
if (!enabled) return undefined
|
||||
const byPath = new Map<string, T & { mounted: true }>()
|
||||
for (const vault of vaults) {
|
||||
if (shouldLoadGraphWorkspace(vault)) {
|
||||
byPath.set(vault.path, mountedGraphWorkspace(vault))
|
||||
}
|
||||
}
|
||||
if (defaultVaultPath.trim() && !byPath.has(defaultVaultPath)) {
|
||||
byPath.set(defaultVaultPath, { path: defaultVaultPath, mounted: true } as T & { mounted: true })
|
||||
}
|
||||
return [...byPath.values()]
|
||||
}
|
||||
|
||||
export function visibleWorkspacePaths({
|
||||
defaultVaultPath,
|
||||
enabled,
|
||||
vaults,
|
||||
}: WorkspaceGraphOptions<VaultOption>): string[] | undefined {
|
||||
if (!enabled) return undefined
|
||||
return uniqueWorkspacePaths([defaultVaultPath, ...mountedWorkspacePaths(vaults)])
|
||||
}
|
||||
|
||||
export function filterEntriesToVisibleWorkspaces(
|
||||
entries: VaultEntry[],
|
||||
visiblePaths: readonly string[] | undefined,
|
||||
): VaultEntry[] {
|
||||
if (!visiblePaths) return entries
|
||||
const visiblePathSet = new Set(visiblePaths)
|
||||
return entries.filter((entry) => {
|
||||
const workspacePath = workspacePathForEntry(entry)
|
||||
return !workspacePath || visiblePathSet.has(workspacePath)
|
||||
})
|
||||
}
|
||||
|
||||
function isWritableWorkspace(workspace: { available?: boolean; mounted?: boolean }): boolean {
|
||||
return workspace.available !== false && workspace.mounted !== false
|
||||
}
|
||||
|
||||
export function writableWorkspacePaths<T extends { path: string; available?: boolean; mounted?: boolean }>({
|
||||
defaultVaultPath,
|
||||
graphVaults,
|
||||
}: WritableWorkspaceOptions<T>): string[] {
|
||||
const workspaces: Array<{ path: string; available?: boolean; mounted?: boolean }> = graphVaults ?? [{ path: defaultVaultPath }]
|
||||
return workspaces
|
||||
.filter(isWritableWorkspace)
|
||||
.map((workspace) => workspace.path)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user