@notis_ai/cli 0.2.17 → 0.2.19
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.
- package/README.md +542 -146
- package/bin/check-runtime.js +15 -0
- package/bin/notis.js +2 -0
- package/config/notis_app_design_rules.json +320 -0
- package/config/portal-route-roots.json +45 -0
- package/dist/agent-hooks/notis-agent-hook.mjs +42323 -18258
- package/dist/base-skills/notis-cli/.notis-base-skill.json +1 -0
- package/dist/base-skills/notis-cli/SKILL.md +51 -280
- package/dist/base-skills/notis-cli/references/app-delivery.md +17 -0
- package/dist/base-skills/notis-cli/references/intelligence.md +94 -0
- package/dist/base-skills/notis-cli/references/native-databases.md +21 -0
- package/dist/base-skills/notis-cli/references/tool-examples.md +56 -0
- package/dist/base-skills/notis-cli/references/troubleshooting.md +67 -0
- package/dist/base-skills/notis-query/.notis-base-skill.json +1 -0
- package/dist/base-skills/notis-query/SKILL.md +35 -699
- package/dist/base-skills/notis-query/references/database-discovery.md +59 -0
- package/dist/base-skills/notis-query/references/documents.md +74 -0
- package/dist/base-skills/notis-query/references/query.md +76 -0
- package/dist/base-skills/notis-spaces/.notis-base-skill.json +1 -0
- package/dist/base-skills/notis-spaces/SKILL.md +218 -0
- package/dist/base-skills/notis-spaces/references/architecture.md +116 -0
- package/dist/base-skills/notis-spaces/references/context.md +81 -0
- package/dist/base-skills/notis-spaces/references/design.md +141 -0
- package/dist/base-skills/notis-spaces/references/fixtures.md +29 -0
- package/dist/base-skills/notis-spaces/references/memory.md +58 -0
- package/dist/base-skills/notis-spaces/references/reading.md +73 -0
- package/dist/base-skills/notis-spaces/references/release.md +120 -0
- package/dist/base-skills/notis-spaces/references/reports.md +77 -0
- package/dist/base-skills/notis-spaces/references/sdk.md +273 -0
- package/dist/base-skills/notis-spaces/references/store.md +118 -0
- package/dist/base-skills/notis-spaces/references/troubleshooting.md +28 -0
- package/dist/base-skills/notis-spaces/references/views.md +229 -0
- package/{template/packages → dist}/sdk/package.json +9 -10
- package/dist/sdk/src/agentContext.ts +36 -0
- package/dist/sdk/src/bundleModule.ts +175 -0
- package/dist/sdk/src/components/Dialog.tsx +103 -0
- package/dist/sdk/src/components/DocumentEditor.tsx +113 -0
- package/dist/sdk/src/components/DocumentPage.tsx +184 -0
- package/{template/packages → dist}/sdk/src/components/Markdown.tsx +9 -3
- package/{template/packages → dist}/sdk/src/components/MarkdownEditor.tsx +26 -5
- package/{template/packages → dist}/sdk/src/components/MultiSelectActionBar.tsx +95 -12
- package/{template/packages → dist}/sdk/src/components/MultiSelectCheckbox.tsx +4 -2
- package/dist/sdk/src/components/NotisCommentBoundary.tsx +172 -0
- package/{template/packages → dist}/sdk/src/components/NotisSelectionBoundary.tsx +9 -5
- package/dist/sdk/src/components/RecordComponents.tsx +111 -0
- package/dist/sdk/src/components/Skeleton.tsx +38 -0
- package/dist/sdk/src/config.ts +31 -0
- package/{template/packages → dist}/sdk/src/documents.ts +21 -6
- package/dist/sdk/src/hooks/useAction.ts +28 -0
- package/dist/sdk/src/hooks/useAgentContext.ts +23 -0
- package/{template/packages → dist}/sdk/src/hooks/useBackend.ts +3 -2
- package/dist/sdk/src/hooks/useCloudComputer.ts +94 -0
- package/dist/sdk/src/hooks/useCloudComputerShell.ts +114 -0
- package/{template/packages → dist}/sdk/src/hooks/useCollectionInteractions.ts +157 -147
- package/dist/sdk/src/hooks/useDatabaseSchema.ts +50 -0
- package/{template/packages → dist}/sdk/src/hooks/useDatabaseSubscription.ts +2 -2
- package/dist/sdk/src/hooks/useDocument.ts +51 -0
- package/{template/packages → dist}/sdk/src/hooks/useDocuments.ts +25 -59
- package/{template/packages → dist}/sdk/src/hooks/useHandover.ts +7 -7
- package/dist/sdk/src/hooks/useLongPressSelection.ts +79 -0
- package/dist/sdk/src/hooks/useMarqueeSelection.ts +194 -0
- package/{template/packages → dist}/sdk/src/hooks/useMultiSelect.ts +2 -8
- package/dist/sdk/src/hooks/useNativeDocumentBody.ts +50 -0
- package/dist/sdk/src/hooks/useNativeDocuments.ts +143 -0
- package/{template/packages → dist}/sdk/src/hooks/useNotis.ts +6 -1
- package/dist/sdk/src/hooks/useNotisNavigation.ts +70 -0
- package/dist/sdk/src/hooks/useQuery.ts +81 -0
- package/dist/sdk/src/hooks/useToolQuery.ts +12 -0
- package/dist/sdk/src/hooks/useTopBarSearch.ts +102 -0
- package/dist/sdk/src/hooks/useTopBarTitleLeaf.ts +75 -0
- package/{template/packages → dist}/sdk/src/hooks/useUpsertDocument.ts +1 -1
- package/dist/sdk/src/hooks/useViewParams.ts +195 -0
- package/dist/sdk/src/hooks/useViewerReads.ts +129 -0
- package/{template/packages → dist}/sdk/src/index.ts +64 -1
- package/{template/packages → dist}/sdk/src/interactions/actions.ts +14 -1
- package/{template/packages → dist}/sdk/src/interactions/shortcuts.tsx +129 -23
- package/dist/sdk/src/interactions/visibility.ts +13 -0
- package/{template/packages → dist}/sdk/src/interactions.ts +5 -1
- package/dist/sdk/src/nativeData.ts +39 -0
- package/{template/packages → dist}/sdk/src/provider.tsx +9 -4
- package/dist/sdk/src/queryCache.ts +292 -0
- package/dist/sdk/src/queryErrors.ts +51 -0
- package/dist/sdk/src/runtime.ts +675 -0
- package/dist/sdk/src/runtimeModules.ts +33 -0
- package/dist/sdk/src/space.ts +442 -0
- package/dist/sdk/src/standardViewParams.ts +36 -0
- package/{template/packages → dist}/sdk/src/styles.css +81 -16
- package/dist/sdk/src/tailwind.ts +66 -0
- package/dist/sdk/src/ui/charts/RenderChartData.tsx +25 -0
- package/dist/sdk/src/ui/engine/core.ts +392 -0
- package/dist/sdk/src/ui/engine/dom.ts +183 -0
- package/dist/sdk/src/ui/engine/hooks.ts +195 -0
- package/dist/sdk/src/ui/engine/instance.ts +63 -0
- package/dist/sdk/src/ui/engine/keycapContext.ts +2 -0
- package/dist/sdk/src/ui/engine/registry.ts +262 -0
- package/dist/sdk/src/ui/engine/types.ts +62 -0
- package/dist/sdk/src/ui/icons.tsx +91 -0
- package/dist/sdk/src/ui/util.ts +83 -0
- package/dist/sdk/src/viewPeriod.ts +26 -0
- package/dist/sdk/src/vite.ts +92 -0
- package/dist/sdk/src/words/actions.ts +295 -0
- package/dist/sdk/src/words/clashes.ts +158 -0
- package/dist/sdk/src/words/format.ts +136 -0
- package/dist/sdk/src/words/index.ts +13 -0
- package/dist/sdk/src/words/keymap.ts +72 -0
- package/dist/sdk/src/words/keys.ts +297 -0
- package/dist/sdk/src/words/shell.ts +46 -0
- package/dist/sdk/src/words/types.ts +65 -0
- package/dist/skill-sync/index.js +832 -151
- package/dist/skill-sync/index.js.map +4 -4
- package/dist/skill-sync-worker.mjs +3941 -0
- package/dist/space-harness/host.js +1460 -0
- package/dist/space-sdk/dist/chunk-ZSN57ATH.js +5 -0
- package/dist/space-sdk/dist/index.js +15 -0
- package/dist/space-sdk/dist/ui.js +18 -0
- package/dist/space-sdk/package.json +33 -0
- package/dist/space-sdk/src/agentContext.d.ts +36 -0
- package/dist/space-sdk/src/components/Dialog.d.ts +20 -0
- package/dist/space-sdk/src/components/DocumentEditor.d.ts +46 -0
- package/dist/space-sdk/src/components/DocumentPage.d.ts +53 -0
- package/dist/space-sdk/src/components/Markdown.d.ts +18 -0
- package/dist/space-sdk/src/components/MarkdownEditor.d.ts +11 -0
- package/dist/space-sdk/src/components/MultiSelectActionBar.d.ts +31 -0
- package/dist/space-sdk/src/components/MultiSelectCheckbox.d.ts +14 -0
- package/dist/space-sdk/src/components/MultiSelectDragOverlay.d.ts +8 -0
- package/dist/space-sdk/src/components/NotisCommentBoundary.d.ts +36 -0
- package/dist/space-sdk/src/components/NotisSelectionBoundary.d.ts +16 -0
- package/dist/space-sdk/src/components/RecordComponents.d.ts +64 -0
- package/dist/space-sdk/src/components/ShortcutHints.d.ts +12 -0
- package/dist/space-sdk/src/components/Skeleton.d.ts +23 -0
- package/dist/space-sdk/src/config.ts +31 -0
- package/dist/space-sdk/src/documents.d.ts +59 -0
- package/dist/space-sdk/src/hooks/useAction.d.ts +8 -0
- package/dist/space-sdk/src/hooks/useActiveResource.d.ts +3 -0
- package/dist/space-sdk/src/hooks/useAgentContext.d.ts +7 -0
- package/dist/space-sdk/src/hooks/useBackend.d.ts +23 -0
- package/dist/space-sdk/src/hooks/useCloudComputer.d.ts +45 -0
- package/dist/space-sdk/src/hooks/useCloudComputerShell.d.ts +49 -0
- package/dist/space-sdk/src/hooks/useCollectionInteractions.d.ts +118 -0
- package/dist/space-sdk/src/hooks/useDatabaseSchema.d.ts +14 -0
- package/dist/space-sdk/src/hooks/useDatabaseSubscription.d.ts +26 -0
- package/dist/space-sdk/src/hooks/useDocument.d.ts +22 -0
- package/dist/space-sdk/src/hooks/useDocuments.d.ts +31 -0
- package/dist/space-sdk/src/hooks/useHandover.d.ts +43 -0
- package/dist/space-sdk/src/hooks/useLongPressSelection.d.ts +9 -0
- package/dist/space-sdk/src/hooks/useMarqueeSelection.d.ts +57 -0
- package/dist/space-sdk/src/hooks/useMultiSelect.d.ts +51 -0
- package/dist/space-sdk/src/hooks/useNativeDocumentBody.d.ts +24 -0
- package/dist/space-sdk/src/hooks/useNativeDocuments.d.ts +55 -0
- package/dist/space-sdk/src/hooks/useNotis.d.ts +25 -0
- package/dist/space-sdk/src/hooks/useNotisNavigation.d.ts +33 -0
- package/dist/space-sdk/src/hooks/useQuery.d.ts +22 -0
- package/dist/space-sdk/src/hooks/useTool.d.ts +20 -0
- package/dist/space-sdk/src/hooks/useToolQuery.d.ts +3 -0
- package/dist/space-sdk/src/hooks/useTools.d.ts +15 -0
- package/dist/space-sdk/src/hooks/useTopBarSearch.d.ts +48 -0
- package/dist/space-sdk/src/hooks/useTopBarTitleLeaf.d.ts +24 -0
- package/dist/space-sdk/src/hooks/useUpsertDocument.d.ts +27 -0
- package/dist/space-sdk/src/hooks/useViewParams.d.ts +76 -0
- package/dist/space-sdk/src/hooks/useViewerReads.d.ts +38 -0
- package/dist/space-sdk/src/index.d.ts +98 -0
- package/dist/space-sdk/src/interactions/actions.d.ts +33 -0
- package/dist/space-sdk/src/interactions/shortcuts.d.ts +42 -0
- package/dist/space-sdk/src/interactions/visibility.d.ts +2 -0
- package/dist/space-sdk/src/nativeData.d.ts +66 -0
- package/dist/space-sdk/src/provider.d.ts +17 -0
- package/dist/space-sdk/src/queryCache.d.ts +45 -0
- package/dist/space-sdk/src/queryErrors.ts +51 -0
- package/dist/space-sdk/src/runtime.d.ts +615 -0
- package/dist/space-sdk/src/runtimeModules.ts +33 -0
- package/dist/space-sdk/src/space.ts +442 -0
- package/dist/space-sdk/src/standardViewParams.d.ts +15 -0
- package/dist/space-sdk/src/tailwind-candidates.d.ts +3418 -0
- package/dist/space-sdk/src/tailwind.ts +66 -0
- package/dist/space-sdk/src/ui/actions/ActionToast.d.ts +15 -0
- package/dist/space-sdk/src/ui/actions/actionWords.d.ts +16 -0
- package/dist/space-sdk/src/ui/actions/index.d.ts +7 -0
- package/dist/space-sdk/src/ui/actions/useActions.d.ts +69 -0
- package/dist/space-sdk/src/ui/actions/useToast.d.ts +41 -0
- package/dist/space-sdk/src/ui/blocks/Tabs.d.ts +139 -0
- package/dist/space-sdk/src/ui/blocks/blocks.d.ts +217 -0
- package/dist/space-sdk/src/ui/blocks/index.d.ts +4 -0
- package/dist/space-sdk/src/ui/board/BoardView.d.ts +36 -0
- package/dist/space-sdk/src/ui/board/boardModel.d.ts +46 -0
- package/dist/space-sdk/src/ui/board/boardWords.d.ts +9 -0
- package/dist/space-sdk/src/ui/board/index.d.ts +5 -0
- package/dist/space-sdk/src/ui/calendar/CalendarView.d.ts +49 -0
- package/dist/space-sdk/src/ui/calendar/DayPicker.d.ts +23 -0
- package/dist/space-sdk/src/ui/calendar/calendarMath.d.ts +81 -0
- package/dist/space-sdk/src/ui/charts/Chart.d.ts +203 -0
- package/dist/space-sdk/src/ui/charts/ChartTable.d.ts +37 -0
- package/dist/space-sdk/src/ui/charts/RenderChartData.d.ts +16 -0
- package/dist/space-sdk/src/ui/charts/StatRow.d.ts +59 -0
- package/dist/space-sdk/src/ui/charts/chartData.d.ts +84 -0
- package/dist/space-sdk/src/ui/comments/CommentPane.d.ts +58 -0
- package/dist/space-sdk/src/ui/comments/commentModel.d.ts +157 -0
- package/dist/space-sdk/src/ui/comments/commentWords.d.ts +60 -0
- package/dist/space-sdk/src/ui/comments/index.d.ts +4 -0
- package/dist/space-sdk/src/ui/comments/useComments.d.ts +146 -0
- package/dist/space-sdk/src/ui/engine/HelpSheet.d.ts +12 -0
- package/dist/space-sdk/src/ui/engine/KeyboardEngine.d.ts +27 -0
- package/dist/space-sdk/src/ui/engine/core.d.ts +73 -0
- package/dist/space-sdk/src/ui/engine/hooks.d.ts +64 -0
- package/dist/space-sdk/src/ui/engine/index.d.ts +14 -0
- package/dist/space-sdk/src/ui/engine/instance.d.ts +21 -0
- package/dist/space-sdk/src/ui/engine/keycapContext.d.ts +2 -0
- package/dist/space-sdk/src/ui/engine/registry.d.ts +77 -0
- package/dist/space-sdk/src/ui/engine/step.d.ts +11 -0
- package/dist/space-sdk/src/ui/engine/types.d.ts +54 -0
- package/dist/space-sdk/src/ui/fields/fieldWords.d.ts +56 -0
- package/dist/space-sdk/src/ui/fields/fields.d.ts +162 -0
- package/dist/space-sdk/src/ui/fields/index.d.ts +5 -0
- package/dist/space-sdk/src/ui/fields/useFieldDraft.d.ts +22 -0
- package/dist/space-sdk/src/ui/gallery/AttachmentGallery.d.ts +53 -0
- package/dist/space-sdk/src/ui/gallery/ImageViewer.d.ts +27 -0
- package/dist/space-sdk/src/ui/gallery/galleryWords.d.ts +95 -0
- package/dist/space-sdk/src/ui/gallery/index.d.ts +5 -0
- package/dist/space-sdk/src/ui/icons.d.ts +13 -0
- package/dist/space-sdk/src/ui/index.d.ts +54 -0
- package/dist/space-sdk/src/ui/keycap/Keycap.d.ts +22 -0
- package/dist/space-sdk/src/ui/keycap/index.d.ts +2 -0
- package/dist/space-sdk/src/ui/layout/Panels.d.ts +50 -0
- package/dist/space-sdk/src/ui/layout/chatPanelEdge.d.ts +77 -0
- package/dist/space-sdk/src/ui/layout/index.d.ts +5 -0
- package/dist/space-sdk/src/ui/layout/panelMath.d.ts +91 -0
- package/dist/space-sdk/src/ui/list/ListReader.d.ts +380 -0
- package/dist/space-sdk/src/ui/list/Row.d.ts +145 -0
- package/dist/space-sdk/src/ui/list/SelectionBar.d.ts +65 -0
- package/dist/space-sdk/src/ui/list/index.d.ts +14 -0
- package/dist/space-sdk/src/ui/list/rowState.d.ts +22 -0
- package/dist/space-sdk/src/ui/list/useListKeys.d.ts +89 -0
- package/dist/space-sdk/src/ui/list/useReorder.d.ts +86 -0
- package/dist/space-sdk/src/ui/list/useSelection.d.ts +90 -0
- package/dist/space-sdk/src/ui/open/OpenAsMenu.d.ts +15 -0
- package/dist/space-sdk/src/ui/open/index.d.ts +6 -0
- package/dist/space-sdk/src/ui/open/openModeStore.d.ts +39 -0
- package/dist/space-sdk/src/ui/open/useOpenMode.d.ts +17 -0
- package/dist/space-sdk/src/ui/overlay/Menu.d.ts +88 -0
- package/dist/space-sdk/src/ui/overlay/Popover.d.ts +28 -0
- package/dist/space-sdk/src/ui/overlay/Tooltip.d.ts +19 -0
- package/dist/space-sdk/src/ui/overlay/index.d.ts +9 -0
- package/dist/space-sdk/src/ui/overlay/layerRoot.d.ts +24 -0
- package/dist/space-sdk/src/ui/overlay/position.d.ts +31 -0
- package/dist/space-sdk/src/ui/period/PeriodPicker.d.ts +30 -0
- package/dist/space-sdk/src/ui/period/dateFormat.d.ts +50 -0
- package/dist/space-sdk/src/ui/period/dateWords.d.ts +88 -0
- package/dist/space-sdk/src/ui/period/periods.d.ts +70 -0
- package/dist/space-sdk/src/ui/period/usePeriod.d.ts +23 -0
- package/dist/space-sdk/src/ui/present/index.d.ts +3 -0
- package/dist/space-sdk/src/ui/present/presentWords.d.ts +27 -0
- package/dist/space-sdk/src/ui/present/screens.d.ts +36 -0
- package/dist/space-sdk/src/ui/present/useAudienceScreen.d.ts +38 -0
- package/dist/space-sdk/src/ui/styles.d.ts +21 -0
- package/dist/space-sdk/src/ui/timeline/TimelineView.d.ts +44 -0
- package/dist/space-sdk/src/ui/timeline/timelineMath.d.ts +123 -0
- package/dist/space-sdk/src/viewPeriod.d.ts +8 -0
- package/dist/space-sdk/src/vite.ts +92 -0
- package/dist/space-sdk/src/words/actions.ts +295 -0
- package/dist/space-sdk/src/words/clashes.ts +158 -0
- package/dist/space-sdk/src/words/format.ts +136 -0
- package/dist/space-sdk/src/words/index.ts +13 -0
- package/dist/space-sdk/src/words/keymap.ts +72 -0
- package/dist/space-sdk/src/words/keys.ts +297 -0
- package/dist/space-sdk/src/words/shell.ts +46 -0
- package/dist/space-sdk/src/words/types.ts +65 -0
- package/dist/view-renderer/dist/host.css +1 -0
- package/dist/view-renderer/dist/host.js +2374 -0
- package/dist/view-renderer/dist/theme.css +73 -0
- package/dist/view-renderer/src/dom.js +212 -0
- package/dist/view-renderer/src/errors.js +8 -0
- package/dist/view-renderer/src/host.tsx +250 -0
- package/dist/view-renderer/src/index.js +4 -0
- package/dist/view-renderer/src/png.js +213 -0
- package/dist/view-renderer/src/pool.js +110 -0
- package/dist/view-renderer/src/remote-broker.js +59 -0
- package/dist/view-renderer/src/renderer.js +271 -0
- package/package.json +21 -11
- package/skills/notis-cli/AGENT_INSTRUCTIONS.md +63 -12
- package/skills/notis-onboarding/BRIEF.md +14 -9
- package/skills/notis-query/cli.md +1 -1
- package/skills/notis-spaces/cli.md +540 -0
- package/src/cli.js +8 -8
- package/src/command-specs/agents.js +121 -8
- package/src/command-specs/auth.js +3 -3
- package/src/command-specs/diagnostics.js +97 -20
- package/src/command-specs/documents.js +158 -0
- package/src/command-specs/files.js +29 -0
- package/src/command-specs/handover.js +331 -79
- package/src/command-specs/helpers.js +50 -40
- package/src/command-specs/index.js +14 -5
- package/src/command-specs/meta.js +38 -9
- package/src/command-specs/onboarding.js +1 -1
- package/src/command-specs/profile.js +42 -1
- package/src/command-specs/skills.js +314 -9
- package/src/command-specs/smoke.js +16 -4
- package/src/command-specs/spaces.js +783 -0
- package/src/command-specs/tools.js +118 -21
- package/src/command-specs/version-history.js +20 -0
- package/src/command-specs/views.js +69 -0
- package/src/runtime/agent-browser.js +178 -267
- package/src/runtime/agent-setup.js +172 -13
- package/src/runtime/app-boundary-validator.js +338 -2
- package/src/runtime/app-data-literals.js +82 -0
- package/src/runtime/app-platform.js +750 -1568
- package/src/runtime/auth-recovery.js +17 -0
- package/src/runtime/base-skills.d.ts +11 -0
- package/src/runtime/base-skills.js +100 -3
- package/src/runtime/computer-identity.d.ts +30 -0
- package/src/runtime/computer-identity.js +151 -0
- package/src/runtime/config-lock.js +135 -0
- package/src/runtime/confined-space-build.js +74 -0
- package/src/runtime/confined-space-verify.js +49 -0
- package/src/runtime/delegated-context.js +5 -21
- package/src/runtime/native-record-files.js +47 -0
- package/src/runtime/oauth.js +342 -31
- package/src/runtime/profiles.js +19 -112
- package/src/runtime/retired-sdk-files.js +24 -0
- package/src/runtime/retry.js +150 -0
- package/src/runtime/skill-sync/cloud-client.ts +33 -8
- package/src/runtime/skill-sync/index.ts +443 -106
- package/src/runtime/skill-sync/local-scanner.ts +248 -32
- package/src/runtime/skill-sync/native-identity.ts +102 -0
- package/src/runtime/skill-sync/native-plan.ts +70 -0
- package/src/runtime/skill-sync/symlink-manager.ts +66 -16
- package/src/runtime/skill-sync/sync-plan.ts +34 -2
- package/src/runtime/skill-sync/types.ts +54 -0
- package/src/runtime/skill-sync/write-cloud-skill.ts +4 -4
- package/src/runtime/skill-sync-service.js +109 -0
- package/src/runtime/source-root.js +32 -0
- package/src/runtime/space-collection-scaffold.js +107 -0
- package/src/runtime/space-database-structures.js +229 -0
- package/src/runtime/space-fixture-broker.js +68 -0
- package/src/runtime/space-lock.js +337 -0
- package/src/runtime/space-platform.js +133 -0
- package/src/runtime/space-source.js +290 -0
- package/src/runtime/space-test-server.js +354 -0
- package/src/runtime/space-verification.js +94 -0
- package/src/runtime/space-verify-browser-worker.js +51 -0
- package/src/runtime/space-view-manifest.js +368 -0
- package/src/runtime/sync-skills.d.ts +5 -1
- package/src/runtime/sync-skills.js +11 -2
- package/src/runtime/tool-confirmation.js +138 -0
- package/src/runtime/transport.js +118 -24
- package/src/runtime/upsell.js +219 -0
- package/src/runtime/view-render.js +49 -0
- package/src/skill-sync-worker-entry.js +2 -0
- package/src/skill-sync-worker.js +114 -0
- package/src/space-harness-host.ts +122 -0
- package/template/.harness/index.html.tmpl +9 -352
- package/template/app/globals.css +29 -3
- package/template/app/layout.tsx +2 -6
- package/template/components/ui/badge.tsx +7 -4
- package/template/components/ui/button.tsx +1 -1
- package/template/components/ui/native-select.tsx +24 -0
- package/template/notis.config.ts +4 -36
- package/template/package-lock.json +1800 -1867
- package/template/package.json +16 -14
- package/template/postcss.config.mjs +1 -1
- package/template/tailwind.config.ts +1 -6
- package/template/tsconfig.json +1 -0
- package/template/vite.config.ts +2 -2
- package/dist/base-skills/notis-apps/SKILL.md +0 -613
- package/skills/notis-apps/cli.md +0 -285
- package/src/command-specs/apps.js +0 -3000
- package/src/runtime/app-changelog.js +0 -79
- package/src/runtime/app-dev-consumers.js +0 -154
- package/src/runtime/app-dev-host-lock.js +0 -80
- package/src/runtime/app-dev-process-identity.js +0 -111
- package/src/runtime/app-dev-roots.js +0 -284
- package/src/runtime/app-dev-server.js +0 -1140
- package/src/runtime/app-dev-sessions.js +0 -185
- package/src/runtime/app-registry-scaffolds.js +0 -367
- package/src/runtime/assets/store-screenshot-dark.png +0 -0
- package/src/runtime/cli-mode.generated.js +0 -5
- package/src/runtime/cli-mode.js +0 -34
- package/src/runtime/store-screenshot.js +0 -143
- package/template/CHANGELOG.md +0 -5
- package/template/app/page.tsx +0 -60
- package/template/components/ui/card.tsx +0 -56
- package/template/packages/sdk/src/components/DocumentEditor.tsx +0 -103
- package/template/packages/sdk/src/config.ts +0 -259
- package/template/packages/sdk/src/hooks/useCloudComputer.ts +0 -97
- package/template/packages/sdk/src/hooks/useDatabaseSchema.ts +0 -85
- package/template/packages/sdk/src/hooks/useDocument.ts +0 -78
- package/template/packages/sdk/src/hooks/useNotisNavigation.ts +0 -52
- package/template/packages/sdk/src/hooks/useTopBarSearch.ts +0 -73
- package/template/packages/sdk/src/runtime.ts +0 -439
- package/template/packages/sdk/src/vite.ts +0 -56
- package/template/packages/sdk/tsconfig.json +0 -15
- /package/{template/packages → dist}/sdk/src/components/MultiSelectDragOverlay.tsx +0 -0
- /package/{template/packages → dist}/sdk/src/components/ShortcutHints.tsx +0 -0
- /package/{template/packages → dist}/sdk/src/hooks/useActiveResource.ts +0 -0
- /package/{template/packages → dist}/sdk/src/hooks/useTool.ts +0 -0
- /package/{template/packages → dist}/sdk/src/hooks/useTools.ts +0 -0
- /package/{template/packages → dist}/sdk/src/ui.ts +0 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useNotisRuntime } from '../provider';
|
|
4
|
+
import { useQuery, type UseQueryResult } from './useQuery';
|
|
5
|
+
import {
|
|
6
|
+
SPACE_VIEWER_READ_OPERATIONS,
|
|
7
|
+
type SpaceViewerDatabase,
|
|
8
|
+
type SpaceViewerReadFamily,
|
|
9
|
+
type SpaceViewerReadInput,
|
|
10
|
+
type SpaceViewerReadOperation,
|
|
11
|
+
type SpaceViewerReadResult,
|
|
12
|
+
type SpaceViewerSkill,
|
|
13
|
+
} from '../space';
|
|
14
|
+
|
|
15
|
+
export interface UseViewerReadOptions {
|
|
16
|
+
enabled?: boolean;
|
|
17
|
+
staleTimeMs?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Bounded crawl of the viewer's Skill inventory: 50 pages of 100.
|
|
21
|
+
const MAX_SKILL_PAGES = 50;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* True when this page may read what the signed-in viewer can open: the Space declares
|
|
25
|
+
* `viewerReads` for this family and the host offers it (never on a Site or anonymous link).
|
|
26
|
+
* Render a fallback when false.
|
|
27
|
+
*/
|
|
28
|
+
export function useViewerReadAvailable(family: SpaceViewerReadFamily): boolean {
|
|
29
|
+
const runtime = useNotisRuntime();
|
|
30
|
+
return runtime?.resource?.kind === 'space' && typeof runtime.viewerRead === 'function'
|
|
31
|
+
&& Boolean(runtime.space?.viewerReads?.includes(family));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** One cached, read-only viewer read. Results are scoped to the current viewer by the host cache scope. */
|
|
35
|
+
export function useViewerRead<TOperation extends SpaceViewerReadOperation>(operation: TOperation,
|
|
36
|
+
input: SpaceViewerReadInput[TOperation], options: UseViewerReadOptions = {}): UseQueryResult<SpaceViewerReadResult[TOperation]> {
|
|
37
|
+
const runtime = useNotisRuntime();
|
|
38
|
+
const available = useViewerReadAvailable(SPACE_VIEWER_READ_OPERATIONS[operation]);
|
|
39
|
+
return useQuery(['viewer-read', operation, input], async () => {
|
|
40
|
+
if (!runtime?.viewerRead) throw new Error('Viewer reads are unavailable here.');
|
|
41
|
+
return runtime.viewerRead<SpaceViewerReadResult[TOperation]>(operation, input as Record<string, unknown>);
|
|
42
|
+
}, { readOnly: true, enabled: available && options.enabled !== false, staleTimeMs: options.staleTimeMs });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Every database the signed-in viewer can open, with its available links and read target. */
|
|
46
|
+
export function useViewerDatabases(options: UseViewerReadOptions = {}): UseQueryResult<SpaceViewerDatabase[]> {
|
|
47
|
+
const runtime = useNotisRuntime();
|
|
48
|
+
const available = useViewerReadAvailable('databases');
|
|
49
|
+
return useQuery(['viewer-databases'], async () => {
|
|
50
|
+
if (!runtime?.viewerRead) throw new Error('Viewer reads are unavailable here.');
|
|
51
|
+
const result = await runtime.viewerRead<SpaceViewerReadResult['list_databases']>('list_databases', {});
|
|
52
|
+
if (!Array.isArray(result?.databases)) throw new Error('The database list is incomplete.');
|
|
53
|
+
return result.databases;
|
|
54
|
+
}, { readOnly: true, enabled: available && options.enabled !== false, staleTimeMs: options.staleTimeMs });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
type ListSkillsRead = (input: SpaceViewerReadInput['list_skills']) => Promise<SpaceViewerReadResult['list_skills']>;
|
|
58
|
+
|
|
59
|
+
// Content pages read at once. Each one holds an API worker while its snapshots are read, and the API
|
|
60
|
+
// has only a few, so a large inventory never takes them all: two pages, plus one quick cursor read.
|
|
61
|
+
export const SKILL_CONTENT_PAGES_AT_ONCE = 2;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Every Skill page, in order. Pages with instructions are slow (each reads its snapshots), so the next
|
|
65
|
+
* page's cursor comes from a quick read without them and that page starts while the previous one is
|
|
66
|
+
* still read, at most SKILL_CONTENT_PAGES_AT_ONCE content pages at a time. A cursor that no longer
|
|
67
|
+
* matches (the list changed meanwhile) drops the pages started from it, waits for them to end and
|
|
68
|
+
* continues from the real cursor.
|
|
69
|
+
*/
|
|
70
|
+
export async function readAllViewerSkills(read: ListSkillsRead,
|
|
71
|
+
{ includeContent, includeDisabled }: { includeContent: boolean; includeDisabled: boolean }): Promise<SpaceViewerSkill[]> {
|
|
72
|
+
const page = (after: string | null, content: boolean) => read({ limit: 100, include_content: content,
|
|
73
|
+
include_disabled: includeDisabled, ...(after ? { after } : {}) }).then(result => {
|
|
74
|
+
if (!Array.isArray(result?.skills)) throw new Error('The Skill list is incomplete.');
|
|
75
|
+
return result;
|
|
76
|
+
});
|
|
77
|
+
const skills: SpaceViewerSkill[] = [];
|
|
78
|
+
// The pages started and not read yet, in order, each with the cursor it started from.
|
|
79
|
+
const started: { after: string | null; result: Promise<SpaceViewerReadResult['list_skills']> }[] = [];
|
|
80
|
+
// Where the next page to start begins, and whether a quick read (or the last page read) says it exists.
|
|
81
|
+
let cursor: string | null = null;
|
|
82
|
+
let known = true;
|
|
83
|
+
let pages = 0;
|
|
84
|
+
for (;;) {
|
|
85
|
+
if (known && started.length < (includeContent ? SKILL_CONTENT_PAGES_AT_ONCE : 1) && pages + started.length < MAX_SKILL_PAGES) {
|
|
86
|
+
const current = { after: cursor, result: page(cursor, includeContent) };
|
|
87
|
+
void current.result.catch(() => {});
|
|
88
|
+
started.push(current);
|
|
89
|
+
known = false;
|
|
90
|
+
if (includeContent) {
|
|
91
|
+
const probe = await page(cursor, false);
|
|
92
|
+
known = Boolean(probe.has_more && probe.next);
|
|
93
|
+
cursor = probe.next ?? null;
|
|
94
|
+
}
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
const head = started.shift();
|
|
98
|
+
if (!head) break;
|
|
99
|
+
const result = await head.result;
|
|
100
|
+
skills.push(...result.skills);
|
|
101
|
+
pages += 1;
|
|
102
|
+
if (!result.has_more || !result.next) return skills;
|
|
103
|
+
// A page started ahead is kept only when it continues exactly where this one ended; otherwise (and
|
|
104
|
+
// when none was started) the next page starts from this page's own cursor.
|
|
105
|
+
if (started[0]?.after !== result.next) {
|
|
106
|
+
const dropped = started.splice(0);
|
|
107
|
+
await Promise.allSettled(dropped.map(entry => entry.result));
|
|
108
|
+
cursor = result.next;
|
|
109
|
+
known = true;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
throw new Error('This account has more Skills than a page can read at once.');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Every Skill the signed-in viewer can see, all pages. With `includeContent`, `skill_md`
|
|
117
|
+
* is present where the viewer holds content access and null elsewhere (curated, legacy).
|
|
118
|
+
*/
|
|
119
|
+
export function useViewerSkills(options: UseViewerReadOptions & { includeContent?: boolean; includeDisabled?: boolean } = {}):
|
|
120
|
+
UseQueryResult<SpaceViewerSkill[]> {
|
|
121
|
+
const runtime = useNotisRuntime();
|
|
122
|
+
const available = useViewerReadAvailable('skills');
|
|
123
|
+
const includeContent = options.includeContent === true, includeDisabled = options.includeDisabled === true;
|
|
124
|
+
return useQuery(['viewer-skills', includeContent, includeDisabled], async () => {
|
|
125
|
+
if (!runtime?.viewerRead) throw new Error('Viewer reads are unavailable here.');
|
|
126
|
+
return readAllViewerSkills((input: SpaceViewerReadInput['list_skills']) => runtime.viewerRead!('list_skills', input),
|
|
127
|
+
{ includeContent, includeDisabled });
|
|
128
|
+
}, { readOnly: true, enabled: available && options.enabled !== false, staleTimeMs: options.staleTimeMs });
|
|
129
|
+
}
|
|
@@ -10,6 +10,32 @@
|
|
|
10
10
|
export { NotisProvider, useNotisRuntime } from './provider';
|
|
11
11
|
|
|
12
12
|
// Hooks
|
|
13
|
+
export { useAction, useActionQuery } from './hooks/useAction';
|
|
14
|
+
export { useNativeDocuments, useNativeMutation, useNativeDelete, type NativeDeleteManyResult } from './hooks/useNativeDocuments';
|
|
15
|
+
export type { NativeField, NativeFilter, NativeQuery, NativeInsert, NativeUpdate, NativeDelete, NativeMutationReceipt } from './nativeData';
|
|
16
|
+
export type { SpaceDatabaseCreate, SpaceResourceReference } from './space';
|
|
17
|
+
export type { SpaceActionTemplate, SpaceActionDescriptor, SpaceRuntimeDescriptor, SpaceActionOptions } from './space';
|
|
18
|
+
export type { SpaceDocumentBodyRequest, SpaceDocumentBodyRead, SpaceDocumentBodySave, SpaceDocumentBodyResult } from './space';
|
|
19
|
+
export { useNativeDocumentBody } from './hooks/useNativeDocumentBody';
|
|
20
|
+
export { useViewerRead, useViewerReadAvailable, useViewerDatabases, useViewerSkills } from './hooks/useViewerReads';
|
|
21
|
+
export type { UseViewerReadOptions } from './hooks/useViewerReads';
|
|
22
|
+
export { useViewParams, useShown, useShownAvailable, parseViewParams } from './hooks/useViewParams';
|
|
23
|
+
export type { ViewParams, ViewParamsOf, ViewParamChanges, SetViewParamsOptions, UseShownOptions } from './hooks/useViewParams';
|
|
24
|
+
export { SPACE_VIEW_SPEC_VERSION } from './space';
|
|
25
|
+
export type { SpaceViewParam, SpaceViewParamType, SpaceViewFilter, SpaceShows, SpaceShownDescriptor, SpaceShownOptions, SpaceShownResult,
|
|
26
|
+
SpaceMemoryPolicy, SpaceMemorySnapshot, SpaceChrome, SpaceMarkdownInput } from './space';
|
|
27
|
+
export { SPACE_VIEWER_READ_OPERATIONS } from './space';
|
|
28
|
+
export { SPACE_CLOUD_COMPUTER_OPERATIONS, spaceCloudComputerAllows } from './space';
|
|
29
|
+
export type { SpaceCloudComputerLevel, SpaceCloudComputerOperation, SpaceCloudComputerStatus, SpaceCloudComputerRelease, SpaceCloudComputerRunInput,
|
|
30
|
+
SpaceCloudComputerRunResult, SpaceCloudComputerUploadInput, SpaceCloudComputerUploadResult, SpaceCloudComputerTransport } from './space';
|
|
31
|
+
export type { SpaceViewerReadFamily, SpaceViewerReadOperation, SpaceViewerReadInput, SpaceViewerReadResult, SpaceViewerLink,
|
|
32
|
+
SpaceViewerDatabase, SpaceViewerDatabaseSchema, SpaceViewerSkill, SpaceViewerSkillPage } from './space';
|
|
33
|
+
export { SpaceActionError, SpaceNavigationCancelled, isSpaceNavigationCancelled } from './space';
|
|
34
|
+
export type { SpaceActionFailureDetails } from './space';
|
|
35
|
+
export { useQuery, useQueryClient } from './hooks/useQuery';
|
|
36
|
+
export type { UseQueryOptions, UseQueryResult } from './hooks/useQuery';
|
|
37
|
+
export { createQueryClient, createPrefetchQueue, queryKey } from './queryCache';
|
|
38
|
+
export type { NotisQueryClient, QuerySnapshot, QueryFetchOptions, HiddenRecords } from './queryCache';
|
|
13
39
|
export { useNotis } from './hooks/useNotis';
|
|
14
40
|
export { useDocuments } from './hooks/useDocuments';
|
|
15
41
|
export type { UseDocumentsOptions, UseDocumentsResult } from './hooks/useDocuments';
|
|
@@ -31,14 +57,18 @@ export { useHandover } from './hooks/useHandover';
|
|
|
31
57
|
export type { UseHandoverResult } from './hooks/useHandover';
|
|
32
58
|
export { useCloudComputer } from './hooks/useCloudComputer';
|
|
33
59
|
export type { UseCloudComputerResult } from './hooks/useCloudComputer';
|
|
60
|
+
export { useCloudComputerShell } from './hooks/useCloudComputerShell';
|
|
61
|
+
export type { UseCloudComputerShellResult } from './hooks/useCloudComputerShell';
|
|
34
62
|
export { useNotisNavigation } from './hooks/useNotisNavigation';
|
|
35
63
|
export { useTopBarSearch } from './hooks/useTopBarSearch';
|
|
64
|
+
export { useTopBarTitleLeaf } from './hooks/useTopBarTitleLeaf';
|
|
36
65
|
export { useBackend } from './hooks/useBackend';
|
|
37
66
|
export { useMultiSelect } from './hooks/useMultiSelect';
|
|
38
67
|
export { useCollectionInteractions, COLLECTION_ITEM_ATTRIBUTE } from './hooks/useCollectionInteractions';
|
|
39
68
|
export { useActiveResource } from './hooks/useActiveResource';
|
|
69
|
+
// ShortcutProvider is not exported here (D2): NotisProvider mounts it for every app; hosts take it from
|
|
70
|
+
// '@notis/sdk/interactions'.
|
|
40
71
|
export {
|
|
41
|
-
ShortcutProvider,
|
|
42
72
|
SHORTCUT_SCOPE_PRIORITY,
|
|
43
73
|
isEditableShortcutEvent,
|
|
44
74
|
shortcutDisplay,
|
|
@@ -49,12 +79,16 @@ export {
|
|
|
49
79
|
export { Markdown } from './components/Markdown';
|
|
50
80
|
export type { MarkdownProps } from './components/Markdown';
|
|
51
81
|
export { DocumentEditor } from './components/DocumentEditor';
|
|
82
|
+
export { DocumentPage, DocumentPageSkeleton, usePrefetchRecord } from './components/DocumentPage';
|
|
83
|
+
export { RecordProperties, HtmlFrame, ReportFrame, ShareControl, SpacePageFrame } from './components/RecordComponents';
|
|
84
|
+
export { RenderChartData } from './ui/charts/RenderChartData';
|
|
52
85
|
export { MarkdownEditor } from './components/MarkdownEditor';
|
|
53
86
|
export { NotisSelectionBoundary, NOTIS_CONTEXT_CLIPBOARD_TYPE } from './components/NotisSelectionBoundary';
|
|
54
87
|
export type { NotisSelectionBoundaryProps } from './components/NotisSelectionBoundary';
|
|
55
88
|
export {
|
|
56
89
|
asRecord,
|
|
57
90
|
blockNoteToPlainText,
|
|
91
|
+
DOCUMENT_WORDS,
|
|
58
92
|
extractRichText,
|
|
59
93
|
getDocumentPreview,
|
|
60
94
|
getRelationIds,
|
|
@@ -66,8 +100,11 @@ export {
|
|
|
66
100
|
normalizePropertyValue,
|
|
67
101
|
optionalString,
|
|
68
102
|
} from './documents';
|
|
103
|
+
export type { DocumentWordsOptions } from './documents';
|
|
69
104
|
|
|
70
105
|
// Multi-select components
|
|
106
|
+
export { Dialog } from './components/Dialog';
|
|
107
|
+
export type { DialogProps } from './components/Dialog';
|
|
71
108
|
export { MultiSelectActionBar } from './components/MultiSelectActionBar';
|
|
72
109
|
export { MultiSelectCheckbox, MultiSelectCheckbox as SelectionCheckbox } from './components/MultiSelectCheckbox';
|
|
73
110
|
export { MultiSelectDragOverlay, MultiSelectDragOverlay as SelectionMarquee } from './components/MultiSelectDragOverlay';
|
|
@@ -129,10 +166,22 @@ export type {
|
|
|
129
166
|
HandoverPayload,
|
|
130
167
|
HandoverResult,
|
|
131
168
|
NotisDocumentEditorProps,
|
|
169
|
+
NotisDocumentPageProps,
|
|
170
|
+
NotisDocumentPageCrumb,
|
|
171
|
+
NotisDocumentPageMenuItem,
|
|
172
|
+
NotisDocumentPageParts,
|
|
173
|
+
NotisRecordProps,
|
|
174
|
+
NotisRecordPropertiesProps,
|
|
175
|
+
NotisHtmlFrameProps,
|
|
176
|
+
NotisHtmlFramePresentAction,
|
|
177
|
+
NotisReportFrameProps,
|
|
178
|
+
NotisShareControlProps,
|
|
179
|
+
NotisSpacePageFrameProps,
|
|
132
180
|
NotisMarkdownEditorProps,
|
|
133
181
|
NotisMarkdownEditorSavePayload,
|
|
134
182
|
NotisMarkdownEditorSaveResult,
|
|
135
183
|
NotisRuntime,
|
|
184
|
+
RuntimeResource,
|
|
136
185
|
NotisRuntimeContext,
|
|
137
186
|
NotisRuntimeUI,
|
|
138
187
|
QueryFilter,
|
|
@@ -143,3 +192,17 @@ export type {
|
|
|
143
192
|
ToolCallOptions,
|
|
144
193
|
ToolInputSchema,
|
|
145
194
|
} from './runtime';
|
|
195
|
+
|
|
196
|
+
export { useToolQuery } from './hooks/useToolQuery';
|
|
197
|
+
|
|
198
|
+
export { Skeleton, SKELETON_WORDS, ViewSkeleton } from './components/Skeleton';
|
|
199
|
+
|
|
200
|
+
export { useLongPressSelection } from './hooks/useLongPressSelection';
|
|
201
|
+
export { isInteractionElementVisible } from './interactions/visibility';
|
|
202
|
+
|
|
203
|
+
export type { AgentContextContent, AgentContextItem, ContextAttachment } from './agentContext';
|
|
204
|
+
export { useAgentContext } from './hooks/useAgentContext';
|
|
205
|
+
export { NotisCommentBoundary, NotisCommentBox } from './components/NotisCommentBoundary';
|
|
206
|
+
export type { NotisCommentBoundaryProps, NotisCommentBoxProps } from './components/NotisCommentBoundary';
|
|
207
|
+
export { VIEW_PERIODS, isViewDate, isViewPeriod, resolveViewPeriod } from './viewPeriod';
|
|
208
|
+
export { journalViewParams, tasksViewParams, tasksViewFilter } from './standardViewParams';
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
2
|
|
|
3
|
-
export type CollectionActionIntent =
|
|
3
|
+
export type CollectionActionIntent =
|
|
4
|
+
| 'archive' | 'star' | 'delete'
|
|
5
|
+
| 'enable' | 'disable' | 'pause' | 'resume'
|
|
6
|
+
| 'move' | 'add-to-folder' | 'complete' | 'start-progress'
|
|
7
|
+
| 'custom';
|
|
4
8
|
|
|
5
9
|
export interface CollectionActionContext<T> {
|
|
6
10
|
selectedIds: string[];
|
|
@@ -13,6 +17,7 @@ export interface CollectionAction<T> {
|
|
|
13
17
|
intent?: CollectionActionIntent;
|
|
14
18
|
label?: string;
|
|
15
19
|
icon?: ReactNode;
|
|
20
|
+
/** Omit to use the intent default, supply a key to override, or false to remove it. */
|
|
16
21
|
shortcut?: string | false;
|
|
17
22
|
destructive?: boolean;
|
|
18
23
|
disabled?: boolean | ((context: CollectionActionContext<T>) => boolean);
|
|
@@ -35,6 +40,14 @@ const ACTION_DEFAULTS: Record<Exclude<CollectionActionIntent, 'custom'>, { label
|
|
|
35
40
|
archive: { label: 'Archive', shortcut: 'E' },
|
|
36
41
|
star: { label: 'Star', shortcut: 'S' },
|
|
37
42
|
delete: { label: 'Delete', shortcut: '#' },
|
|
43
|
+
enable: { label: 'Enable', shortcut: 'E' },
|
|
44
|
+
disable: { label: 'Disable', shortcut: 'D' },
|
|
45
|
+
pause: { label: 'Pause', shortcut: 'P' },
|
|
46
|
+
resume: { label: 'Resume', shortcut: 'R' },
|
|
47
|
+
move: { label: 'Move', shortcut: 'M' },
|
|
48
|
+
'add-to-folder': { label: 'Add to folder', shortcut: 'F' },
|
|
49
|
+
complete: { label: 'Mark complete', shortcut: 'C' },
|
|
50
|
+
'start-progress': { label: 'Start progress', shortcut: 'P' },
|
|
38
51
|
};
|
|
39
52
|
|
|
40
53
|
export function collectionActionDefaults(intent: CollectionActionIntent | undefined): {
|
|
@@ -5,11 +5,13 @@ import React, {
|
|
|
5
5
|
useCallback,
|
|
6
6
|
useContext,
|
|
7
7
|
useEffect,
|
|
8
|
+
useLayoutEffect,
|
|
8
9
|
useMemo,
|
|
9
10
|
useRef,
|
|
10
11
|
useState,
|
|
11
12
|
type ReactNode,
|
|
12
13
|
} from 'react';
|
|
14
|
+
import { isInteractionElementVisible } from './visibility';
|
|
13
15
|
|
|
14
16
|
export type ShortcutScope = 'app' | 'route' | 'collection' | 'detail' | 'modal';
|
|
15
17
|
|
|
@@ -42,6 +44,8 @@ export interface UseShortcutsOptions {
|
|
|
42
44
|
sequenceTimeoutMs?: number;
|
|
43
45
|
/** Internal owner used to route collection shortcuts to the last interacted collection. */
|
|
44
46
|
collectionOwnerId?: string;
|
|
47
|
+
/** Evaluated at dispatch time, including for retained hidden app views. */
|
|
48
|
+
isAvailable?: () => boolean;
|
|
45
49
|
}
|
|
46
50
|
|
|
47
51
|
interface ShortcutRegistration {
|
|
@@ -52,6 +56,8 @@ interface ShortcutRegistration {
|
|
|
52
56
|
enabled: boolean;
|
|
53
57
|
sequenceTimeoutMs: number;
|
|
54
58
|
collectionOwnerId?: string;
|
|
59
|
+
/** Evaluated at dispatch time, including for retained hidden app views. */
|
|
60
|
+
isAvailable?: () => boolean;
|
|
55
61
|
getShortcuts: () => ShortcutDefinition[];
|
|
56
62
|
}
|
|
57
63
|
|
|
@@ -92,6 +98,11 @@ export function createShortcutCollectionOwner(): string {
|
|
|
92
98
|
return `collection-${state.nextOwnerId++}`;
|
|
93
99
|
}
|
|
94
100
|
|
|
101
|
+
export function releaseShortcutCollection(ownerId: string): void {
|
|
102
|
+
const state = getShortcutCollectionState();
|
|
103
|
+
if (state.activeOwnerId === ownerId) state.activeOwnerId = null;
|
|
104
|
+
}
|
|
105
|
+
|
|
95
106
|
export function activateShortcutCollection(ownerId: string): void {
|
|
96
107
|
getShortcutCollectionState().activeOwnerId = ownerId;
|
|
97
108
|
}
|
|
@@ -186,7 +197,8 @@ export function isEditableShortcutEvent(event: Event): boolean {
|
|
|
186
197
|
return true;
|
|
187
198
|
}
|
|
188
199
|
if (tag === 'A' && target.hasAttribute('href')) return true;
|
|
189
|
-
|
|
200
|
+
// A focused separator (a resizable edge) owns its arrow keys.
|
|
201
|
+
return ['button', 'link', 'menuitem', 'separator', 'switch', 'tab'].includes(
|
|
190
202
|
target.getAttribute('role') || '',
|
|
191
203
|
);
|
|
192
204
|
});
|
|
@@ -206,10 +218,37 @@ function consumeShortcut(event: KeyboardEvent, definition: ShortcutDefinition):
|
|
|
206
218
|
void definition.onTrigger(event);
|
|
207
219
|
}
|
|
208
220
|
|
|
209
|
-
|
|
221
|
+
// Apps built before collections stopped binding Escape to an empty selection
|
|
222
|
+
// still register it, which swallows Escape for nothing. With collection items
|
|
223
|
+
// rendered and none selected, leave Escape to the enclosing surface (the Portal
|
|
224
|
+
// closes its frontmost layer, then returns to Inbox).
|
|
225
|
+
function isEmptyCollectionClear(shortcut: ShortcutDefinition, ownerDocument: Document, event: KeyboardEvent): boolean {
|
|
226
|
+
if (event.key !== 'Escape' || shortcut.id !== 'collection.clear') return false;
|
|
227
|
+
const items = collectionItems(ownerDocument).filter((item) => isInteractionElementVisible(item as HTMLElement));
|
|
228
|
+
return items.length > 0 && !items.some((item) => item.getAttribute('aria-selected') === 'true');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** App views render in open shadow roots, which document queries do not enter. */
|
|
232
|
+
function collectionItems(root: Document | ShadowRoot): Element[] {
|
|
233
|
+
const items = Array.from(root.querySelectorAll('[data-notis-collection-item-id], [data-notis-row-id]'));
|
|
234
|
+
for (const element of Array.from(root.querySelectorAll('*'))) {
|
|
235
|
+
if (element.shadowRoot) items.push(...collectionItems(element.shadowRoot));
|
|
236
|
+
}
|
|
237
|
+
return items;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Native dialogs make the background inert, but document capture listeners still
|
|
241
|
+
// receive their keyboard events. Leave those events to the dialog and its controls.
|
|
242
|
+
function isDialogEvent(event: KeyboardEvent): boolean {
|
|
243
|
+
return event.composedPath().some((target) =>
|
|
244
|
+
target instanceof HTMLElement && target.matches('dialog.notis-dialog[open]'),
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export function ShortcutProvider({ children, isAvailable }: { children: ReactNode; isAvailable?: () => boolean }) {
|
|
210
249
|
const parentRegistry = useContext(ShortcutContext);
|
|
211
250
|
if (parentRegistry) return <>{children}</>;
|
|
212
|
-
return <ShortcutProviderRoot>{children}</ShortcutProviderRoot>;
|
|
251
|
+
return <ShortcutProviderRoot isAvailable={isAvailable}>{children}</ShortcutProviderRoot>;
|
|
213
252
|
}
|
|
214
253
|
|
|
215
254
|
export interface ShortcutHelpEntry {
|
|
@@ -233,7 +272,7 @@ function applicableRegistrations(
|
|
|
233
272
|
registrations: Iterable<ShortcutRegistration>,
|
|
234
273
|
): ShortcutRegistration[] {
|
|
235
274
|
const ordered = Array.from(registrations)
|
|
236
|
-
.filter((registration) => registration.enabled)
|
|
275
|
+
.filter((registration) => registration.enabled && registration.isAvailable?.() !== false)
|
|
237
276
|
.sort((a, b) => b.priority - a.priority || b.order - a.order);
|
|
238
277
|
const activeCollectionOwner = getShortcutCollectionState().activeOwnerId;
|
|
239
278
|
const hasActiveCollection = Boolean(
|
|
@@ -248,6 +287,61 @@ function applicableRegistrations(
|
|
|
248
287
|
: ordered;
|
|
249
288
|
}
|
|
250
289
|
|
|
290
|
+
// Standalone app verification and legacy hosts may not install a provider.
|
|
291
|
+
// They must still arbitrate shortcuts together, not by effect/listener order.
|
|
292
|
+
const FALLBACK_REGISTRIES_SYMBOL = Symbol.for('notis.sdk.fallback_shortcut_registries');
|
|
293
|
+
interface FallbackShortcutRegistry {
|
|
294
|
+
registrations: Map<number, ShortcutRegistration>;
|
|
295
|
+
nextId: number;
|
|
296
|
+
dispatch: (event: KeyboardEvent) => void;
|
|
297
|
+
}
|
|
298
|
+
type FallbackShortcutGlobal = typeof globalThis & {
|
|
299
|
+
[FALLBACK_REGISTRIES_SYMBOL]?: WeakMap<Document, FallbackShortcutRegistry>;
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
function registerFallbackShortcut(input: Omit<ShortcutRegistration, 'id' | 'order'>): () => void {
|
|
303
|
+
const scope = globalThis as FallbackShortcutGlobal;
|
|
304
|
+
const registries = scope[FALLBACK_REGISTRIES_SYMBOL] ??= new WeakMap();
|
|
305
|
+
const ownerDocument = document;
|
|
306
|
+
let registry = registries.get(ownerDocument);
|
|
307
|
+
if (!registry) {
|
|
308
|
+
const registrations = new Map<number, ShortcutRegistration>();
|
|
309
|
+
registry = {
|
|
310
|
+
registrations,
|
|
311
|
+
nextId: 1,
|
|
312
|
+
dispatch: (event) => {
|
|
313
|
+
if (event.defaultPrevented || isDialogEvent(event)) return;
|
|
314
|
+
const editable = isEditableShortcutEvent(event);
|
|
315
|
+
const token = chordToken(eventChord(event));
|
|
316
|
+
for (const registration of applicableRegistrations(registrations.values())) {
|
|
317
|
+
for (const shortcut of registration.getShortcuts()) {
|
|
318
|
+
if (shortcut.enabled === false || (editable && !shortcut.allowInEditable) || (event.repeat && !shortcut.allowRepeat)
|
|
319
|
+
|| isEmptyCollectionClear(shortcut, ownerDocument, event)) continue;
|
|
320
|
+
const match = parseShortcut(shortcut).some(
|
|
321
|
+
(candidate) => candidate.sequence.length === 1 && chordToken(candidate.sequence[0]!) === token,
|
|
322
|
+
);
|
|
323
|
+
if (match) {
|
|
324
|
+
consumeShortcut(event, shortcut);
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
};
|
|
331
|
+
registries.set(ownerDocument, registry);
|
|
332
|
+
ownerDocument.addEventListener('keydown', registry.dispatch, true);
|
|
333
|
+
}
|
|
334
|
+
const id = registry.nextId++;
|
|
335
|
+
registry.registrations.set(id, { ...input, id, order: id });
|
|
336
|
+
return () => {
|
|
337
|
+
registry.registrations.delete(id);
|
|
338
|
+
if (registry.registrations.size === 0) {
|
|
339
|
+
ownerDocument.removeEventListener('keydown', registry.dispatch, true);
|
|
340
|
+
registries.delete(ownerDocument);
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
|
|
251
345
|
function shortcutToken(raw: string): string | null {
|
|
252
346
|
const definition: ShortcutDefinition = { id: '', keys: raw, onTrigger: () => undefined };
|
|
253
347
|
const parsed = parseShortcut(definition)[0];
|
|
@@ -277,7 +371,7 @@ function collectShortcutHelpEntries(
|
|
|
277
371
|
return entries;
|
|
278
372
|
}
|
|
279
373
|
|
|
280
|
-
function ShortcutProviderRoot({ children }: { children: ReactNode }) {
|
|
374
|
+
function ShortcutProviderRoot({ children, isAvailable }: { children: ReactNode; isAvailable?: () => boolean }) {
|
|
281
375
|
const registrationsRef = useRef(new Map<number, ShortcutRegistration>());
|
|
282
376
|
const nextIdRef = useRef(1);
|
|
283
377
|
const nextOrderRef = useRef(1);
|
|
@@ -285,12 +379,21 @@ function ShortcutProviderRoot({ children }: { children: ReactNode }) {
|
|
|
285
379
|
const [helpOpen, setHelpOpen] = useState(false);
|
|
286
380
|
const helpOpenRef = useRef(false);
|
|
287
381
|
const [, setRegistryVersion] = useState(0);
|
|
382
|
+
const availabilityRef = useRef(isAvailable);
|
|
288
383
|
|
|
289
384
|
const updateHelpOpen = useCallback((open: boolean) => {
|
|
290
385
|
helpOpenRef.current = open;
|
|
291
386
|
setHelpOpen(open);
|
|
292
387
|
}, []);
|
|
293
388
|
|
|
389
|
+
useLayoutEffect(() => {
|
|
390
|
+
availabilityRef.current = isAvailable;
|
|
391
|
+
if (isAvailable?.() === false) {
|
|
392
|
+
sequenceRef.current = { chords: [], at: 0 };
|
|
393
|
+
updateHelpOpen(false);
|
|
394
|
+
}
|
|
395
|
+
}, [isAvailable, updateHelpOpen]);
|
|
396
|
+
|
|
294
397
|
const register = useCallback((input: Omit<ShortcutRegistration, 'id' | 'order'>) => {
|
|
295
398
|
const id = nextIdRef.current++;
|
|
296
399
|
registrationsRef.current.set(id, {
|
|
@@ -307,6 +410,11 @@ function ShortcutProviderRoot({ children }: { children: ReactNode }) {
|
|
|
307
410
|
|
|
308
411
|
useEffect(() => {
|
|
309
412
|
const handleKeyDown = (event: KeyboardEvent) => {
|
|
413
|
+
if (availabilityRef.current?.() === false) {
|
|
414
|
+
sequenceRef.current = { chords: [], at: 0 };
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
if (event.defaultPrevented || isDialogEvent(event)) return;
|
|
310
418
|
const editable = isEditableShortcutEvent(event);
|
|
311
419
|
if (!editable && !event.repeat && event.key === '?' && !event.metaKey && !event.ctrlKey && !event.altKey) {
|
|
312
420
|
event.preventDefault();
|
|
@@ -335,6 +443,7 @@ function ShortcutProviderRoot({ children }: { children: ReactNode }) {
|
|
|
335
443
|
.filter((shortcut) => shortcut.enabled !== false)
|
|
336
444
|
.filter((shortcut) => !editable || shortcut.allowInEditable)
|
|
337
445
|
.filter((shortcut) => !event.repeat || shortcut.allowRepeat)
|
|
446
|
+
.filter((shortcut) => !isEmptyCollectionClear(shortcut, document, event))
|
|
338
447
|
.flatMap(parseShortcut);
|
|
339
448
|
if (candidates.length === 0) continue;
|
|
340
449
|
|
|
@@ -575,7 +684,10 @@ export function useShortcuts(
|
|
|
575
684
|
priority = 0,
|
|
576
685
|
sequenceTimeoutMs = 1500,
|
|
577
686
|
collectionOwnerId,
|
|
687
|
+
isAvailable,
|
|
578
688
|
} = options;
|
|
689
|
+
const availableRef = useRef(isAvailable);
|
|
690
|
+
availableRef.current = isAvailable;
|
|
579
691
|
|
|
580
692
|
useEffect(() => {
|
|
581
693
|
if (!registry || !enabled) return;
|
|
@@ -585,30 +697,24 @@ export function useShortcuts(
|
|
|
585
697
|
priority: SHORTCUT_SCOPE_PRIORITY[scope] + priority,
|
|
586
698
|
sequenceTimeoutMs,
|
|
587
699
|
collectionOwnerId,
|
|
588
|
-
|
|
700
|
+
isAvailable: () => availableRef.current?.() !== false,
|
|
701
|
+
getShortcuts: () => availableRef.current?.() === false ? [] : shortcutsRef.current,
|
|
589
702
|
});
|
|
590
703
|
}, [collectionOwnerId, enabled, priority, registry, scope, sequenceTimeoutMs]);
|
|
591
704
|
|
|
592
705
|
// Compatibility fallback for SDK components rendered outside a provider.
|
|
593
706
|
useEffect(() => {
|
|
594
707
|
if (registry || !enabled) return;
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
return;
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
};
|
|
609
|
-
document.addEventListener('keydown', handleKeyDown, true);
|
|
610
|
-
return () => document.removeEventListener('keydown', handleKeyDown, true);
|
|
611
|
-
}, [enabled, registry]);
|
|
708
|
+
return registerFallbackShortcut({
|
|
709
|
+
enabled,
|
|
710
|
+
scope,
|
|
711
|
+
priority: SHORTCUT_SCOPE_PRIORITY[scope] + priority,
|
|
712
|
+
sequenceTimeoutMs,
|
|
713
|
+
collectionOwnerId,
|
|
714
|
+
isAvailable: () => availableRef.current?.() !== false,
|
|
715
|
+
getShortcuts: () => shortcutsRef.current,
|
|
716
|
+
});
|
|
717
|
+
}, [collectionOwnerId, enabled, priority, registry, scope, sequenceTimeoutMs]);
|
|
612
718
|
}
|
|
613
719
|
|
|
614
720
|
export function shortcutDisplay(raw: string): string {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Visibility across light and Shadow DOM, without relying on layout (virtual rows may have no rect). */
|
|
2
|
+
export function isInteractionElementVisible(element: HTMLElement | null): boolean {
|
|
3
|
+
if (!element?.isConnected) return false;
|
|
4
|
+
let current: Element | null = element;
|
|
5
|
+
while (current) {
|
|
6
|
+
if (current.hasAttribute('hidden') || current.hasAttribute('inert') || current.getAttribute('aria-hidden') === 'true') return false;
|
|
7
|
+
const style = current.ownerDocument.defaultView?.getComputedStyle(current);
|
|
8
|
+
if (style?.display === 'none' || style?.visibility === 'hidden') return false;
|
|
9
|
+
const root: Node = current.getRootNode();
|
|
10
|
+
current = current.parentElement ?? ('host' in root ? (root as ShadowRoot).host : null);
|
|
11
|
+
}
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
@@ -29,7 +29,8 @@ export type {
|
|
|
29
29
|
SelectionMarqueeRect,
|
|
30
30
|
UseCollectionInteractionsOptions,
|
|
31
31
|
} from './hooks/useCollectionInteractions';
|
|
32
|
-
export { MultiSelectActionBar } from './components/MultiSelectActionBar';
|
|
32
|
+
export { MultiSelectActionBar, BULK_ACTION_LAYOUT_EVENT } from './components/MultiSelectActionBar';
|
|
33
|
+
export type { BulkActionLayoutDetail } from './components/MultiSelectActionBar';
|
|
33
34
|
export { MultiSelectCheckbox, MultiSelectCheckbox as SelectionCheckbox } from './components/MultiSelectCheckbox';
|
|
34
35
|
export { MultiSelectDragOverlay, MultiSelectDragOverlay as SelectionMarquee } from './components/MultiSelectDragOverlay';
|
|
35
36
|
export { ShortcutHints } from './components/ShortcutHints';
|
|
@@ -39,3 +40,6 @@ export type { MultiSelectDragOverlayProps } from './components/MultiSelectDragOv
|
|
|
39
40
|
export type { MultiSelectCheckboxProps as SelectionCheckboxProps } from './components/MultiSelectCheckbox';
|
|
40
41
|
export type { MultiSelectDragOverlayProps as SelectionMarqueeProps } from './components/MultiSelectDragOverlay';
|
|
41
42
|
export type { ShortcutHint, ShortcutHintsProps } from './components/ShortcutHints';
|
|
43
|
+
|
|
44
|
+
export { useLongPressSelection } from './hooks/useLongPressSelection';
|
|
45
|
+
export { isInteractionElementVisible } from './interactions/visibility';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/** The native row contract, shared by direct tools and scoped Space actions. */
|
|
2
|
+
export type NativeField = { property_id: string } | { property: string } | {
|
|
3
|
+
column: 'record_key' | 'id' | 'title' | 'created_at' | 'updated_at' | 'last_edited_time' | 'content_type' | 'file_type';
|
|
4
|
+
};
|
|
5
|
+
export type NativeFilter = { and: NativeFilter[] } | { or: NativeFilter[] } | {
|
|
6
|
+
field: NativeField;
|
|
7
|
+
op: 'equals' | 'not_equals' | 'in' | 'contains' | 'not_contains' | 'does_not_contain'
|
|
8
|
+
| 'starts_with' | 'ends_with' | 'is_empty' | 'is_not_empty' | 'greater_than' | 'less_than'
|
|
9
|
+
| 'greater_than_or_equal_to' | 'less_than_or_equal_to' | 'before' | 'after' | 'on_or_before' | 'on_or_after';
|
|
10
|
+
value?: unknown;
|
|
11
|
+
};
|
|
12
|
+
export interface NativeQuery {
|
|
13
|
+
include_content?: boolean;
|
|
14
|
+
include_archived?: boolean;
|
|
15
|
+
mode?: 'rows' | 'search' | 'count' | 'aggregate';
|
|
16
|
+
filter?: NativeFilter;
|
|
17
|
+
sorts?: Array<{ field: NativeField; direction: 'asc' | 'desc' }>;
|
|
18
|
+
search?: { text: string; fields: NativeField[] };
|
|
19
|
+
aggregate?: { op: 'sum' | 'avg' | 'min' | 'max'; field: NativeField };
|
|
20
|
+
page_size?: number;
|
|
21
|
+
offset?: number;
|
|
22
|
+
}
|
|
23
|
+
export interface NativeInsert {
|
|
24
|
+
columns?: { title?: string | null; icon?: string | null; cover?: string | null };
|
|
25
|
+
properties?: Record<string, unknown>;
|
|
26
|
+
body?: { blocks: Array<Record<string, unknown>> };
|
|
27
|
+
}
|
|
28
|
+
export interface NativeUpdate extends NativeInsert { record_key: string; expected_revision: number; }
|
|
29
|
+
export interface NativeDelete { record_key: string; expected_revision: number; }
|
|
30
|
+
export interface NativeMutationReceipt {
|
|
31
|
+
record_key: string;
|
|
32
|
+
id: string;
|
|
33
|
+
database_id: string;
|
|
34
|
+
revision: number;
|
|
35
|
+
schema_revision: number;
|
|
36
|
+
receipt_id: string;
|
|
37
|
+
operation: 'insert' | 'update' | 'delete';
|
|
38
|
+
replayed?: boolean;
|
|
39
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import React, { createContext, useContext, type Context, type ReactNode } from 'react';
|
|
3
|
+
import React, { createContext, useContext, useMemo, type Context, type ReactNode } from 'react';
|
|
4
4
|
import type { NotisRuntime } from './runtime';
|
|
5
5
|
import { ShortcutProvider } from './interactions/shortcuts';
|
|
6
|
+
import { guardSpaceRuntime } from './space';
|
|
6
7
|
|
|
7
8
|
const NOTIS_CONTEXT_SYMBOL = Symbol.for('notis.sdk.runtime_context');
|
|
8
9
|
|
|
@@ -24,13 +25,17 @@ const NotisContext = getNotisContext();
|
|
|
24
25
|
* Provides the Notis runtime context to all child components. The portal owns
|
|
25
26
|
* the runtime and injects it into the rendered app tree.
|
|
26
27
|
*/
|
|
27
|
-
export function NotisProvider({ children, runtime
|
|
28
|
+
export function NotisProvider({ children, runtime, onProtocolError, shortcutsAvailable }: {
|
|
29
|
+
children: ReactNode; runtime: NotisRuntime | null; onProtocolError?: (error: Error) => void;
|
|
30
|
+
shortcutsAvailable?: () => boolean;
|
|
31
|
+
}) {
|
|
32
|
+
const scopedRuntime = useMemo(() => guardSpaceRuntime(runtime, onProtocolError), [runtime, onProtocolError]);
|
|
28
33
|
if (runtime === undefined) {
|
|
29
34
|
throw new Error('NotisProvider requires an explicit runtime prop.');
|
|
30
35
|
}
|
|
31
36
|
return (
|
|
32
|
-
<NotisContext.Provider value={
|
|
33
|
-
<ShortcutProvider>{children}</ShortcutProvider>
|
|
37
|
+
<NotisContext.Provider value={scopedRuntime}>
|
|
38
|
+
<ShortcutProvider isAvailable={shortcutsAvailable}>{children}</ShortcutProvider>
|
|
34
39
|
</NotisContext.Provider>
|
|
35
40
|
);
|
|
36
41
|
}
|