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,100 @@
import { describe, expect, it } from 'vitest'
import type { ViewDefinition, ViewFile } from '../types'
import { makeEntry } from '../test-utils/noteListTestUtils'
import { collectionFromSelection } from './collectionFromSelection'
function emptyFilters(): ViewDefinition['filters'] {
return { all: [] }
}
function makeView(definition: Partial<ViewDefinition> = {}): ViewFile {
return {
filename: 'active-projects.yml',
definition: {
name: 'Active Projects',
icon: null,
color: null,
sort: 'modified:desc',
listPropertiesDisplay: ['status'],
filters: emptyFilters(),
...definition,
},
}
}
describe('collectionFromSelection', () => {
it('maps built-in filters to list-presented collections', () => {
const collection = collectionFromSelection({ kind: 'filter', filter: 'all' })
expect(collection).toMatchObject({
id: 'builtin:all',
label: 'All Notes',
origin: 'builtin',
presentation: { type: 'list', sort: null, properties: [] },
})
})
it('maps type and folder selections without requiring saved YAML', () => {
expect(collectionFromSelection({ kind: 'sectionGroup', type: 'Project' })).toMatchObject({
id: 'type:Project',
label: 'Project',
origin: 'type',
})
expect(collectionFromSelection({ kind: 'folder', path: 'clients', rootPath: '/vault' })).toMatchObject({
id: 'folder:/vault:clients',
label: 'clients',
origin: 'folder',
})
})
it('maps neighborhood selections to a collection around the source note', () => {
const entry = makeEntry({ path: '/vault/alpha.md', title: 'Alpha' })
const collection = collectionFromSelection({ kind: 'entity', entry })
expect(collection).toMatchObject({
id: 'neighborhood:/vault/alpha.md',
label: 'Alpha',
origin: 'neighborhood',
entry,
})
})
it('normalizes legacy saved-view list settings into presentation config', () => {
const view = makeView()
const collection = collectionFromSelection(
{ kind: 'view', filename: view.filename },
{ views: [view] },
)
expect(collection).toMatchObject({
id: 'saved-view::active-projects.yml',
label: 'Active Projects',
origin: 'saved-view',
filter: view.definition.filters,
presentation: { type: 'list', sort: 'modified:desc', properties: ['status'] },
view,
})
})
it('lets nested list presentation config override legacy saved-view fields in memory', () => {
const view = makeView({
presentation: {
type: 'list',
sort: 'title:asc',
properties: ['Owner'],
},
} as Partial<ViewDefinition>)
const collection = collectionFromSelection(
{ kind: 'view', filename: view.filename },
{ views: [view] },
)
expect(collection.presentation).toEqual({
type: 'list',
sort: 'title:asc',
properties: ['Owner'],
})
})
})

View file

@ -0,0 +1,88 @@
import type { SidebarFilter, SidebarSelection, ViewFile } from '../types'
import { viewMatchesSelection } from '../utils/viewIdentity'
import { defaultListPresentation, presentationFromViewDefinition } from './presentationConfig'
import type { CollectionDefinition } from './collectionTypes'
interface CollectionContext {
views?: ViewFile[]
}
const BUILTIN_LABELS: Record<SidebarFilter, string> = {
all: 'All Notes',
archived: 'Archived',
changes: 'Changes',
pulse: 'Pulse',
inbox: 'Inbox',
favorites: 'Favorites',
}
function folderLabel(selection: Extract<SidebarSelection, { kind: 'folder' }>): string {
return selection.path || 'Vault'
}
function identityPart(value: string | undefined): string {
return value ?? ''
}
function selectedView(selection: SidebarSelection, views?: ViewFile[]): ViewFile | undefined {
return selection.kind === 'view'
? views?.find((view) => viewMatchesSelection(view, selection))
: undefined
}
export function collectionFromSelection(
selection: SidebarSelection,
context: CollectionContext = {},
): CollectionDefinition {
if (selection.kind === 'filter') {
return {
id: `builtin:${selection.filter}`,
label: BUILTIN_LABELS[selection.filter],
origin: 'builtin',
selection,
presentation: defaultListPresentation(),
}
}
if (selection.kind === 'sectionGroup') {
return {
id: `type:${selection.type}`,
label: selection.type,
origin: 'type',
selection,
presentation: defaultListPresentation(),
}
}
if (selection.kind === 'folder') {
return {
id: `folder:${identityPart(selection.rootPath)}:${selection.path}`,
label: folderLabel(selection),
origin: 'folder',
selection,
presentation: defaultListPresentation(),
}
}
if (selection.kind === 'entity') {
return {
id: `neighborhood:${selection.entry.path}`,
label: selection.entry.title,
origin: 'neighborhood',
selection,
presentation: defaultListPresentation(),
entry: selection.entry,
}
}
const view = selectedView(selection, context.views)
return {
id: `saved-view:${identityPart(selection.rootPath)}:${selection.filename}`,
label: view?.definition.name ?? selection.filename,
origin: 'saved-view',
selection,
presentation: view ? presentationFromViewDefinition(view.definition) : defaultListPresentation(),
filter: view?.definition.filters,
view,
}
}

