@dxos/plugin-space 0.13.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/lib/chunk-ImportSpaceDialog.mjs +2 -2
  2. package/dist/lib/chunk-ImportSpaceDialog.mjs.map +1 -1
  3. package/dist/lib/chunk-MembersContainer.mjs +8 -5
  4. package/dist/lib/chunk-MembersContainer.mjs.map +1 -1
  5. package/dist/lib/chunk-ObjectMasonry.mjs +1 -1
  6. package/dist/lib/chunk-ObjectMasonry.mjs.map +1 -1
  7. package/dist/lib/chunk-RecordArticle.mjs +4 -3
  8. package/dist/lib/chunk-RecordArticle.mjs.map +1 -1
  9. package/dist/lib/chunk-SpaceHomeDashboard.mjs +1 -1
  10. package/dist/lib/chunk-SpaceHomeDashboard.mjs.map +1 -1
  11. package/dist/lib/chunk-SpaceHomeRecent.mjs +41 -19
  12. package/dist/lib/chunk-SpaceHomeRecent.mjs.map +1 -1
  13. package/dist/lib/chunk-SyncStatus.mjs +2 -2
  14. package/dist/lib/chunk-SyncStatus.mjs.map +1 -1
  15. package/dist/lib/chunk-TypeArticle.mjs +1 -1
  16. package/dist/lib/chunk-TypeArticle.mjs.map +1 -1
  17. package/dist/lib/chunk-app-graph-builder.mjs +8 -4
  18. package/dist/lib/chunk-app-graph-builder.mjs.map +1 -1
  19. package/dist/lib/components.mjs +3 -2
  20. package/dist/lib/components.mjs.map +1 -1
  21. package/dist/lib/dashboard.mjs +3 -1
  22. package/dist/lib/dashboard.mjs.map +1 -1
  23. package/dist/types/src/capabilities/app-graph-builder/extensions/database.d.ts.map +1 -1
  24. package/dist/types/src/capabilities/app-graph-builder/extensions/shared.d.ts +2 -0
  25. package/dist/types/src/capabilities/app-graph-builder/extensions/shared.d.ts.map +1 -1
  26. package/dist/types/src/containers/MembersContainer/MembersContainer.d.ts.map +1 -1
  27. package/dist/types/src/containers/RecordArticle/RecordArticle.d.ts.map +1 -1
  28. package/dist/types/src/containers/SpaceHomeArticle/SpaceHomeArticle.stories.d.ts.map +1 -1
  29. package/dist/types/src/containers/SpaceHomeRecent/SpaceHomeRecent.d.ts.map +1 -1
  30. package/dist/types/src/dashboard/stats.d.ts +2 -0
  31. package/dist/types/src/dashboard/stats.d.ts.map +1 -1
  32. package/dist/types/tsconfig.tsbuildinfo +1 -1
  33. package/package.json +62 -62
  34. package/src/capabilities/app-graph-builder/extensions/database.ts +5 -3
  35. package/src/capabilities/app-graph-builder/extensions/shared.test.ts +25 -1
  36. package/src/capabilities/app-graph-builder/extensions/shared.ts +6 -0
  37. package/src/components/CreateObjectPanel/CreateObjectPanel.tsx +2 -2
  38. package/src/containers/ImportSpaceDialog/ImportSpaceDialog.tsx +2 -2
  39. package/src/containers/MembersContainer/MembersContainer.tsx +3 -2
  40. package/src/containers/ObjectMasonry/ObjectMasonryArticle.tsx +1 -1
  41. package/src/containers/RecordArticle/RecordArticle.tsx +2 -5
  42. package/src/containers/SpaceHomeArticle/SpaceHomeArticle.stories.tsx +6 -1
  43. package/src/containers/SpaceHomeDashboard/SpaceHomeDashboard.tsx +1 -1
  44. package/src/containers/SpaceHomeRecent/SpaceHomeRecent.tsx +71 -28
  45. package/src/containers/SyncStatus/SyncStatus.tsx +2 -2
  46. package/src/containers/TypeArticle/TypeArticle.tsx +1 -1
  47. package/src/dashboard/stats.test.ts +12 -2
  48. package/src/dashboard/stats.ts +4 -0