View file

@ -0,0 +1,26 @@
import type { FilterGroup, SidebarSelection, VaultEntry, ViewFile } from '../types'
export const COLLECTION_PRESENTATION_LIST = 'list'
export type CollectionPresentationType = typeof COLLECTION_PRESENTATION_LIST
export interface ListCollectionPresentationConfig {
type: typeof COLLECTION_PRESENTATION_LIST
sort: string | null
properties: string[]
}
export type CollectionPresentationConfig = ListCollectionPresentationConfig
export type CollectionOrigin = 'builtin' | 'type' | 'folder' | 'saved-view' | 'neighborhood'
export interface CollectionDefinition {
id: string
label: string
origin: CollectionOrigin
selection: SidebarSelection
presentation: CollectionPresentationConfig
filter?: FilterGroup
entry?: VaultEntry
view?: ViewFile
}

View file

@ -0,0 +1,44 @@
import type { ViewDefinition } from '../types'
import {
COLLECTION_PRESENTATION_LIST,
type CollectionPresentationConfig,
type ListCollectionPresentationConfig,
} from './collectionTypes'
type UnknownRecord = Record<string, unknown>
function isRecord(value: unknown): value is UnknownRecord {
return typeof value === 'object' && value !== null && !Array.isArray(value)
}
function nullableString(value: unknown): string | null {
return typeof value === 'string' && value.trim().length > 0 ? value : null
}
function stringArray(value: unknown): string[] {
return Array.isArray(value) ? value.filter((item): item is string => typeof item === 'string') : []
}
function listPresentation(source: UnknownRecord, fallback: ListCollectionPresentationConfig): ListCollectionPresentationConfig {
return {
type: COLLECTION_PRESENTATION_LIST,
sort: nullableString(source.sort) ?? fallback.sort,
properties: stringArray(source.properties).length > 0 ? stringArray(source.properties) : fallback.properties,
}
}
export function defaultListPresentation(): ListCollectionPresentationConfig {
return { type: COLLECTION_PRESENTATION_LIST, sort: null, properties: [] }
}
export function presentationFromViewDefinition(definition: ViewDefinition): CollectionPresentationConfig {
const fallback: ListCollectionPresentationConfig = {
type: COLLECTION_PRESENTATION_LIST,
sort: nullableString(definition.sort),
properties: stringArray(definition.listPropertiesDisplay),
}
const rawPresentation = Reflect.get(definition as unknown as UnknownRecord, 'presentation')
if (!isRecord(rawPresentation)) return fallback
if (rawPresentation.type !== COLLECTION_PRESENTATION_LIST) return fallback
return listPresentation(rawPresentation, fallback)
}

View file