@@ -1 +1 @@
1
- {"version":3,"file":"components.mjs","names":[],"sources":["../../src/components/CreateObjectPanel/CreateObjectPanel.tsx","../../src/components/ForeignKeys/ForeignKeys.tsx","../../src/components/RelatedTypeFilter/RelatedTypeFilter.tsx","../../src/components/SyncStatus/save-tracker.ts","../../src/components/SyncStatus/status.ts"],"sourcesContent":["//\n// Copyright 2024 DXOS.org\n//\n\nimport type * as Schema from 'effect/Schema';\nimport React, { useCallback, useMemo, useRef } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport { type Database, Obj, type Type } from '@dxos/echo';\nimport { type AnyProperties } from '@dxos/echo/internal';\nimport { type Space } from '@dxos/react-client/echo';\nimport { Form, ObjectForm, omitId, useFormContext, useSubmitOnEnter } from '@dxos/react-ui-form';\nimport { Picker } from '@dxos/react-ui-list';\nimport { SearchList, useSearchListResults } from '@dxos/react-ui-search';\nimport * as Button from '@dxos/react-ui/Button';\nimport * as Hooks from '@dxos/react-ui/Hooks';\nimport * as Icon from '@dxos/react-ui/Icon';\nimport * as Layout from '@dxos/react-ui/Layout';\nimport * as Theme from '@dxos/react-ui/Theme';\nimport { getStyles } from '@dxos/ui-theme';\nimport { type MaybePromise } from '@dxos/util';\n\nimport { useInputSurfaceLookup } from '#hooks';\nimport { meta } from '#meta';\nimport { SpaceCapabilities } from '#types';\n\nimport { getSpaceDisplayName } from '../../util/index.ts';\n\n/** Display-ready option for the create object search list. */\nexport type CreateObjectOption = {\n id: string;\n label: string;\n icon?: string;\n iconHue?: string;\n /** Plugin name shown as \"{{plugin}} Plugin\" subtitle. */\n plugin?: string;\n /** Generic subtitle shown when plugin is not set. */\n description?: string;\n};\n\nexport type Metadata = SpaceCapabilities.CreateObjectEntry;\n\nexport type CreateObjectPanelProps = {\n options: CreateObjectOption[];\n spaces: Space[];\n typename?: string;\n target?: Database.Database | Obj.Unknown;\n /** Whether the object is built from the form's values on submit (`draft`) or already exists (`live`). */\n mode?: 'draft' | 'live';\n initialFormValues?: Partial<AnyProperties>;\n /**\n * Form schema, overriding the create entry's `inputSchema` (draft) or the object's own schema\n * (live). Typically a projection of the type, e.g. `Type.getSchema(T).pipe(Schema.pick(...))`.\n */\n schema?: Schema.Codec<any, any>;\n /**\n * The live object being edited, once the dialog has added it to the database. Its presence is what\n * switches the panel from building a draft on submit to writing through to a real object.\n */\n object?: Obj.Unknown;\n /** The live object's type; required alongside `object`. */\n type?: Type.AnyEntity;\n resolve?: (typename: string) => Metadata | undefined;\n onTargetChange?: (target: Database.Database) => void;\n onTypenameChange?: (typename: string) => void;\n onCreateObject?: (params: { metadata: Metadata; data?: Record<string, any> }) => MaybePromise<void>;\n /** Abandons the create; the draft form offers a Cancel button only when this is supplied. */\n onCancel?: () => void;\n /**\n * Where the draft form's Cancel and Create go, e.g. a dialog's footer; inline under the fields when absent. A portal\n * rather than a lifted form, since only the schema-form step of the panel has actions.\n */\n actionsContainer?: HTMLElement | null;\n};\n\nexport const CreateObjectPanel = ({\n options,\n spaces,\n typename,\n target,\n mode = 'draft',\n initialFormValues: initialFormValuesProp,\n schema,\n object,\n type,\n resolve,\n onTargetChange,\n onTypenameChange,\n onCreateObject,\n onCancel,\n actionsContainer,\n}: CreateObjectPanelProps) => {\n const initialFormValues = Hooks.useDefaultValue(initialFormValuesProp, () => ({}));\n const metadata = typename && resolve?.(typename);\n\n const sortedOptions = useMemo(() => [...options].sort((a, b) => a.label.localeCompare(b.label)), [options]);\n\n const handleCreateObject = useCallback(\n async (props: Record<string, any>) => {\n if (!metadata) {\n return;\n }\n await onCreateObject?.({ metadata, data: props });\n },\n [onCreateObject, metadata],\n );\n\n const handleSelectOption = useCallback(\n async (id: string) => {\n const metadata = resolve?.(id);\n // A live create always has a form to show — the object's own — so only a draft can skip\n // straight to creating from an entry that declares no inputs.\n if (mode !== 'live' && metadata && !metadata.inputSchema && !metadata.customPanel && !schema) {\n // No form to show, so the caller's defaults (a name typed into a link, say) are the data.\n await onCreateObject?.({ metadata, data: initialFormValues });\n } else {\n onTypenameChange?.(id);\n }\n },\n [mode, schema, resolve, initialFormValues, onCreateObject, onTypenameChange],\n );\n\n const inputSchema = useMemo(() => {\n const base = schema ?? (metadata && typeof metadata === 'object' ? metadata.inputSchema : undefined);\n return base ? omitId(base) : undefined;\n }, [schema, metadata]);\n const inputSurfaceLookup = useInputSurfaceLookup({ target });\n\n // TODO(wittjosiah): Extends and use react-ui-form to handle variants.\n\n // The live object edits in place, so the type is settled and the submit lives in the dialog's\n // action bar rather than in the form.\n if (object && type) {\n return <ObjectForm object={object} type={type} schema={schema} showTags={false} />;\n }\n\n // The type picker belongs to the case where no type has been chosen. Gating it on the entry\n // instead would also catch a dialog opened *for* a type whose plugin is still activating, and\n // flash the full list of every creatable type into a dialog already titled after one of them.\n if (!typename) {\n return <SelectType options={sortedOptions} onChange={handleSelectOption} />;\n }\n\n // A live create is driven by the type entity rather than a registered create entry, so only a\n // draft waits on one; until it resolves there is nothing correct to draw.\n if (mode !== 'live' && !metadata) {\n return null;\n }\n\n if (!target) {\n return <SelectSpace spaces={spaces} onChange={onTargetChange} />;\n }\n\n // Live: both pickers are answered and the dialog is adding the object; it arrives next render.\n if (!metadata) {\n return null;\n }\n\n if (metadata.customPanel) {\n const CustomPanel = metadata.customPanel;\n return (\n <CustomPanel\n target={target}\n initialFormValues={initialFormValues}\n onCreateObject={(data) => handleCreateObject(data)}\n onCancel={onCancel}\n />\n );\n }\n\n if (inputSchema) {\n return (\n <Form.Root\n autoFocus\n schema={inputSchema}\n defaultValues={initialFormValues}\n fieldProvider={inputSurfaceLookup}\n db={Obj.isObject(target) ? Obj.getDatabase(target) : target}\n onSave={handleCreateObject}\n testId='create-object-form'\n >\n <Form.Viewport>\n <CreateObjectFormContent onCancel={onCancel} actionsContainer={actionsContainer} />\n </Form.Viewport>\n </Form.Root>\n );\n }\n\n return null;\n};\n\nCreateObjectPanel.displayName = 'CreateObjectPanel';\n\ntype CreateObjectFormContentProps = Pick<CreateObjectPanelProps, 'onCancel' | 'actionsContainer'>;\n\n/** The draft form's body: its fields, then Cancel and Create; Enter in a single-line field creates. */\nconst CreateObjectFormContent = ({ onCancel, actionsContainer }: CreateObjectFormContentProps) => {\n const { t } = Hooks.useTranslation(meta.profile.key);\n const {\n form: { canSave, onSave },\n } = useFormContext(CreateObjectFormContent.displayName);\n const contentRef = useRef<HTMLDivElement>(null);\n const handleSubmit = useCallback(() => {\n if (canSave) {\n void onSave();\n }\n }, [canSave, onSave]);\n useSubmitOnEnter(contentRef, handleSubmit);\n\n const actions = (\n <>\n {onCancel && (\n <Button.Root onClick={onCancel} data-testid='cancel-button'>\n {t('object-form-cancel.label')}\n </Button.Root>\n )}\n <Button.Root variant='primary' disabled={!canSave} onClick={handleSubmit} data-testid='save-button'>\n {t('object-form-confirm.label')}\n </Button.Root>\n </>\n );\n\n return (\n <Form.Content ref={contentRef}>\n <Form.Fields />\n {actionsContainer ? (\n createPortal(actions, actionsContainer)\n ) : (\n <Layout.Flex gap='sm' justify='end' classNames='pt-form-padding'>\n {actions}\n </Layout.Flex>\n )}\n </Form.Content>\n );\n};\n\nCreateObjectFormContent.displayName = 'CreateObjectPanel.FormContent';\n\ntype SelectTypeProps = Pick<CreateObjectPanelProps, 'options'> & {\n onChange: (id: string) => void;\n};\n\nconst SelectType = ({ options, onChange }: SelectTypeProps) => {\n const { t } = Hooks.useTranslation(meta.profile.key);\n\n const { results, handleSearch } = useSearchListResults({\n items: options,\n // Match the type label as well as the contributing plugin name / description so a plugin\n // name (e.g. \"Blogger\") surfaces the types it contributes.\n extract: (option) => [option.label, option.plugin, option.description].filter(Boolean).join(' '),\n });\n\n return (\n // Types arrive as plugins contribute them, so the highlight follows the list's first item rather than the first seen.\n <SearchList.Root onSearch={handleSearch} resetSelectionOnChange>\n <SearchList.Input\n classNames='mb-form-gap'\n autoFocus\n data-testid='create-object-form.schema-input'\n placeholder={t('schema-input.placeholder')}\n />\n <SearchList.Viewport>\n {results.map((option) => (\n <Picker.Item\n key={option.id}\n value={option.id}\n onSelect={() => onChange(option.id)}\n classNames='flex gap-3 items-center px-2 py-2 rounded-xs'\n // Keyed by typename, since the label is localized and, for database types, user-authored.\n data-testid={`create-object-form.type.${option.id}`}\n >\n <Icon.Icon\n icon={option.icon ?? 'ph--circle-dashed--regular'}\n size='xl'\n classNames={getIconHueStyles(option.iconHue)}\n />\n <div className='flex flex-col min-w-0 grow gap-0.5'>\n <span className='truncate'>{option.label}</span>\n {(option.plugin || option.description) && (\n <span className='truncate text-fg-muted text-xs'>\n {option.plugin ? t('plugin-subtitle.label', { plugin: option.plugin }) : option.description}\n </span>\n )}\n </div>\n </Picker.Item>\n ))}\n </SearchList.Viewport>\n </SearchList.Root>\n );\n};\n\ntype SelectSpaceProps = Pick<CreateObjectPanelProps, 'spaces'> & {\n onChange?: (db: Database.Database) => void;\n};\n\nconst SelectSpace = ({ spaces, onChange }: SelectSpaceProps) => {\n const { t } = Hooks.useTranslation(meta.profile.key);\n\n const sortedSpaces = useMemo(\n () =>\n [...spaces].sort((a, b) => {\n const labelA = Theme.toLocalizedString(getSpaceDisplayName(a), t);\n const labelB = Theme.toLocalizedString(getSpaceDisplayName(b), t);\n return labelA.localeCompare(labelB);\n }),\n [spaces, t],\n );\n\n const { results, handleSearch } = useSearchListResults({\n items: sortedSpaces,\n extract: (space) => Theme.toLocalizedString(getSpaceDisplayName(space), t),\n });\n\n // TODO(burdon): Change to Masonry.\n return (\n // Types arrive as plugins contribute them, so the highlight follows the list's first item rather than the first seen.\n <SearchList.Root onSearch={handleSearch} resetSelectionOnChange>\n <SearchList.Input\n classNames='mb-form-gap'\n autoFocus\n data-testid='create-object-form.space-input'\n placeholder={t('space-input.placeholder')}\n />\n <SearchList.Viewport>\n {results.map((space) => (\n <SearchList.Item\n key={space.id}\n value={space.id}\n label={Theme.toLocalizedString(getSpaceDisplayName(space), t)}\n onSelect={() => onChange?.(space.db)}\n />\n ))}\n </SearchList.Viewport>\n </SearchList.Root>\n );\n};\n\nconst getIconHueStyles = (iconHue?: string): string | undefined => {\n const styles = iconHue ? getStyles(iconHue) : undefined;\n return styles?.text;\n};\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport React, { useCallback } from 'react';\n\nimport { type Key } from '@dxos/echo';\nimport { Listbox } from '@dxos/react-ui-list';\nimport * as Button from '@dxos/react-ui/Button';\nimport * as Hooks from '@dxos/react-ui/Hooks';\n\nimport { meta } from '#meta';\n\nexport type ForeignKeysProps = {\n keys: Key.ForeignKey[];\n onDelete?: (key: Key.ForeignKey) => void;\n};\n\n// TODO(wittjosiah): This is a clone of `TokenManager`. Consider a form variant for arrays of read-only objects.\nexport const ForeignKeys = ({ keys, onDelete }: ForeignKeysProps) => {\n return (\n <Listbox.Root items={keys.map((key) => ({ value: key.id, label: key.source, description: key.id }))}>\n <Listbox.Content classNames='gap-2'>\n {keys.map((key) => (\n <KeyItem key={key.id} forignKey={key} onDelete={onDelete} />\n ))}\n </Listbox.Content>\n </Listbox.Root>\n );\n};\n\ntype KeyItemProps = {\n forignKey: Key.ForeignKey;\n onDelete?: (key: Key.ForeignKey) => void;\n};\n\nconst KeyItem = ({ forignKey, onDelete }: KeyItemProps) => {\n const { t } = Hooks.useTranslation(meta.profile.key);\n\n const handleDelete = useCallback(() => {\n onDelete?.(forignKey);\n }, [forignKey, onDelete]);\n\n return (\n <Listbox.Item id={forignKey.id}>\n <Listbox.ItemText />\n <Listbox.ItemDescription />\n <Button.Root\n iconOnly\n icon='ph--x--regular'\n variant='ghost'\n label={t('delete-key.button')}\n onClick={handleDelete}\n />\n </Listbox.Item>\n );\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport React from 'react';\n\nimport * as Hooks from '@dxos/react-ui/Hooks';\nimport * as ToggleGroup from '@dxos/react-ui/ToggleGroup';\nimport { mx } from '@dxos/ui-theme';\n\nimport { type RelatedType } from '#hooks';\nimport { meta } from '#meta';\n\nexport type RelatedTypeFilterProps = {\n types: RelatedType[];\n onToggle: (typename: string) => void;\n classNames?: string;\n};\n\n/**\n * Toggles which types a related-object view shows, one item per type present in the set.\n *\n * Uses the standalone `ToggleGroup` because the record's section header, one of its two hosts, has\n * no toolbar to supply the roving-focus context `Toolbar.ToggleGroup` requires.\n */\nexport const RelatedTypeFilter = ({ types, onToggle, classNames }: RelatedTypeFilterProps) => {\n const { t } = Hooks.useTranslation(meta.profile.key);\n // Filtering by the only type present cannot narrow anything.\n if (types.length < 2) {\n return null;\n }\n\n return (\n <ToggleGroup.Root\n type='multiple'\n aria-label={t('type-filter.label')}\n // Gapped, so each type reads as its own control rather than one segmented bar; grouped\n // buttons drop their own rounding, which the gap makes visible again.\n classNames={mx('gap-1 [&>button]:rounded-xs', classNames)}\n value={types.filter(({ visible }) => visible).map(({ typename }) => typename)}\n >\n {types.map(({ typename, label, icon, count }) => (\n <ToggleGroup.Item\n key={typename}\n iconOnly\n value={typename}\n icon={icon}\n // Selection reads off the icon alone: the pressed fill is pinned to the resting one so\n // the chip itself never changes, leaving `text-fg-subtle` to mark a type as hidden.\n classNames='aria-pressed:bg-input-bg aria-[pressed=false]:text-fg-subtle'\n // Carries the count to the tooltip; the type's label is already localized.\n label={`${label} (${count})`}\n onClick={() => onToggle(typename)}\n />\n ))}\n </ToggleGroup.Root>\n );\n};\n\nRelatedTypeFilter.displayName = 'RelatedTypeFilter';\n","//\n// Copyright 2024 DXOS.org\n//\n\nimport { type CleanupFn } from '@dxos/async';\nimport { type Client } from '@dxos/client';\nimport { type Space } from '@dxos/client/echo';\nimport { Context } from '@dxos/context';\nimport { type EchoDatabase } from '@dxos/echo-client';\nimport { type SpaceId } from '@dxos/keys';\nimport { log } from '@dxos/log';\n\nexport const createClientSaveTracker = (client: Client, cb: (state: 'saved' | 'saving') => void) => {\n const CleanupFns: Record<SpaceId, CleanupFn> = {};\n const state: Record<SpaceId, 'saved' | 'saving'> = {};\n\n const install = (spaces: Space[]) => {\n for (const space of spaces) {\n if (state[space.id]) {\n continue;\n }\n\n state[space.id] = 'saved';\n CleanupFns[space.id] = createSpaceSaveTracker(space, (s) => {\n state[space.id] = s;\n cb(Object.values(state).some((s) => s === 'saving') ? 'saving' : 'saved');\n });\n }\n };\n client.spaces.subscribe((spaces) => {\n install(spaces);\n });\n install(client.spaces.get());\n\n return () => {\n for (const unsubscribe of Object.values(CleanupFns)) {\n unsubscribe();\n }\n };\n};\n\n/** The part of a {@link Space} the save tracker reads. */\nexport type SaveTrackedSpace = {\n readonly db: Pick<EchoDatabase, 'saveStateChanged' | 'flush'>;\n waitUntilReady(): Promise<unknown>;\n};\n\nexport const createSpaceSaveTracker = (space: SaveTrackedSpace, cb: (state: 'saved' | 'saving') => void): CleanupFn => {\n const ctx = new Context();\n\n void space\n .waitUntilReady()\n .then(() => {\n if (ctx.disposed) {\n return;\n }\n\n let hasUnsavedChanges = false;\n let lastFlushPromise: Promise<void> | undefined;\n space.db.saveStateChanged.on(ctx, ({ unsavedDocuments }) => {\n hasUnsavedChanges = unsavedDocuments.length > 0;\n });\n space.db.saveStateChanged.debounce(500).on(ctx, () => {\n if (hasUnsavedChanges) {\n lastFlushPromise = undefined;\n cb('saving');\n } else {\n const flushPromise = space.db.flush();\n lastFlushPromise = flushPromise;\n void flushPromise.then(\n () => {\n if (lastFlushPromise === flushPromise) {\n cb('saved');\n }\n },\n (error: unknown) => {\n // The failed write is logged where it failed; here it only means the space is not saved.\n log.verbose('space flush failed', { error });\n if (lastFlushPromise === flushPromise) {\n cb('saving');\n }\n },\n );\n }\n });\n })\n .catch((err) => log.catch(err));\n\n return () => {\n void ctx.dispose();\n };\n};\n","//\n// Copyright 2024 DXOS.org\n//\n\nexport type Status =\n | 'saving-locally'\n | 'downloading'\n | 'uploading'\n | 'stalled'\n | 'disconnected'\n | 'offline-persisted'\n | 'remote-synced';\n\nexport type StatusInput = {\n offline: boolean;\n saved: boolean;\n /** Replication is outstanding but making no progress. */\n stalled: boolean;\n needsToUpload: boolean;\n needsToDownload: boolean;\n};\n\nexport const getStatus = ({ offline, saved, stalled, needsToUpload, needsToDownload }: StatusInput): Status => {\n if (!saved) {\n return 'saving-locally';\n } else if (offline) {\n // Offline with nothing outstanding is durable; offline with pending work is not.\n return needsToUpload || needsToDownload ? 'disconnected' : 'offline-persisted';\n } else if (stalled) {\n return 'stalled';\n } else if (needsToDownload) {\n return 'downloading';\n } else if (needsToUpload) {\n return 'uploading';\n } else {\n return 'remote-synced';\n }\n};\n\nexport const getIcon = (status: Status) => {\n switch (status) {\n case 'saving-locally':\n return 'ph--download--regular';\n case 'downloading':\n return 'ph--cloud-arrow-down--regular';\n case 'uploading':\n return 'ph--cloud-arrow-up--regular';\n case 'stalled':\n return 'ph--cloud-warning--regular';\n case 'disconnected':\n return 'ph--cloud-slash--regular';\n case 'offline-persisted':\n return 'ph--check-circle--regular';\n case 'remote-synced':\n return 'ph--cloud-check--regular';\n }\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"components.mjs","names":[],"sources":["../../src/components/CreateObjectPanel/CreateObjectPanel.tsx","../../src/components/ForeignKeys/ForeignKeys.tsx","../../src/components/RelatedTypeFilter/RelatedTypeFilter.tsx","../../src/components/SyncStatus/save-tracker.ts","../../src/components/SyncStatus/status.ts"],"sourcesContent":["//\n// Copyright 2024 DXOS.org\n//\n\nimport type * as Schema from 'effect/Schema';\nimport React, { useCallback, useMemo, useRef } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport { type Database, Obj, type Type } from '@dxos/echo';\nimport { type AnyProperties } from '@dxos/echo/internal';\nimport { type Space } from '@dxos/react-client/echo';\nimport { Form, ObjectForm, omitId, useFormContext, useSubmitOnEnter } from '@dxos/react-ui-form';\nimport { Picker } from '@dxos/react-ui-list';\nimport { SearchList, useSearchListResults } from '@dxos/react-ui-search';\nimport * as Button from '@dxos/react-ui/Button';\nimport * as Hooks from '@dxos/react-ui/Hooks';\nimport * as Icon from '@dxos/react-ui/Icon';\nimport * as Layout from '@dxos/react-ui/Layout';\nimport * as Theme from '@dxos/react-ui/Theme';\nimport { getStyles } from '@dxos/ui-theme';\nimport { type MaybePromise } from '@dxos/util';\n\nimport { useInputSurfaceLookup } from '#hooks';\nimport { meta } from '#meta';\nimport { SpaceCapabilities } from '#types';\n\nimport { getSpaceDisplayName } from '../../util/index.ts';\n\n/** Display-ready option for the create object search list. */\nexport type CreateObjectOption = {\n id: string;\n label: string;\n icon?: string;\n iconHue?: string;\n /** Plugin name shown as \"{{plugin}} Plugin\" subtitle. */\n plugin?: string;\n /** Generic subtitle shown when plugin is not set. */\n description?: string;\n};\n\nexport type Metadata = SpaceCapabilities.CreateObjectEntry;\n\nexport type CreateObjectPanelProps = {\n options: CreateObjectOption[];\n spaces: Space[];\n typename?: string;\n target?: Database.Database | Obj.Unknown;\n /** Whether the object is built from the form's values on submit (`draft`) or already exists (`live`). */\n mode?: 'draft' | 'live';\n initialFormValues?: Partial<AnyProperties>;\n /**\n * Form schema, overriding the create entry's `inputSchema` (draft) or the object's own schema\n * (live). Typically a projection of the type, e.g. `Type.getSchema(T).pipe(Schema.pick(...))`.\n */\n schema?: Schema.Codec<any, any>;\n /**\n * The live object being edited, once the dialog has added it to the database. Its presence is what\n * switches the panel from building a draft on submit to writing through to a real object.\n */\n object?: Obj.Unknown;\n /** The live object's type; required alongside `object`. */\n type?: Type.AnyEntity;\n resolve?: (typename: string) => Metadata | undefined;\n onTargetChange?: (target: Database.Database) => void;\n onTypenameChange?: (typename: string) => void;\n onCreateObject?: (params: { metadata: Metadata; data?: Record<string, any> }) => MaybePromise<void>;\n /** Abandons the create; the draft form offers a Cancel button only when this is supplied. */\n onCancel?: () => void;\n /**\n * Where the draft form's Cancel and Create go, e.g. a dialog's footer; inline under the fields when absent. A portal\n * rather than a lifted form, since only the schema-form step of the panel has actions.\n */\n actionsContainer?: HTMLElement | null;\n};\n\nexport const CreateObjectPanel = ({\n options,\n spaces,\n typename,\n target,\n mode = 'draft',\n initialFormValues: initialFormValuesProp,\n schema,\n object,\n type,\n resolve,\n onTargetChange,\n onTypenameChange,\n onCreateObject,\n onCancel,\n actionsContainer,\n}: CreateObjectPanelProps) => {\n const initialFormValues = Hooks.useDefaultValue(initialFormValuesProp, () => ({}));\n const metadata = typename && resolve?.(typename);\n\n const sortedOptions = useMemo(() => [...options].sort((a, b) => a.label.localeCompare(b.label)), [options]);\n\n const handleCreateObject = useCallback(\n async (props: Record<string, any>) => {\n if (!metadata) {\n return;\n }\n await onCreateObject?.({ metadata, data: props });\n },\n [onCreateObject, metadata],\n );\n\n const handleSelectOption = useCallback(\n async (id: string) => {\n const metadata = resolve?.(id);\n // A live create always has a form to show — the object's own — so only a draft can skip\n // straight to creating from an entry that declares no inputs.\n if (mode !== 'live' && metadata && !metadata.inputSchema && !metadata.customPanel && !schema) {\n // No form to show, so the caller's defaults (a name typed into a link, say) are the data.\n await onCreateObject?.({ metadata, data: initialFormValues });\n } else {\n onTypenameChange?.(id);\n }\n },\n [mode, schema, resolve, initialFormValues, onCreateObject, onTypenameChange],\n );\n\n const inputSchema = useMemo(() => {\n const base = schema ?? (metadata && typeof metadata === 'object' ? metadata.inputSchema : undefined);\n return base ? omitId(base) : undefined;\n }, [schema, metadata]);\n const inputSurfaceLookup = useInputSurfaceLookup({ target });\n\n // TODO(wittjosiah): Extends and use react-ui-form to handle variants.\n\n // The live object edits in place, so the type is settled and the submit lives in the dialog's\n // action bar rather than in the form.\n if (object && type) {\n return <ObjectForm object={object} type={type} schema={schema} showTags={false} />;\n }\n\n // The type picker belongs to the case where no type has been chosen. Gating it on the entry\n // instead would also catch a dialog opened *for* a type whose plugin is still activating, and\n // flash the full list of every creatable type into a dialog already titled after one of them.\n if (!typename) {\n return <SelectType options={sortedOptions} onChange={handleSelectOption} />;\n }\n\n // A live create is driven by the type entity rather than a registered create entry, so only a\n // draft waits on one; until it resolves there is nothing correct to draw.\n if (mode !== 'live' && !metadata) {\n return null;\n }\n\n if (!target) {\n return <SelectSpace spaces={spaces} onChange={onTargetChange} />;\n }\n\n // Live: both pickers are answered and the dialog is adding the object; it arrives next render.\n if (!metadata) {\n return null;\n }\n\n if (metadata.customPanel) {\n const CustomPanel = metadata.customPanel;\n return (\n <CustomPanel\n target={target}\n initialFormValues={initialFormValues}\n onCreateObject={(data) => handleCreateObject(data)}\n onCancel={onCancel}\n />\n );\n }\n\n if (inputSchema) {\n return (\n <Form.Root\n autoFocus\n schema={inputSchema}\n defaultValues={initialFormValues}\n fieldProvider={inputSurfaceLookup}\n db={Obj.isObject(target) ? Obj.getDatabase(target) : target}\n onSave={handleCreateObject}\n testId='create-object-form'\n >\n <Form.Viewport>\n <CreateObjectFormContent onCancel={onCancel} actionsContainer={actionsContainer} />\n </Form.Viewport>\n </Form.Root>\n );\n }\n\n return null;\n};\n\nCreateObjectPanel.displayName = 'CreateObjectPanel';\n\ntype CreateObjectFormContentProps = Pick<CreateObjectPanelProps, 'onCancel' | 'actionsContainer'>;\n\n/** The draft form's body: its fields, then Cancel and Create; Enter in a single-line field creates. */\nconst CreateObjectFormContent = ({ onCancel, actionsContainer }: CreateObjectFormContentProps) => {\n const { t } = Hooks.useTranslation(meta.profile.key);\n const {\n form: { canSave, onSave },\n } = useFormContext(CreateObjectFormContent.displayName);\n const contentRef = useRef<HTMLDivElement>(null);\n const handleSubmit = useCallback(() => {\n if (canSave) {\n void onSave();\n }\n }, [canSave, onSave]);\n useSubmitOnEnter(contentRef, handleSubmit);\n\n const actions = (\n <>\n {onCancel && (\n <Button.Root onClick={onCancel} data-testid='cancel-button'>\n {t('object-form-cancel.label')}\n </Button.Root>\n )}\n <Button.Root variant='primary' disabled={!canSave} onClick={handleSubmit} data-testid='save-button'>\n {t('object-form-confirm.label')}\n </Button.Root>\n </>\n );\n\n return (\n <Form.Content ref={contentRef}>\n <Form.Fields />\n {actionsContainer ? (\n createPortal(actions, actionsContainer)\n ) : (\n <Layout.Flex gap='sm' justify='end' classNames='pt-form-padding'>\n {actions}\n </Layout.Flex>\n )}\n </Form.Content>\n );\n};\n\nCreateObjectFormContent.displayName = 'CreateObjectPanel.FormContent';\n\ntype SelectTypeProps = Pick<CreateObjectPanelProps, 'options'> & {\n onChange: (id: string) => void;\n};\n\nconst SelectType = ({ options, onChange }: SelectTypeProps) => {\n const { t } = Hooks.useTranslation(meta.profile.key);\n\n const { results, handleSearch } = useSearchListResults({\n items: options,\n // Match the type label as well as the contributing plugin name / description so a plugin\n // name (e.g. \"Blogger\") surfaces the types it contributes.\n extract: (option) => [option.label, option.plugin, option.description].filter(Boolean).join(' '),\n });\n\n return (\n // Types arrive as plugins contribute them, so the highlight follows the list's first item rather than the first seen.\n <SearchList.Root onSearch={handleSearch} resetSelectionOnChange>\n <SearchList.Input\n classNames='mb-form-gap'\n autoFocus\n data-testid='create-object-form.schema-input'\n placeholder={t('schema-input.placeholder')}\n />\n <SearchList.Viewport>\n {results.map((option) => (\n <Picker.Item\n key={option.id}\n value={option.id}\n onSelect={() => onChange(option.id)}\n classNames='flex gap-3 items-center px-2 py-2 rounded-xs'\n // Keyed by typename, since the label is localized and, for database types, user-authored.\n data-testid={`create-object-form.type.${option.id}`}\n >\n <Icon.Icon\n icon={option.icon ?? 'ph--circle-dashed--regular'}\n size='xl'\n classNames={getIconHueStyles(option.iconHue)}\n />\n <Layout.Flex column classNames='min-w-0 grow gap-0.5'>\n <span className='truncate'>{option.label}</span>\n {(option.plugin || option.description) && (\n <span className='truncate text-fg-muted text-xs'>\n {option.plugin ? t('plugin-subtitle.label', { plugin: option.plugin }) : option.description}\n </span>\n )}\n </Layout.Flex>\n </Picker.Item>\n ))}\n </SearchList.Viewport>\n </SearchList.Root>\n );\n};\n\ntype SelectSpaceProps = Pick<CreateObjectPanelProps, 'spaces'> & {\n onChange?: (db: Database.Database) => void;\n};\n\nconst SelectSpace = ({ spaces, onChange }: SelectSpaceProps) => {\n const { t } = Hooks.useTranslation(meta.profile.key);\n\n const sortedSpaces = useMemo(\n () =>\n [...spaces].sort((a, b) => {\n const labelA = Theme.toLocalizedString(getSpaceDisplayName(a), t);\n const labelB = Theme.toLocalizedString(getSpaceDisplayName(b), t);\n return labelA.localeCompare(labelB);\n }),\n [spaces, t],\n );\n\n const { results, handleSearch } = useSearchListResults({\n items: sortedSpaces,\n extract: (space) => Theme.toLocalizedString(getSpaceDisplayName(space), t),\n });\n\n // TODO(burdon): Change to Masonry.\n return (\n // Types arrive as plugins contribute them, so the highlight follows the list's first item rather than the first seen.\n <SearchList.Root onSearch={handleSearch} resetSelectionOnChange>\n <SearchList.Input\n classNames='mb-form-gap'\n autoFocus\n data-testid='create-object-form.space-input'\n placeholder={t('space-input.placeholder')}\n />\n <SearchList.Viewport>\n {results.map((space) => (\n <SearchList.Item\n key={space.id}\n value={space.id}\n label={Theme.toLocalizedString(getSpaceDisplayName(space), t)}\n onSelect={() => onChange?.(space.db)}\n />\n ))}\n </SearchList.Viewport>\n </SearchList.Root>\n );\n};\n\nconst getIconHueStyles = (iconHue?: string): string | undefined => {\n const styles = iconHue ? getStyles(iconHue) : undefined;\n return styles?.text;\n};\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport React, { useCallback } from 'react';\n\nimport { type Key } from '@dxos/echo';\nimport { Listbox } from '@dxos/react-ui-list';\nimport * as Button from '@dxos/react-ui/Button';\nimport * as Hooks from '@dxos/react-ui/Hooks';\n\nimport { meta } from '#meta';\n\nexport type ForeignKeysProps = {\n keys: Key.ForeignKey[];\n onDelete?: (key: Key.ForeignKey) => void;\n};\n\n// TODO(wittjosiah): This is a clone of `TokenManager`. Consider a form variant for arrays of read-only objects.\nexport const ForeignKeys = ({ keys, onDelete }: ForeignKeysProps) => {\n return (\n <Listbox.Root items={keys.map((key) => ({ value: key.id, label: key.source, description: key.id }))}>\n <Listbox.Content classNames='gap-2'>\n {keys.map((key) => (\n <KeyItem key={key.id} forignKey={key} onDelete={onDelete} />\n ))}\n </Listbox.Content>\n </Listbox.Root>\n );\n};\n\ntype KeyItemProps = {\n forignKey: Key.ForeignKey;\n onDelete?: (key: Key.ForeignKey) => void;\n};\n\nconst KeyItem = ({ forignKey, onDelete }: KeyItemProps) => {\n const { t } = Hooks.useTranslation(meta.profile.key);\n\n const handleDelete = useCallback(() => {\n onDelete?.(forignKey);\n }, [forignKey, onDelete]);\n\n return (\n <Listbox.Item id={forignKey.id}>\n <Listbox.ItemText />\n <Listbox.ItemDescription />\n <Button.Root\n iconOnly\n icon='ph--x--regular'\n variant='ghost'\n label={t('delete-key.button')}\n onClick={handleDelete}\n />\n </Listbox.Item>\n );\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport React from 'react';\n\nimport * as Hooks from '@dxos/react-ui/Hooks';\nimport * as ToggleGroup from '@dxos/react-ui/ToggleGroup';\nimport { mx } from '@dxos/ui-theme';\n\nimport { type RelatedType } from '#hooks';\nimport { meta } from '#meta';\n\nexport type RelatedTypeFilterProps = {\n types: RelatedType[];\n onToggle: (typename: string) => void;\n classNames?: string;\n};\n\n/**\n * Toggles which types a related-object view shows, one item per type present in the set.\n *\n * Uses the standalone `ToggleGroup` because the record's section header, one of its two hosts, has\n * no toolbar to supply the roving-focus context `Toolbar.ToggleGroup` requires.\n */\nexport const RelatedTypeFilter = ({ types, onToggle, classNames }: RelatedTypeFilterProps) => {\n const { t } = Hooks.useTranslation(meta.profile.key);\n // Filtering by the only type present cannot narrow anything.\n if (types.length < 2) {\n return null;\n }\n\n return (\n <ToggleGroup.Root\n type='multiple'\n aria-label={t('type-filter.label')}\n // Gapped, so each type reads as its own control rather than one segmented bar; grouped\n // buttons drop their own rounding, which the gap makes visible again.\n classNames={mx('gap-1 [&>button]:rounded-xs', classNames)}\n value={types.filter(({ visible }) => visible).map(({ typename }) => typename)}\n >\n {types.map(({ typename, label, icon, count }) => (\n <ToggleGroup.Item\n key={typename}\n iconOnly\n value={typename}\n icon={icon}\n // Selection reads off the icon alone: the pressed fill is pinned to the resting one so\n // the chip itself never changes, leaving `text-fg-subtle` to mark a type as hidden.\n classNames='aria-pressed:bg-input-bg aria-[pressed=false]:text-fg-subtle'\n // Carries the count to the tooltip; the type's label is already localized.\n label={`${label} (${count})`}\n onClick={() => onToggle(typename)}\n />\n ))}\n </ToggleGroup.Root>\n );\n};\n\nRelatedTypeFilter.displayName = 'RelatedTypeFilter';\n","//\n// Copyright 2024 DXOS.org\n//\n\nimport { type CleanupFn } from '@dxos/async';\nimport { type Client } from '@dxos/client';\nimport { type Space } from '@dxos/client/echo';\nimport { Context } from '@dxos/context';\nimport { type EchoDatabase } from '@dxos/echo-client';\nimport { type SpaceId } from '@dxos/keys';\nimport { log } from '@dxos/log';\n\nexport const createClientSaveTracker = (client: Client, cb: (state: 'saved' | 'saving') => void) => {\n const CleanupFns: Record<SpaceId, CleanupFn> = {};\n const state: Record<SpaceId, 'saved' | 'saving'> = {};\n\n const install = (spaces: Space[]) => {\n for (const space of spaces) {\n if (state[space.id]) {\n continue;\n }\n\n state[space.id] = 'saved';\n CleanupFns[space.id] = createSpaceSaveTracker(space, (s) => {\n state[space.id] = s;\n cb(Object.values(state).some((s) => s === 'saving') ? 'saving' : 'saved');\n });\n }\n };\n client.spaces.subscribe((spaces) => {\n install(spaces);\n });\n install(client.spaces.get());\n\n return () => {\n for (const unsubscribe of Object.values(CleanupFns)) {\n unsubscribe();\n }\n };\n};\n\n/** The part of a {@link Space} the save tracker reads. */\nexport type SaveTrackedSpace = {\n readonly db: Pick<EchoDatabase, 'saveStateChanged' | 'flush'>;\n waitUntilReady(): Promise<unknown>;\n};\n\nexport const createSpaceSaveTracker = (space: SaveTrackedSpace, cb: (state: 'saved' | 'saving') => void): CleanupFn => {\n const ctx = new Context();\n\n void space\n .waitUntilReady()\n .then(() => {\n if (ctx.disposed) {\n return;\n }\n\n let hasUnsavedChanges = false;\n let lastFlushPromise: Promise<void> | undefined;\n space.db.saveStateChanged.on(ctx, ({ unsavedDocuments }) => {\n hasUnsavedChanges = unsavedDocuments.length > 0;\n });\n space.db.saveStateChanged.debounce(500).on(ctx, () => {\n if (hasUnsavedChanges) {\n lastFlushPromise = undefined;\n cb('saving');\n } else {\n const flushPromise = space.db.flush();\n lastFlushPromise = flushPromise;\n void flushPromise.then(\n () => {\n if (lastFlushPromise === flushPromise) {\n cb('saved');\n }\n },\n (error: unknown) => {\n // The failed write is logged where it failed; here it only means the space is not saved.\n log.verbose('space flush failed', { error });\n if (lastFlushPromise === flushPromise) {\n cb('saving');\n }\n },\n );\n }\n });\n })\n .catch((err) => log.catch(err));\n\n return () => {\n void ctx.dispose();\n };\n};\n","//\n// Copyright 2024 DXOS.org\n//\n\nexport type Status =\n | 'saving-locally'\n | 'downloading'\n | 'uploading'\n | 'stalled'\n | 'disconnected'\n | 'offline-persisted'\n | 'remote-synced';\n\nexport type StatusInput = {\n offline: boolean;\n saved: boolean;\n /** Replication is outstanding but making no progress. */\n stalled: boolean;\n needsToUpload: boolean;\n needsToDownload: boolean;\n};\n\nexport const getStatus = ({ offline, saved, stalled, needsToUpload, needsToDownload }: StatusInput): Status => {\n if (!saved) {\n return 'saving-locally';\n } else if (offline) {\n // Offline with nothing outstanding is durable; offline with pending work is not.\n return needsToUpload || needsToDownload ? 'disconnected' : 'offline-persisted';\n } else if (stalled) {\n return 'stalled';\n } else if (needsToDownload) {\n return 'downloading';\n } else if (needsToUpload) {\n return 'uploading';\n } else {\n return 'remote-synced';\n }\n};\n\nexport const getIcon = (status: Status) => {\n switch (status) {\n case 'saving-locally':\n return 'ph--download--regular';\n case 'downloading':\n return 'ph--cloud-arrow-down--regular';\n case 'uploading':\n return 'ph--cloud-arrow-up--regular';\n case 'stalled':\n return 'ph--cloud-warning--regular';\n case 'disconnected':\n return 'ph--cloud-slash--regular';\n case 'offline-persisted':\n return 'ph--check-circle--regular';\n case 'remote-synced':\n return 'ph--cloud-check--regular';\n }\n};\n"],"mappings":"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"}
@@ -85,6 +85,8 @@ var SPACE_STATS_QUERY = Query.select(Filter.everything()).aggregate({
85
85
  type: Aggregate.type(),
86
86
  count: Aggregate.count()
87
87
  });
88
+ /** Type URIs with at least one live object, in the form `Type.getURI` returns. */
89
+ var typeUrisWithObjects = (rows) => new Set(rows.flatMap((row) => row.type !== null && row.count > 0 ? [row.type] : []));
88
90
  /** The versionless typename a stored type URI names, or the URI itself when it is not a DXN. */
89
91
  var typenameOf = (type) => {
90
92
  const dxn = DXN$1.tryMake(type);
@@ -105,6 +107,6 @@ var countObjects = (rows, typename) => rows.filter((row) => typename === void 0
105
107
  /** Distinct versionless typenames across `rows`; two schema versions of one type count once. */
106
108
  var countTypenames = (rows) => new Set(rows.flatMap((row) => row.type !== null ? [typenameOf(row.type)] : [])).size;
107
109
  //#endregion
108
- export { FAVORITE_TAG, SPACE_STATS_QUERY, countObjects, countTypenames, findFavoriteTag, toMetrics, toShortcuts, toSlots, toSpaceStats, typenameOf };
110
+ export { FAVORITE_TAG, SPACE_STATS_QUERY, countObjects, countTypenames, findFavoriteTag, toMetrics, toShortcuts, toSlots, toSpaceStats, typeUrisWithObjects, typenameOf };
109
111
 
110
112
  //# sourceMappingURL=dashboard.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"dashboard.mjs","names":[],"sources":["../../src/dashboard/slots.ts","../../src/dashboard/metrics.ts","../../src/dashboard/shortcuts.ts","../../src/dashboard/stats.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n/**\n * Pads or truncates a list to a device's slot count, so a renderer can index slots directly and a\n * cleared slot is explicit rather than a missing index.\n */\nexport const toSlots = <T>(items: readonly T[], slots: number): (T | null)[] =>\n Array.from({ length: slots }, (_, index) => items[index] ?? null);\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { type Progress } from '@dxos/progress';\n\nimport { toSlots } from './slots.ts';\nimport { type MetricSpec, type SpaceStats } from './types.ts';\n\nconst ACTIVE: readonly Progress.TaskStatus[] = ['pending', 'running'];\n\nconst STAT_LABELS: readonly [keyof SpaceStats, string][] = [\n ['objects', 'Objects'],\n ['feeds', 'Feeds'],\n ['types', 'Types'],\n ['plugins', 'Plugins'],\n];\n\nconst toProgressMetric = (task: Progress.TaskProgress): MetricSpec => ({\n kind: 'progress',\n title: task.label ?? task.name,\n // Clamped both ways: a task reporting a negative current would emit a ratio outside [0, 1].\n ratio: task.total && task.total > 0 ? Math.max(0, Math.min(1, task.current / task.total)) : undefined,\n detail: task.total ? `${task.current}/${task.total}` : String(task.current),\n});\n\n/**\n * Projects the progress registry onto a fixed number of slots, falling back to space stats when\n * nothing is running — the display is never blank, and an active task always wins over the counts.\n *\n * Pure, so both modes and the transition between them are testable without a device.\n */\nexport const toMetrics = (\n tasks: readonly Progress.TaskProgress[],\n stats: SpaceStats,\n slots: number,\n): (MetricSpec | null)[] => {\n const active = tasks.filter((task) => ACTIVE.includes(task.status));\n const specs: MetricSpec[] = active.length\n ? active.slice(0, slots).map(toProgressMetric)\n : STAT_LABELS.map(([key, title]) => ({ kind: 'stat', title, value: String(stats[key]) }));\n\n return toSlots(specs, slots);\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport * as GraphPath from '@dxos/app-toolkit/GraphPath';\nimport { Obj, type Tag } from '@dxos/echo';\n\nimport { toSlots } from './slots.ts';\nimport { type Shortcut } from './types.ts';\n\n/**\n * Label of the canonical tag marking an object as a favorite. ECHO tags are first-class\n * (`Obj.getMeta(obj).tags`, `Obj.addTag`, `Filter.tag`), so no new annotation is introduced.\n */\nexport const FAVORITE_TAG = 'favorite';\n\nconst DEFAULT_ICON = 'ph--cube--regular';\n\n/**\n * Projects favorites onto a fixed number of slots.\n *\n * Slots are assigned from a stable sort rather than persisted positions, so adding a favorite can\n * reshuffle them under the user's fingers; a persisted layout object fixes that later. Kept pure so\n * the mapping is testable without a device or a live query.\n */\nexport const toShortcuts = (objects: readonly Obj.Unknown[], slots: number): (Shortcut | null)[] => {\n const specs = objects\n .flatMap((object): Shortcut[] => {\n // An object with no database or no type URI has no navigation path, so a slot for it could not\n // open anything; drop it rather than surfacing a dead slot.\n let target: string;\n try {\n target = GraphPath.getObjectPathFromObject(object);\n } catch {\n return [];\n }\n const icon = Obj.getIcon(object);\n return [\n {\n target,\n label: Obj.getLabel(object, { fallback: 'typename' }) ?? 'Object',\n icon: icon?.icon ?? DEFAULT_ICON,\n hue: icon?.hue,\n },\n ];\n })\n .sort((a, b) => a.label.localeCompare(b.label) || a.target.localeCompare(b.target));\n\n return toSlots(specs, slots);\n};\n\n/**\n * The space's `favorite` tag, if it exists. `Filter.tag` matches a tag's URI rather than its label,\n * so the tag object has to be resolved before favorites can be queried. Keyless tags only: a\n * provider tag that happens to be labelled \"favorite\" is somebody else's tag.\n */\nexport const findFavoriteTag = (tags: readonly Tag.Tag[]): Tag.Tag | undefined =>\n tags.find((tag) => tag.label.toLowerCase() === FAVORITE_TAG && (Obj.getMeta(tag).keys ?? []).length === 0);\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { Aggregate, Feed, Filter, Query, Type } from '@dxos/echo';\nimport { DXN } from '@dxos/keys';\n\nimport { type SpaceStats } from './types.ts';\n\nconst FEED_TYPENAME = Type.getTypename(Feed.Feed);\n\n/** How many live objects carry a given type URI. */\nexport type TypeCount = {\n readonly type: string | null;\n readonly count: number;\n};\n\n/**\n * Counts every live object in a space by type. The host answers from index rows and sends one row\n * per type, so no document is loaded.\n */\nexport const SPACE_STATS_QUERY = Query.select(Filter.everything()).aggregate({\n type: Aggregate.type(),\n count: Aggregate.count(),\n});\n\n/** The versionless typename a stored type URI names, or the URI itself when it is not a DXN. */\nexport const typenameOf = (type: string): string => {\n const dxn = DXN.tryMake(type);\n return dxn !== undefined ? DXN.getName(dxn) : type;\n};\n\n/**\n * Counts shown while nothing is running. Derived from a single \"everything\" query rather than one\n * query per statistic — a peripheral display is a glance, not a report.\n */\nexport const toSpaceStats = (rows: readonly TypeCount[], plugins: number): SpaceStats => ({\n objects: countObjects(rows),\n feeds: countObjects(rows, FEED_TYPENAME),\n types: countTypenames(rows),\n plugins,\n});\n\n/** Live objects across `rows`, or only those of one versionless typename. */\nexport const countObjects = (rows: readonly TypeCount[], typename?: string): number =>\n rows\n .filter((row) => typename === undefined || (row.type !== null && typenameOf(row.type) === typename))\n .reduce((total, row) => total + row.count, 0);\n\n/** Distinct versionless typenames across `rows`; two schema versions of one type count once. */\nexport const countTypenames = (rows: readonly TypeCount[]): number =>\n new Set(rows.flatMap((row) => (row.type !== null ? [typenameOf(row.type)] : []))).size;\n"],"mappings":";;;;;;;;AAQA,IAAa,WAAc,OAAqB,UAC9C,MAAM,KAAK,EAAE,QAAQ,MAAM,IAAI,GAAG,UAAU,MAAM,UAAU,IAAI;;;ACAlE,IAAM,SAAyC,CAAC,WAAW,SAAS;AAEpE,IAAM,cAAqD;CACzD,CAAC,WAAW,SAAS;CACrB,CAAC,SAAS,OAAO;CACjB,CAAC,SAAS,OAAO;CACjB,CAAC,WAAW,SAAS;AACvB;AAEA,IAAM,oBAAoB,UAA6C;CACrE,MAAM;CACN,OAAO,KAAK,SAAS,KAAK;CAE1B,OAAO,KAAK,SAAS,KAAK,QAAQ,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,UAAU,KAAK,KAAK,CAAC,IAAI,KAAA;CAC5F,QAAQ,KAAK,QAAQ,GAAG,KAAK,QAAQ,GAAG,KAAK,UAAU,OAAO,KAAK,OAAO;AAC5E;;;;;;;AAQA,IAAa,aACX,OACA,OACA,UAC0B;CAC1B,MAAM,SAAS,MAAM,QAAQ,SAAS,OAAO,SAAS,KAAK,MAAM,CAAC;CAKlE,OAAO,QAJqB,OAAO,SAC/B,OAAO,MAAM,GAAG,KAAK,CAAC,CAAC,IAAI,gBAAgB,IAC3C,YAAY,KAAK,CAAC,KAAK,YAAY;EAAE,MAAM;EAAQ;EAAO,OAAO,OAAO,MAAM,IAAI;CAAE,EAAE,GAEpE,KAAK;AAC7B;;;;;;;AC7BA,IAAa,eAAe;AAE5B,IAAM,eAAe;;;;;;;;AASrB,IAAa,eAAe,SAAiC,UAAuC;CAuBlG,OAAO,QAtBO,QACX,SAAS,WAAuB;EAG/B,IAAI;EACJ,IAAI;GACF,SAAS,UAAU,wBAAwB,MAAM;EACnD,QAAQ;GACN,OAAO,CAAC;EACV;EACA,MAAM,OAAO,IAAI,QAAQ,MAAM;EAC/B,OAAO,CACL;GACE;GACA,OAAO,IAAI,SAAS,QAAQ,EAAE,UAAU,WAAW,CAAC,KAAK;GACzD,MAAM,MAAM,QAAQ;GACpB,KAAK,MAAM;EACb,CACF;CACF,CAAC,CAAA,CACA,MAAM,GAAG,MAAM,EAAE,MAAM,cAAc,EAAE,KAAK,KAAK,EAAE,OAAO,cAAc,EAAE,MAAM,CAEpE,GAAO,KAAK;AAC7B;;;;;;AAOA,IAAa,mBAAmB,SAC9B,KAAK,MAAM,QAAQ,IAAI,MAAM,YAAY,MAAA,eAAuB,IAAI,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,EAAA,CAAG,WAAW,CAAC;;;AChD3G,IAAM,gBAAgB,KAAK,YAAY,KAAK,IAAI;;;;;AAYhD,IAAa,oBAAoB,MAAM,OAAO,OAAO,WAAW,CAAC,CAAC,CAAC,UAAU;CAC3E,MAAM,UAAU,KAAK;CACrB,OAAO,UAAU,MAAM;AACzB,CAAC;;AAGD,IAAa,cAAc,SAAyB;CAClD,MAAM,MAAM,MAAI,QAAQ,IAAI;CAC5B,OAAO,QAAQ,KAAA,IAAY,MAAI,QAAQ,GAAG,IAAI;AAChD;;;;;AAMA,IAAa,gBAAgB,MAA4B,aAAiC;CACxF,SAAS,aAAa,IAAI;CAC1B,OAAO,aAAa,MAAM,aAAa;CACvC,OAAO,eAAe,IAAI;CAC1B;AACF;;AAGA,IAAa,gBAAgB,MAA4B,aACvD,KACG,QAAQ,QAAQ,aAAa,KAAA,KAAc,IAAI,SAAS,QAAQ,WAAW,IAAI,IAAI,MAAM,QAAS,CAAA,CAClG,QAAQ,OAAO,QAAQ,QAAQ,IAAI,OAAO,CAAC;;AAGhD,IAAa,kBAAkB,SAC7B,IAAI,IAAI,KAAK,SAAS,QAAS,IAAI,SAAS,OAAO,CAAC,WAAW,IAAI,IAAI,CAAC,IAAI,CAAC,CAAE,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"dashboard.mjs","names":[],"sources":["../../src/dashboard/slots.ts","../../src/dashboard/metrics.ts","../../src/dashboard/shortcuts.ts","../../src/dashboard/stats.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n/**\n * Pads or truncates a list to a device's slot count, so a renderer can index slots directly and a\n * cleared slot is explicit rather than a missing index.\n */\nexport const toSlots = <T>(items: readonly T[], slots: number): (T | null)[] =>\n Array.from({ length: slots }, (_, index) => items[index] ?? null);\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { type Progress } from '@dxos/progress';\n\nimport { toSlots } from './slots.ts';\nimport { type MetricSpec, type SpaceStats } from './types.ts';\n\nconst ACTIVE: readonly Progress.TaskStatus[] = ['pending', 'running'];\n\nconst STAT_LABELS: readonly [keyof SpaceStats, string][] = [\n ['objects', 'Objects'],\n ['feeds', 'Feeds'],\n ['types', 'Types'],\n ['plugins', 'Plugins'],\n];\n\nconst toProgressMetric = (task: Progress.TaskProgress): MetricSpec => ({\n kind: 'progress',\n title: task.label ?? task.name,\n // Clamped both ways: a task reporting a negative current would emit a ratio outside [0, 1].\n ratio: task.total && task.total > 0 ? Math.max(0, Math.min(1, task.current / task.total)) : undefined,\n detail: task.total ? `${task.current}/${task.total}` : String(task.current),\n});\n\n/**\n * Projects the progress registry onto a fixed number of slots, falling back to space stats when\n * nothing is running — the display is never blank, and an active task always wins over the counts.\n *\n * Pure, so both modes and the transition between them are testable without a device.\n */\nexport const toMetrics = (\n tasks: readonly Progress.TaskProgress[],\n stats: SpaceStats,\n slots: number,\n): (MetricSpec | null)[] => {\n const active = tasks.filter((task) => ACTIVE.includes(task.status));\n const specs: MetricSpec[] = active.length\n ? active.slice(0, slots).map(toProgressMetric)\n : STAT_LABELS.map(([key, title]) => ({ kind: 'stat', title, value: String(stats[key]) }));\n\n return toSlots(specs, slots);\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport * as GraphPath from '@dxos/app-toolkit/GraphPath';\nimport { Obj, type Tag } from '@dxos/echo';\n\nimport { toSlots } from './slots.ts';\nimport { type Shortcut } from './types.ts';\n\n/**\n * Label of the canonical tag marking an object as a favorite. ECHO tags are first-class\n * (`Obj.getMeta(obj).tags`, `Obj.addTag`, `Filter.tag`), so no new annotation is introduced.\n */\nexport const FAVORITE_TAG = 'favorite';\n\nconst DEFAULT_ICON = 'ph--cube--regular';\n\n/**\n * Projects favorites onto a fixed number of slots.\n *\n * Slots are assigned from a stable sort rather than persisted positions, so adding a favorite can\n * reshuffle them under the user's fingers; a persisted layout object fixes that later. Kept pure so\n * the mapping is testable without a device or a live query.\n */\nexport const toShortcuts = (objects: readonly Obj.Unknown[], slots: number): (Shortcut | null)[] => {\n const specs = objects\n .flatMap((object): Shortcut[] => {\n // An object with no database or no type URI has no navigation path, so a slot for it could not\n // open anything; drop it rather than surfacing a dead slot.\n let target: string;\n try {\n target = GraphPath.getObjectPathFromObject(object);\n } catch {\n return [];\n }\n const icon = Obj.getIcon(object);\n return [\n {\n target,\n label: Obj.getLabel(object, { fallback: 'typename' }) ?? 'Object',\n icon: icon?.icon ?? DEFAULT_ICON,\n hue: icon?.hue,\n },\n ];\n })\n .sort((a, b) => a.label.localeCompare(b.label) || a.target.localeCompare(b.target));\n\n return toSlots(specs, slots);\n};\n\n/**\n * The space's `favorite` tag, if it exists. `Filter.tag` matches a tag's URI rather than its label,\n * so the tag object has to be resolved before favorites can be queried. Keyless tags only: a\n * provider tag that happens to be labelled \"favorite\" is somebody else's tag.\n */\nexport const findFavoriteTag = (tags: readonly Tag.Tag[]): Tag.Tag | undefined =>\n tags.find((tag) => tag.label.toLowerCase() === FAVORITE_TAG && (Obj.getMeta(tag).keys ?? []).length === 0);\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { Aggregate, Feed, Filter, Query, Type } from '@dxos/echo';\nimport { DXN } from '@dxos/keys';\n\nimport { type SpaceStats } from './types.ts';\n\nconst FEED_TYPENAME = Type.getTypename(Feed.Feed);\n\n/** How many live objects carry a given type URI. */\nexport type TypeCount = {\n readonly type: string | null;\n readonly count: number;\n};\n\n/**\n * Counts every live object in a space by type. The host answers from index rows and sends one row\n * per type, so no document is loaded.\n */\nexport const SPACE_STATS_QUERY = Query.select(Filter.everything()).aggregate({\n type: Aggregate.type(),\n count: Aggregate.count(),\n});\n\n/** Type URIs with at least one live object, in the form `Type.getURI` returns. */\nexport const typeUrisWithObjects = (rows: readonly TypeCount[]): Set<string> =>\n new Set(rows.flatMap((row) => (row.type !== null && row.count > 0 ? [row.type] : [])));\n\n/** The versionless typename a stored type URI names, or the URI itself when it is not a DXN. */\nexport const typenameOf = (type: string): string => {\n const dxn = DXN.tryMake(type);\n return dxn !== undefined ? DXN.getName(dxn) : type;\n};\n\n/**\n * Counts shown while nothing is running. Derived from a single \"everything\" query rather than one\n * query per statistic — a peripheral display is a glance, not a report.\n */\nexport const toSpaceStats = (rows: readonly TypeCount[], plugins: number): SpaceStats => ({\n objects: countObjects(rows),\n feeds: countObjects(rows, FEED_TYPENAME),\n types: countTypenames(rows),\n plugins,\n});\n\n/** Live objects across `rows`, or only those of one versionless typename. */\nexport const countObjects = (rows: readonly TypeCount[], typename?: string): number =>\n rows\n .filter((row) => typename === undefined || (row.type !== null && typenameOf(row.type) === typename))\n .reduce((total, row) => total + row.count, 0);\n\n/** Distinct versionless typenames across `rows`; two schema versions of one type count once. */\nexport const countTypenames = (rows: readonly TypeCount[]): number =>\n new Set(rows.flatMap((row) => (row.type !== null ? [typenameOf(row.type)] : []))).size;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../../../../../src/capabilities/app-graph-builder/extensions/database.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAKxC,OAAO,KAAK,UAAU,MAAM,gCAAgC,CAAC;AAE7D,OAAO,KAAK,MAAM,MAAM,4BAA4B,CAAC;AACrD,OAAO,KAAK,eAAe,MAAM,iCAAiC,CAAC;AACnE,OAAO,KAAK,YAAY,MAAM,8BAA8B,CAAC;AAkC7D,qGAAqG;AACrG,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kDAuOnC,CAAC"}
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../../../../../src/capabilities/app-graph-builder/extensions/database.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAKxC,OAAO,KAAK,UAAU,MAAM,gCAAgC,CAAC;AAE7D,OAAO,KAAK,MAAM,MAAM,4BAA4B,CAAC;AACrD,OAAO,KAAK,eAAe,MAAM,iCAAiC,CAAC;AACnE,OAAO,KAAK,YAAY,MAAM,8BAA8B,CAAC;AAmC7D,qGAAqG;AACrG,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kDAwOnC,CAAC"}
@@ -48,6 +48,8 @@ export type ViewIndex = {
48
48
  getViewsForTypeUri: (typeUri: string) => Obj.Any[];
49
49
  /** True when the schema has `ViewAnnotation` and that path resolves non-null on `object`; false otherwise (no annotation, or null/undefined path — e.g. Kanban with unset `view`). */
50
50
  isView: (object: Obj.Any) => boolean;
51
+ /** View-annotated type URIs with at least one instance that is not a view. */
52
+ typeUrisWithNonViewObjects: Set<string>;
51
53
  };
52
54
  /**
53
55
  * Builds an index of view objects by scanning view-annotated schemas and resolving the target
@@ -1 +1 @@
1
- {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../../../../src/capabilities/app-graph-builder/extensions/shared.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,IAAI,MAAM,wBAAwB,CAAC;AAEpD,OAAO,KAAK,KAAK,YAAY,MAAM,8BAA8B,CAAC;AAClE,OAAO,EAAE,KAAK,KAAK,EAAE,UAAU,EAAW,MAAM,mBAAmB,CAAC;AACpE,OAAO,KAAK,KAAK,SAAS,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAsB,GAAG,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAE3D,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAIpD,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAQzD,eAAO,MAAM,qBAAqB,QAAiC,CAAC;AACpE,eAAO,MAAM,wBAAwB,QAAoC,CAAC;AAC1E,mHAAmH;AACnH,eAAO,MAAM,gBAAgB,QAAsC,CAAC;AAMpE,+CAA+C;AAC/C,eAAO,MAAM,OAAO,EAAE;IAAE,EAAE,EAAE,MAAM,CAAA;CAA6B,CAAC;AAMhE,eAAO,MAAM,cAAc,WAAY,QAAQ,YAAgE,CAAC;AAMhH,eAAO,MAAM,iBAAiB;WAGnB,UAAU;yBACI,OAAO;eACjB,OAAO;aACT,YAAY,CAAC,OAAO,CAAC,YAAY,CAAC,UAAU,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,EAAE;EAE5E,CAAC;AACJ,eAAO,MAAM,mBAAmB,0BAA+B,MAAM,EAAE,KAAK,IAAI,CAAG,CAAC;AAMpF,eAAO,MAAM,uBAAuB,EAAE,KAA2D,CAAC;AAClG,eAAO,MAAM,wBAAwB,EAAE,KAA6C,CAAC;AACrF,eAAO,MAAM,oBAAoB,EAAE,KAAyC,CAAC;AAC7E,eAAO,MAAM,eAAe,EAAE,KAAoC,CAAC;AACnE,eAAO,MAAM,iCAAiC,EAAE,KAAsD,CAAC;AACvG,eAAO,MAAM,4BAA4B,EAAE,KAAiD,CAAC;AAC7F,eAAO,MAAM,mBAAmB,EAAE,KAAwC,CAAC;AAC3E,eAAO,MAAM,mBAAmB,EAAE,KAAwC,CAAC;AAC3E,eAAO,MAAM,cAAc,EAAE,KAAmC,CAAC;AACjE,eAAO,MAAM,mBAAmB,EAAE,KAAwC,CAAC;AAC3E,eAAO,MAAM,4BAA4B,EAAE,KAAiD,CAAC;AAC7F,eAAO,MAAM,kBAAkB,EAAE,KAAuC,CAAC;AACzE,eAAO,MAAM,mBAAmB,EAAE,KAAwC,CAAC;AAC3E,eAAO,MAAM,oBAAoB,EAAE,KAAyC,CAAC;AAC7E,eAAO,MAAM,wBAAwB,EAAE,KAA6C,CAAC;AACrF,eAAO,MAAM,sBAAsB,EAAE,KAA2C,CAAC;AAMjF,eAAO,MAAM,qBAAqB,UAAW,KAAK,YAQjD,CAAC;AAMF;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,4EAA4E;IAC5E,iBAAiB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC/B,kDAAkD;IAClD,kBAAkB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,GAAG,CAAC,GAAG,EAAE,CAAC;IACnD,sLAAsL;IACtL,MAAM,EAAE,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,KAAK,OAAO,CAAC;CACtC,CAAC;AAEF;;;;GAIG;AAEH,eAAO,MAAM,cAAc,QAAS,IAAI,CAAC,WAAW,SAAS,KAAK,WAAW,IAAI,CAAC,SAAS,EAAE,KAAG,SAsD/F,CAAC"}
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../../../../src/capabilities/app-graph-builder/extensions/shared.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,KAAK,IAAI,MAAM,wBAAwB,CAAC;AAEpD,OAAO,KAAK,KAAK,YAAY,MAAM,8BAA8B,CAAC;AAClE,OAAO,EAAE,KAAK,KAAK,EAAE,UAAU,EAAW,MAAM,mBAAmB,CAAC;AACpE,OAAO,KAAK,KAAK,SAAS,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAsB,GAAG,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAE3D,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAIpD,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAQzD,eAAO,MAAM,qBAAqB,QAAiC,CAAC;AACpE,eAAO,MAAM,wBAAwB,QAAoC,CAAC;AAC1E,mHAAmH;AACnH,eAAO,MAAM,gBAAgB,QAAsC,CAAC;AAMpE,+CAA+C;AAC/C,eAAO,MAAM,OAAO,EAAE;IAAE,EAAE,EAAE,MAAM,CAAA;CAA6B,CAAC;AAMhE,eAAO,MAAM,cAAc,WAAY,QAAQ,YAAgE,CAAC;AAMhH,eAAO,MAAM,iBAAiB;WAGnB,UAAU;yBACI,OAAO;eACjB,OAAO;aACT,YAAY,CAAC,OAAO,CAAC,YAAY,CAAC,UAAU,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,EAAE;EAE5E,CAAC;AACJ,eAAO,MAAM,mBAAmB,0BAA+B,MAAM,EAAE,KAAK,IAAI,CAAG,CAAC;AAMpF,eAAO,MAAM,uBAAuB,EAAE,KAA2D,CAAC;AAClG,eAAO,MAAM,wBAAwB,EAAE,KAA6C,CAAC;AACrF,eAAO,MAAM,oBAAoB,EAAE,KAAyC,CAAC;AAC7E,eAAO,MAAM,eAAe,EAAE,KAAoC,CAAC;AACnE,eAAO,MAAM,iCAAiC,EAAE,KAAsD,CAAC;AACvG,eAAO,MAAM,4BAA4B,EAAE,KAAiD,CAAC;AAC7F,eAAO,MAAM,mBAAmB,EAAE,KAAwC,CAAC;AAC3E,eAAO,MAAM,mBAAmB,EAAE,KAAwC,CAAC;AAC3E,eAAO,MAAM,cAAc,EAAE,KAAmC,CAAC;AACjE,eAAO,MAAM,mBAAmB,EAAE,KAAwC,CAAC;AAC3E,eAAO,MAAM,4BAA4B,EAAE,KAAiD,CAAC;AAC7F,eAAO,MAAM,kBAAkB,EAAE,KAAuC,CAAC;AACzE,eAAO,MAAM,mBAAmB,EAAE,KAAwC,CAAC;AAC3E,eAAO,MAAM,oBAAoB,EAAE,KAAyC,CAAC;AAC7E,eAAO,MAAM,wBAAwB,EAAE,KAA6C,CAAC;AACrF,eAAO,MAAM,sBAAsB,EAAE,KAA2C,CAAC;AAMjF,eAAO,MAAM,qBAAqB,UAAW,KAAK,YAQjD,CAAC;AAMF;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,4EAA4E;IAC5E,iBAAiB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC/B,kDAAkD;IAClD,kBAAkB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,GAAG,CAAC,GAAG,EAAE,CAAC;IACnD,sLAAsL;IACtL,MAAM,EAAE,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,KAAK,OAAO,CAAC;IACrC,8EAA8E;IAC9E,0BAA0B,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACzC,CAAC;AAEF;;;;GAIG;AAEH,eAAO,MAAM,cAAc,QAAS,IAAI,CAAC,WAAW,SAAS,KAAK,WAAW,IAAI,CAAC,SAAS,EAAE,KAAG,SA0D/F,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"MembersContainer.d.ts","sourceRoot":"","sources":["../../../../../src/containers/MembersContainer/MembersContainer.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAgE,MAAM,OAAO,CAAC;AAKrF,OAAO,KAAK,UAAU,MAAM,8BAA8B,CAAC;AAkD3D,MAAM,MAAM,qBAAqB,GAAG,UAAU,CAAC,iBAAiB,CAAC;IAC/D,mBAAmB,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,MAAM,CAAC;CACzD,CAAC,CAAC;yCAE8B,EAAE,KAAK,EAAE,mBAAmB,EAAE,EAAE,qBAAqB"}
1
+ {"version":3,"file":"MembersContainer.d.ts","sourceRoot":"","sources":["../../../../../src/containers/MembersContainer/MembersContainer.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAgE,MAAM,OAAO,CAAC;AAKrF,OAAO,KAAK,UAAU,MAAM,8BAA8B,CAAC;AAmD3D,MAAM,MAAM,qBAAqB,GAAG,UAAU,CAAC,iBAAiB,CAAC;IAC/D,mBAAmB,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,MAAM,CAAC;CACzD,CAAC,CAAC;yCAE8B,EAAE,KAAK,EAAE,mBAAmB,EAAE,EAAE,qBAAqB"}
@@ -1 +1 @@
1
- {"version":3,"file":"RecordArticle.d.ts","sourceRoot":"","sources":["../../../../../src/containers/RecordArticle/RecordArticle.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAsB,MAAM,OAAO,CAAC;AAG3C,OAAO,KAAK,UAAU,MAAM,8BAA8B,CAAC;sCA6B7B,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,UAAU,CAAC,kBAAkB"}
1
+ {"version":3,"file":"RecordArticle.d.ts","sourceRoot":"","sources":["../../../../../src/containers/RecordArticle/RecordArticle.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAsB,MAAM,OAAO,CAAC;AAG3C,OAAO,KAAK,UAAU,MAAM,8BAA8B,CAAC;sCA4B7B,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,EAAE,UAAU,CAAC,kBAAkB"}
@@ -1 +1 @@
1
- {"version":3,"file":"SpaceHomeArticle.stories.d.ts","sourceRoot":"","sources":["../../../../../src/containers/SpaceHomeArticle/SpaceHomeArticle.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAa,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGjE,OAAO,KAAK,MAAM,OAAO,CAAC;AAsC1B,QAAA,MAAM,YAAY,yBAQjB,CAAC;AAEF,QAAA,MAAM,IAAI;;;;;QAwDN,MAAM;;CAE2B,CAAC;eAEvB,IAAI;AAEnB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,OAAO,EAAE,KAAU,CAAC"}
1
+ {"version":3,"file":"SpaceHomeArticle.stories.d.ts","sourceRoot":"","sources":["../../../../../src/containers/SpaceHomeArticle/SpaceHomeArticle.stories.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAa,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGjE,OAAO,KAAK,MAAM,OAAO,CAAC;AA2C1B,QAAA,MAAM,YAAY,yBAQjB,CAAC;AAEF,QAAA,MAAM,IAAI;;;;;QAwDN,MAAM;;CAE2B,CAAC;eAEvB,IAAI;AAEnB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,OAAO,EAAE,KAAU,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"SpaceHomeRecent.d.ts","sourceRoot":"","sources":["../../../../../src/containers/SpaceHomeRecent/SpaceHomeRecent.tsx"],"names":[],"mappings":"AAIA,OAAO,KAA+B,MAAM,OAAO,CAAC;AAUpD,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAcrD,KAAK,gBAAgB,GAAG;IACtB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB,CAAC;wCAQ8B,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,gBAAgB"}
1
+ {"version":3,"file":"SpaceHomeRecent.d.ts","sourceRoot":"","sources":["../../../../../src/containers/SpaceHomeRecent/SpaceHomeRecent.tsx"],"names":[],"mappings":"AAKA,OAAO,KAA+B,MAAM,OAAO,CAAC;AAUpD,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAerD,KAAK,gBAAgB,GAAG;IACtB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB,CAAC;wCAE8B,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,gBAAgB"}
@@ -14,6 +14,8 @@ export declare const SPACE_STATS_QUERY: Query.Query<{
14
14
  readonly type: string | null;
15
15
  readonly count: number;
16
16
  }>;
17
+ /** Type URIs with at least one live object, in the form `Type.getURI` returns. */
18
+ export declare const typeUrisWithObjects: (rows: readonly TypeCount[]) => Set<string>;
17
19
  /** The versionless typename a stored type URI names, or the URI itself when it is not a DXN. */
18
20
  export declare const typenameOf: (type: string) => string;
19
21
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../../../src/dashboard/stats.ts"],"names":[],"mappings":"AAIA,OAAO,EAA2B,KAAK,EAAQ,MAAM,YAAY,CAAC;AAGlE,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAI7C,oDAAoD;AACpD,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iBAAiB;;;;EAG5B,CAAC;AAEH,gGAAgG;AAChG,eAAO,MAAM,UAAU,SAAU,MAAM,KAAG,MAGzC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,YAAY,SAAU,SAAS,SAAS,EAAE,WAAW,MAAM,KAAG,UAKzE,CAAC;AAEH,6EAA6E;AAC7E,eAAO,MAAM,YAAY,SAAU,SAAS,SAAS,EAAE,aAAa,MAAM,KAAG,MAG5B,CAAC;AAElD,gGAAgG;AAChG,eAAO,MAAM,cAAc,SAAU,SAAS,SAAS,EAAE,KAAG,MAC4B,CAAC"}
1
+ {"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../../../src/dashboard/stats.ts"],"names":[],"mappings":"AAIA,OAAO,EAA2B,KAAK,EAAQ,MAAM,YAAY,CAAC;AAGlE,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAI7C,oDAAoD;AACpD,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iBAAiB;;;;EAG5B,CAAC;AAEH,kFAAkF;AAClF,eAAO,MAAM,mBAAmB,SAAU,SAAS,SAAS,EAAE,KAAG,GAAG,CAAC,MAAM,CACa,CAAC;AAEzF,gGAAgG;AAChG,eAAO,MAAM,UAAU,SAAU,MAAM,KAAG,MAGzC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,YAAY,SAAU,SAAS,SAAS,EAAE,WAAW,MAAM,KAAG,UAKzE,CAAC;AAEH,6EAA6E;AAC7E,eAAO,MAAM,YAAY,SAAU,SAAS,SAAS,EAAE,aAAa,MAAM,KAAG,MAG5B,CAAC;AAElD,gGAAgG;AAChG,eAAO,MAAM,cAAc,SAAU,SAAS,SAAS,EAAE,KAAG,MAC4B,CAAC"}