@ -0,0 +1,80 @@
import { describe, expect, it } from 'vitest'
import type { ViewFile } from '../types'
import { makeEntry } from '../test-utils/noteListTestUtils'
import { collectionFromSelection } from './collectionFromSelection'
import { resolveCollectionEntries } from './resolveCollectionEntries'
describe('resolveCollectionEntries', () => {
it('resolves ordinary collections through current note-list filtering', () => {
const entries = [
makeEntry({ path: '/vault/alpha.md', title: 'Alpha', isA: 'Project' }),
makeEntry({ path: '/vault/beta.md', title: 'Beta', isA: 'Note' }),
]
const collection = collectionFromSelection({ kind: 'sectionGroup', type: 'Project' })
const resolved = resolveCollectionEntries(collection, entries)
expect(resolved.entries.map((entry) => entry.title)).toEqual(['Alpha'])
expect(resolved.entityEntry).toBeNull()
expect(resolved.relationshipGroups).toEqual([])
})
it('keeps Changes and Inbox entries caller-supplied for existing transient flows', () => {
const changes = [makeEntry({ path: '/vault/changed.md', title: 'Changed' })]
const inbox = [makeEntry({ path: '/vault/inbox.md', title: 'Inbox' })]
expect(
resolveCollectionEntries(
collectionFromSelection({ kind: 'filter', filter: 'changes' }),
[],
{ changesEntries: changes },
).entries,
).toBe(changes)
expect(
resolveCollectionEntries(
collectionFromSelection({ kind: 'filter', filter: 'inbox' }),
[],
{ inboxEntries: inbox },
).entries,
).toBe(inbox)
})
it('resolves saved views using their existing YAML filters', () => {
const entries = [
makeEntry({ path: '/vault/alpha.md', title: 'Alpha', isA: 'Project' }),
makeEntry({ path: '/vault/beta.md', title: 'Beta', isA: 'Note' }),
]
const view: ViewFile = {
filename: 'projects.yml',
definition: {
name: 'Projects',
icon: null,
color: null,
sort: null,
filters: { all: [{ field: 'type', op: 'equals', value: 'Project' }] },
},
}
const collection = collectionFromSelection({ kind: 'view', filename: view.filename }, { views: [view] })
const resolved = resolveCollectionEntries(collection, entries, { views: [view] })
expect(resolved.entries.map((entry) => entry.title)).toEqual(['Alpha'])
})
it('resolves neighborhood collections into grouped relationship data', () => {
const alpha = makeEntry({
path: '/vault/alpha.md',
title: 'Alpha',
relationships: { related_to: ['[[beta]]'] },
})
const beta = makeEntry({ path: '/vault/beta.md', filename: 'beta.md', title: 'Beta' })
const collection = collectionFromSelection({ kind: 'entity', entry: alpha })
const resolved = resolveCollectionEntries(collection, [alpha, beta])
expect(resolved.entries).toEqual([])
expect(resolved.entityEntry).toBe(alpha)
expect(resolved.relationshipGroups.some((group) => group.entries.includes(beta))).toBe(true)
})
})

View file

@ -0,0 +1,57 @@
import type { VaultEntry } from '../types'
import {
type FilterEntriesOptions,
type RelationshipGroup,
buildRelationshipGroups,
filterEntries,
} from '../utils/noteListHelpers'
import type { CollectionDefinition } from './collectionTypes'
interface ResolveCollectionEntriesOptions extends FilterEntriesOptions {
changesEntries?: VaultEntry[]
inboxEntries?: VaultEntry[]
}
export interface ResolvedCollectionEntries {
entries: VaultEntry[]
entityEntry: VaultEntry | null
relationshipGroups: RelationshipGroup[]
}
function specialFilterEntries(
collection: CollectionDefinition,
options: ResolveCollectionEntriesOptions,
): VaultEntry[] | null {
const selection = collection.selection
if (selection.kind !== 'filter') return null
if (selection.filter === 'changes') return options.changesEntries ?? []
if (selection.filter === 'inbox') return options.inboxEntries ?? []
return null
}
function currentEntityEntry(collection: CollectionDefinition, entries: VaultEntry[]): VaultEntry | null {
if (collection.selection.kind !== 'entity') return null
const selectedEntry = collection.selection.entry
return entries.find((entry) => entry.path === selectedEntry.path) ?? selectedEntry
}
export function resolveCollectionEntries(
collection: CollectionDefinition,
entries: VaultEntry[],
options: ResolveCollectionEntriesOptions = {},
): ResolvedCollectionEntries {
const entityEntry = currentEntityEntry(collection, entries)
if (entityEntry) {
return {
entries: [],
entityEntry,
relationshipGroups: buildRelationshipGroups(entityEntry, entries),
}
}
return {
entries: specialFilterEntries(collection, options) ?? filterEntries(entries, collection.selection, options),
entityEntry: null,
relationshipGroups: [],
}
}