@notis_ai/cli 0.2.0-beta.19.1 → 0.2.0-beta.196.1
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 +822 -135
- package/bin/check-runtime.js +15 -0
- package/bin/notis.js +2 -0
- package/config/notis_app_boundary_rules.json +50 -0
- package/config/notis_app_design_rules.json +135 -0
- package/config/portal-route-roots.json +48 -0
- package/dist/agent-hooks/notis-agent-hook.mjs +45587 -0
- package/dist/base-skills/notis-apps/SKILL.md +93 -0
- package/dist/base-skills/notis-apps/references/architecture.md +114 -0
- package/dist/base-skills/notis-apps/references/context.md +81 -0
- package/dist/base-skills/notis-apps/references/design.md +97 -0
- package/dist/base-skills/notis-apps/references/memory.md +58 -0
- package/dist/base-skills/notis-apps/references/reading.md +67 -0
- package/dist/base-skills/notis-apps/references/release.md +107 -0
- package/dist/base-skills/notis-apps/references/sdk.md +116 -0
- package/dist/base-skills/notis-apps/references/troubleshooting.md +24 -0
- package/dist/base-skills/notis-apps/references/views.md +142 -0
- package/dist/base-skills/notis-cli/SKILL.md +154 -0
- package/dist/base-skills/notis-cli/references/app-delivery.md +19 -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 +39 -0
- package/dist/base-skills/notis-query/SKILL.md +41 -0
- package/dist/base-skills/notis-query/references/database-discovery.md +60 -0
- package/dist/base-skills/notis-query/references/documents.md +63 -0
- package/dist/base-skills/notis-query/references/query.md +76 -0
- package/{template/packages/notis-sdk → dist/sdk}/package.json +14 -4
- package/dist/sdk/src/agentContext.ts +36 -0
- package/dist/sdk/src/bundleModule.ts +79 -0
- package/dist/sdk/src/components/Dialog.tsx +46 -0
- package/dist/sdk/src/components/DocumentEditor.tsx +106 -0
- package/dist/sdk/src/components/DocumentPage.tsx +141 -0
- package/dist/sdk/src/components/Markdown.tsx +60 -0
- package/dist/sdk/src/components/MarkdownEditor.tsx +121 -0
- package/dist/sdk/src/components/MultiSelectActionBar.tsx +285 -0
- package/dist/sdk/src/components/MultiSelectCheckbox.tsx +97 -0
- package/dist/sdk/src/components/MultiSelectDragOverlay.tsx +39 -0
- package/dist/sdk/src/components/NotisCommentBoundary.tsx +172 -0
- package/dist/sdk/src/components/NotisSelectionBoundary.tsx +59 -0
- package/dist/sdk/src/components/RecordComponents.tsx +33 -0
- package/dist/sdk/src/components/RenderChartData.tsx +19 -0
- package/dist/sdk/src/components/ShortcutHints.tsx +56 -0
- package/dist/sdk/src/components/Skeleton.tsx +24 -0
- package/dist/sdk/src/config.ts +239 -0
- package/dist/sdk/src/documents.ts +258 -0
- package/dist/sdk/src/hooks/useAction.ts +28 -0
- package/dist/sdk/src/hooks/useActiveResource.ts +19 -0
- package/dist/sdk/src/hooks/useAgentContext.ts +23 -0
- package/dist/sdk/src/hooks/useCloudComputer.ts +95 -0
- package/dist/sdk/src/hooks/useCloudComputerShell.ts +114 -0
- package/dist/sdk/src/hooks/useCollectionInteractions.ts +838 -0
- package/dist/sdk/src/hooks/useDatabaseSchema.ts +49 -0
- package/dist/sdk/src/hooks/useDatabaseSubscription.ts +76 -0
- package/dist/sdk/src/hooks/useDocument.ts +43 -0
- package/dist/sdk/src/hooks/useDocuments.ts +84 -0
- package/dist/sdk/src/hooks/useHandover.ts +78 -0
- package/dist/sdk/src/hooks/useLongPressSelection.ts +79 -0
- package/dist/sdk/src/hooks/useMultiSelect.ts +95 -0
- package/dist/sdk/src/hooks/useNativeDocumentBody.ts +50 -0
- package/dist/sdk/src/hooks/useNativeDocuments.ts +83 -0
- package/dist/sdk/src/hooks/useNotis.ts +42 -0
- package/dist/sdk/src/hooks/useNotisNavigation.ts +67 -0
- package/dist/sdk/src/hooks/useQuery.ts +74 -0
- package/dist/sdk/src/hooks/useTool.ts +65 -0
- package/dist/sdk/src/hooks/useToolQuery.ts +12 -0
- package/dist/sdk/src/hooks/useTopBarSearch.ts +81 -0
- package/dist/sdk/src/hooks/useUpsertDocument.ts +95 -0
- package/dist/sdk/src/hooks/useViewParams.ts +121 -0
- package/dist/sdk/src/hooks/useViewerReads.ts +129 -0
- package/dist/sdk/src/index.ts +200 -0
- package/dist/sdk/src/interactions/actions.ts +59 -0
- package/dist/sdk/src/interactions/shortcuts.tsx +740 -0
- package/dist/sdk/src/interactions/visibility.ts +13 -0
- package/dist/sdk/src/interactions.ts +45 -0
- package/dist/sdk/src/nativeData.ts +39 -0
- package/dist/sdk/src/presentation.tsx +82 -0
- package/dist/sdk/src/provider.tsx +49 -0
- package/dist/sdk/src/queryCache.ts +172 -0
- package/dist/sdk/src/queryErrors.ts +51 -0
- package/dist/sdk/src/runtime.ts +603 -0
- package/dist/sdk/src/space.ts +425 -0
- package/dist/sdk/src/styles.css +290 -0
- package/dist/sdk/src/tailwind.ts +66 -0
- package/dist/sdk/src/vite.ts +83 -0
- package/dist/skill-sync/index.js +2143 -0
- package/dist/skill-sync/index.js.map +7 -0
- package/dist/skill-sync-worker.mjs +3487 -0
- package/dist/space-harness/host.js +66 -0
- package/dist/view-renderer/dist/host.css +1 -0
- package/dist/view-renderer/dist/host.js +1042 -0
- package/dist/view-renderer/dist/theme.css +73 -0
- package/dist/view-renderer/src/dom.js +180 -0
- package/dist/view-renderer/src/errors.js +8 -0
- package/dist/view-renderer/src/host.tsx +173 -0
- package/dist/view-renderer/src/index.js +4 -0
- package/dist/view-renderer/src/pool.js +76 -0
- package/dist/view-renderer/src/remote-broker.js +59 -0
- package/dist/view-renderer/src/renderer.js +201 -0
- package/package.json +25 -8
- package/skills/notis-apps/cli.md +406 -0
- package/skills/notis-cli/AGENT_INSTRUCTIONS.md +39 -0
- package/skills/notis-onboarding/BRIEF.md +133 -0
- package/skills/notis-query/cli.md +39 -0
- package/src/agent-hook-entry.js +5 -0
- package/src/cli.js +294 -25
- package/src/command-specs/agents.js +392 -0
- package/src/command-specs/apps.js +1476 -203
- package/src/command-specs/auth.js +114 -137
- package/src/command-specs/diagnostics.js +730 -0
- package/src/command-specs/files.js +29 -0
- package/src/command-specs/handover.js +374 -0
- package/src/command-specs/helpers.js +86 -82
- package/src/command-specs/index.js +31 -6
- package/src/command-specs/meta.js +150 -18
- package/src/command-specs/onboarding.js +290 -0
- package/src/command-specs/profile.js +358 -0
- package/src/command-specs/skills.js +304 -0
- package/src/command-specs/smoke.js +386 -0
- package/src/command-specs/spaces.js +572 -0
- package/src/command-specs/tools.js +455 -139
- package/src/command-specs/views.js +69 -0
- package/src/runtime/agent-browser.js +700 -0
- package/src/runtime/agent-memory-state.js +126 -0
- package/src/runtime/agent-setup.js +383 -0
- package/src/runtime/app-boundary-validator.js +407 -0
- package/src/runtime/app-changelog.js +79 -0
- package/src/runtime/app-data-literals.js +82 -0
- package/src/runtime/app-platform.js +2752 -237
- package/src/runtime/app-registry-scaffolds.js +367 -0
- package/src/runtime/app-test-server.js +293 -0
- package/src/runtime/assets/store-screenshot-dark.png +0 -0
- package/src/runtime/auth-recovery.js +110 -0
- package/src/runtime/base-skills.d.ts +20 -0
- package/src/runtime/base-skills.js +167 -0
- package/src/runtime/channel.js +133 -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 +68 -0
- package/src/runtime/errors.js +1 -0
- package/src/runtime/git.js +233 -0
- package/src/runtime/login-listener.js +15 -0
- package/src/runtime/native-record-files.js +47 -0
- package/src/runtime/oauth.js +2622 -0
- package/src/runtime/output.js +37 -5
- package/src/runtime/ports.js +31 -0
- package/src/runtime/profiles.js +906 -55
- package/src/runtime/retired-sdk-files.js +20 -0
- package/src/runtime/screenshot-framing.js +85 -0
- package/src/runtime/skill-sync/cloud-client.ts +121 -0
- package/src/runtime/skill-sync/index.ts +905 -0
- package/src/runtime/skill-sync/local-scanner.ts +1259 -0
- package/src/runtime/skill-sync/native-identity.ts +102 -0
- package/src/runtime/skill-sync/native-plan.ts +59 -0
- package/src/runtime/skill-sync/symlink-manager.ts +433 -0
- package/src/runtime/skill-sync/sync-plan.ts +54 -0
- package/src/runtime/skill-sync/types.ts +157 -0
- package/src/runtime/skill-sync/write-cloud-skill.ts +50 -0
- package/src/runtime/skill-sync-service.js +109 -0
- package/src/runtime/source-root.js +32 -0
- package/src/runtime/space-collection-scaffold.js +66 -0
- package/src/runtime/space-database-structures.js +229 -0
- package/src/runtime/space-fixture-broker.js +50 -0
- package/src/runtime/space-lock.js +337 -0
- package/src/runtime/space-platform.js +105 -0
- package/src/runtime/space-source.js +281 -0
- package/src/runtime/space-test-server.js +327 -0
- package/src/runtime/space-verification.js +89 -0
- package/src/runtime/space-verify-browser-worker.js +51 -0
- package/src/runtime/space-view-manifest.js +254 -0
- package/src/runtime/store-screenshot.js +146 -0
- package/src/runtime/sync-skills.d.ts +37 -0
- package/src/runtime/sync-skills.js +231 -0
- package/src/runtime/telemetry.js +92 -0
- package/src/runtime/transport.js +331 -45
- package/src/runtime/view-render.js +49 -0
- package/src/skill-sync-worker-entry.js +2 -0
- package/src/skill-sync-worker.js +50 -0
- package/src/space-harness-host.ts +103 -0
- package/template/.harness/index.html.tmpl +435 -0
- package/template/CHANGELOG.md +5 -0
- package/template/app/globals.css +29 -3
- package/template/app/layout.tsx +6 -3
- package/template/app/page.tsx +49 -42
- package/template/components/page-heading.tsx +23 -0
- package/template/components/ui/badge.tsx +7 -4
- package/template/components/ui/button.tsx +1 -1
- package/template/components/ui/card.tsx +24 -11
- package/template/components/ui/native-select.tsx +24 -0
- package/template/notis.config.ts +24 -6
- package/template/package-lock.json +3642 -0
- package/template/package.json +19 -16
- package/template/postcss.config.mjs +1 -1
- package/template/tailwind.config.ts +1 -6
- package/template/tsconfig.json +1 -0
- package/src/command-specs/db.js +0 -163
- package/src/runtime/app-preview-server.js +0 -312
- package/template/packages/notis-sdk/src/config.ts +0 -48
- package/template/packages/notis-sdk/src/helpers.ts +0 -131
- package/template/packages/notis-sdk/src/hooks/useAppState.ts +0 -50
- package/template/packages/notis-sdk/src/hooks/useCollectionItem.ts +0 -58
- package/template/packages/notis-sdk/src/hooks/useDatabase.ts +0 -87
- package/template/packages/notis-sdk/src/hooks/useDocument.ts +0 -61
- package/template/packages/notis-sdk/src/hooks/useNotis.ts +0 -31
- package/template/packages/notis-sdk/src/hooks/useNotisNavigation.ts +0 -49
- package/template/packages/notis-sdk/src/hooks/useTool.ts +0 -49
- package/template/packages/notis-sdk/src/hooks/useUpsertDocument.ts +0 -57
- package/template/packages/notis-sdk/src/index.ts +0 -47
- package/template/packages/notis-sdk/src/provider.tsx +0 -44
- package/template/packages/notis-sdk/src/runtime.ts +0 -159
- package/template/packages/notis-sdk/src/styles.css +0 -123
- package/template/packages/notis-sdk/src/vite.ts +0 -54
- package/template/packages/notis-sdk/tsconfig.json +0 -15
- /package/{template/packages/notis-sdk → dist/sdk}/src/hooks/useBackend.ts +0 -0
- /package/{template/packages/notis-sdk → dist/sdk}/src/hooks/useTools.ts +0 -0
- /package/{template/packages/notis-sdk → dist/sdk}/src/ui.ts +0 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
## Supporting commands
|
|
2
|
+
|
|
3
|
+
- `npx --package @notis_ai/cli@latest -- notis whoami` — confirm which account and endpoint a command will target
|
|
4
|
+
- `npx --package @notis_ai/cli@latest -- notis doctor` — verify CLI config, auth, routing, and API reachability before relying on the CLI
|
|
5
|
+
- `npx --package @notis_ai/cli@latest -- notis describe <command...>` — get the exact command contract for first-class CLI commands
|
|
6
|
+
|
|
7
|
+
## Troubleshooting
|
|
8
|
+
|
|
9
|
+
### CLI returns `auth_expired` or `auth_missing`
|
|
10
|
+
|
|
11
|
+
The profile's browser authorization has lapsed or was never granted. Run
|
|
12
|
+
`notis login` (add `--profile <name>` when the failing profile is not the
|
|
13
|
+
active one) and have the user approve the browser prompt. In JSON/agent mode
|
|
14
|
+
the first hint is the exact command to run. Do not copy refresh tokens into
|
|
15
|
+
commands or try to mint a credential yourself.
|
|
16
|
+
|
|
17
|
+
If the profile is a `dev-*` one, the fix is to restart `./dev.sh` in the
|
|
18
|
+
workspace it belongs to, or to switch to a real account profile.
|
|
19
|
+
|
|
20
|
+
### Deploy fails with "network_error" or "fetch failed"
|
|
21
|
+
|
|
22
|
+
Run `notis doctor` to verify the effective profile and endpoint. Read back the exact app ID,
|
|
23
|
+
version and release state before retrying. An uncertain network response is not proof of rollback.
|
|
24
|
+
There is no direct storage deployment path. Repair authentication when needed without changing the
|
|
25
|
+
intended profile, then reconcile the previous outcome before starting a new release.
|
|
26
|
+
|
|
27
|
+
Localhost backends are a Notis-developer test lane owned by `./dev.sh` and its lease-backed profile.
|
|
28
|
+
Do not silently switch between that lane and a live account.
|
|
29
|
+
|
|
30
|
+
### Health or tool-roundtrip errors
|
|
31
|
+
|
|
32
|
+
Local scaffold/build and stub verification can run without an API connection (dependencies and
|
|
33
|
+
browser tooling must already be available). `link`, `pull`, `create`, `list`, `deploy`, live verification
|
|
34
|
+
and Store operations require the intended backend. Never bypass it.
|
|
35
|
+
|
|
36
|
+
### Stale bundle in Portal after an update
|
|
37
|
+
|
|
38
|
+
Every successful release gets a new integer deployment version. Read back that version, then use
|
|
39
|
+
normal refresh/navigation to load it. Never overwrite or decrement an existing deployment version.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: notis-query
|
|
3
|
+
description: Discover native Notis databases and query their records with exact structured filters, current Space authority and view-qualified links.
|
|
4
|
+
feature_flag: spaces
|
|
5
|
+
mcp_resource: true
|
|
6
|
+
mcp_tool_patterns: ["LOCAL_NOTIS_DATABASE_*"]
|
|
7
|
+
mcp_references: ["references/database-discovery.md", "references/documents.md", "references/query.md"]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Native database queries
|
|
11
|
+
|
|
12
|
+
Use structured native queries for filtering, sorting, counts, aggregates and
|
|
13
|
+
pagination. Use memory for semantic discovery; use view rendering for what a page
|
|
14
|
+
shows. The live tool `inputSchema` is authoritative.
|
|
15
|
+
|
|
16
|
+
1. Discover the database and inspect its schema, property descriptions, relation
|
|
17
|
+
targets and option IDs with `LOCAL_NOTIS_DATABASE_GET_DATABASE`.
|
|
18
|
+
2. Keep its exact returned native `target`, schema revision and available operations.
|
|
19
|
+
Every database lives in a Space; failed access never falls back to another owner.
|
|
20
|
+
3. Use `LOCAL_NOTIS_DATABASE_QUERY` with `protocol: 1`, that `target` and direct
|
|
21
|
+
`input`. Build filters from actual field types/options. Page until the returned
|
|
22
|
+
continuation ends; one page is not the whole database.
|
|
23
|
+
4. For a known record or view link, follow [Reading records](references/documents.md)
|
|
24
|
+
instead of running broad discovery. View links come from write results or
|
|
25
|
+
`LOCAL_NOTIS_FIND_VIEWS`; sensitive or stale evidence needs a fresh render.
|
|
26
|
+
5. Writes use discovered native tools with current schema/record revisions and a
|
|
27
|
+
stable request ID. Dry-run first and read back the committed state. A missing
|
|
28
|
+
view link is not a failed write and does not justify repeating it.
|
|
29
|
+
|
|
30
|
+
In authored Space code use `useShown` or declared actions, not account-wide tool
|
|
31
|
+
calls. Load `notis-apps` for [Space authoring](../notis-apps/references/views.md);
|
|
32
|
+
its hosted reference is `notis://docs/notis-apps/references/views.md`.
|
|
33
|
+
|
|
34
|
+
## Guides
|
|
35
|
+
|
|
36
|
+
- [Database discovery and creation](references/database-discovery.md)
|
|
37
|
+
- [Structured native query grammar](references/query.md)
|
|
38
|
+
- [Reading, updating and citing records](references/documents.md)
|
|
39
|
+
|
|
40
|
+
Hosted MCP exposes these references under
|
|
41
|
+
`notis://docs/notis-query/references/<file>.md`; load only the relevant guide.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Database discovery and creation
|
|
2
|
+
|
|
3
|
+
Use `LOCAL_NOTIS_DATABASE_LIST_DATABASES` to find accessible databases, then
|
|
4
|
+
`LOCAL_NOTIS_DATABASE_GET_DATABASE` for schema and current native authority. A
|
|
5
|
+
name or slug helps discovery; use the returned exact identity for further work.
|
|
6
|
+
A slug shared by several databases you can reach answers `database_slug_ambiguous`
|
|
7
|
+
with each `database_id` and its Spaces: choose with the user, then keep that ID.
|
|
8
|
+
Inspect property descriptions, types, option IDs and relation targets before
|
|
9
|
+
filtering or writing. Keep the returned target intact, including binding pins.
|
|
10
|
+
|
|
11
|
+
## Database creation has two supported routes
|
|
12
|
+
|
|
13
|
+
**Native tool.** Discover/inspect `LOCAL_NOTIS_DATABASE_UPSERT_DATABASE`. With
|
|
14
|
+
`protocol: 1`, creation requires a stable `request_id`, `links.add` with at least
|
|
15
|
+
one Space and its current revision, and `main_view: {space_id, param}` naming a
|
|
16
|
+
Space linked in that call. The database and link are created together. That
|
|
17
|
+
Space's next source deploy must declare the record param with `main: true`.
|
|
18
|
+
Until then, bare record links open the Space and write results carry `view_warning`.
|
|
19
|
+
|
|
20
|
+
**Space source.** Declare `resources.<alias> = {kind: 'database', create: {name,
|
|
21
|
+
schema, starterRows?}}` and a main record param for that alias in the same view.
|
|
22
|
+
Promotion creates the database/link/main/source atomically. Build/verify does not
|
|
23
|
+
create anything. See [View authoring](../../notis-apps/references/views.md).
|
|
24
|
+
|
|
25
|
+
Every database keeps a main view and at least one Space. To move it, add the new
|
|
26
|
+
link and remove the old one in a single resource mutation; unlinking the final
|
|
27
|
+
Space is refused with `database_last_space`. Moving into another Space tree also
|
|
28
|
+
sets `main_view` in the added Space and needs an Editor of both Spaces; the main
|
|
29
|
+
view stays pending (`view_warning`) until that Space deploys it. Reuse the current
|
|
30
|
+
IDs instead of making an equivalent duplicate. Explain who gains or loses access
|
|
31
|
+
first ([consequences](../../notis-apps/references/architecture.md#consequences-to-explain-before-acting)).
|
|
32
|
+
|
|
33
|
+
## Schema edits
|
|
34
|
+
|
|
35
|
+
Use the exact `target`, `schema_revision` and stable `request_id` from discovery.
|
|
36
|
+
Only whole-database Editors change shared definitions. `rename` preserves a
|
|
37
|
+
property's ID and values; remove-plus-add is not a rename. Merge choice options
|
|
38
|
+
by default; replacing the complete option set can make old stored values unreadable.
|
|
39
|
+
|
|
40
|
+
A relation must specify the exact target database. For Space authoring, also
|
|
41
|
+
resolve its same-Space relation binding when required; a description does not
|
|
42
|
+
grant access or define the target. Native writes validate the related record's
|
|
43
|
+
identity and visibility inside that binding.
|
|
44
|
+
|
|
45
|
+
Source `create` declarations apply only once. Later changes use the native schema
|
|
46
|
+
tool; `spaces pull` refreshes source from the live schema instead of reapplying an
|
|
47
|
+
old creation schema. Keep schemas, source and tests aligned after intentional edits.
|
|
48
|
+
|
|
49
|
+
## Deleting databases
|
|
50
|
+
|
|
51
|
+
`LOCAL_NOTIS_DATABASE_DELETE_DATABASE` permanently deletes a database and every
|
|
52
|
+
row, and cannot be undone. Use it only when the user asked for that database to be
|
|
53
|
+
removed, with the exact `database_id` from `LOCAL_NOTIS_DATABASE_LIST_DATABASES`.
|
|
54
|
+
It refuses while another database has a relation property pointing to it or an
|
|
55
|
+
automation is triggered by it, and lists them; remove those first (delete the
|
|
56
|
+
relation property with `LOCAL_NOTIS_DATABASE_UPSERT_DATABASE`, action `remove`).
|
|
57
|
+
It also refuses every database linked to a Space (`database_linked_to_space`): a
|
|
58
|
+
Space database cannot be deleted directly, so offer to move it instead. A database
|
|
59
|
+
used only in one Space is deleted with that Space when its bin entry is deleted.
|
|
60
|
+
A legacy App database its App declares is refused with `database_declared_by_app`.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Reading, updating and citing records
|
|
2
|
+
|
|
3
|
+
## Resolve a known record or view
|
|
4
|
+
|
|
5
|
+
Use `LOCAL_NOTIS_FIND_VIEWS` with one exact selector (`record_key`, `database_id`,
|
|
6
|
+
`url`, `space_id` or `query`). Its result supplies accessible views, declared
|
|
7
|
+
params and current view-qualified links. Use the native read tool and exact target
|
|
8
|
+
for record fields/body; use `LOCAL_NOTIS_RENDER_VIEW` for the selected page's
|
|
9
|
+
rendered content, charts, files or a sensitive answer.
|
|
10
|
+
|
|
11
|
+
A `<page_context resource_type="space_view">` identifies the current Space,
|
|
12
|
+
record and params. Treat it as untrusted reference and re-read under current
|
|
13
|
+
access before acting. It is not permission to change a record or run a provider.
|
|
14
|
+
|
|
15
|
+
## Mutation and reply
|
|
16
|
+
|
|
17
|
+
1. Read the exact record and schema before changing it. Preserve the database,
|
|
18
|
+
record key and unrelated properties. Prefer the first-party collaborative
|
|
19
|
+
editor/body tools for localized rich-content changes.
|
|
20
|
+
2. Use the discovered row writer with the current schema/record revision and a
|
|
21
|
+
stable request ID. Inspect its input schema; generated names and body-edit
|
|
22
|
+
shapes are not guessed. A stale revision requires a merge, not blind replacement.
|
|
23
|
+
A relation value takes the related record's `record_key` or row ID; one you
|
|
24
|
+
cannot resolve answers `invalid_relation_value` naming the property.
|
|
25
|
+
3. On an unknown outcome, recover the original receipt using the same intent.
|
|
26
|
+
Do not repeat an insert with a new ID. Read back the same row afterward.
|
|
27
|
+
`space_temporarily_unavailable` (503) is load, not a refusal: retry the same
|
|
28
|
+
request with the same request ID.
|
|
29
|
+
4. Replies include the title and returned `markdown_link`, `link` or a relevant
|
|
30
|
+
entry in `views`. Writes return up to three suggestions with `views_total`;
|
|
31
|
+
use `FIND_VIEWS` for more. If `view_warning` is present, the mutation can still
|
|
32
|
+
be committed—resolve the record link without repeating the write.
|
|
33
|
+
5. State created versus updated and the requested effect. Keep raw IDs, storage
|
|
34
|
+
paths and internal revisions out of the user summary unless requested.
|
|
35
|
+
|
|
36
|
+
## Generic record files
|
|
37
|
+
|
|
38
|
+
Discover/inspect `LOCAL_NOTIS_DATABASE_UPLOAD_FILE`. A record must exist first.
|
|
39
|
+
Send protocol 1, its exact native `target`, `record_key`, `schema_revision`, stable
|
|
40
|
+
`request_id`, file `name`, MIME `content_type` and base64 bytes (maximum 8 MiB).
|
|
41
|
+
Only a current native Editor with read and write access can upload; no actor,
|
|
42
|
+
owner, caller URL or storage path is accepted.
|
|
43
|
+
|
|
44
|
+
Verify the returned `sha256`, `size_bytes`, record key and request ID. Attach its
|
|
45
|
+
`file: {name, url}` through an ordinary revision-checked native row update with a
|
|
46
|
+
separate stable request ID. Upload success alone is not a saved attachment. An
|
|
47
|
+
uncertain upload retries only the identical byte/intent request; it never replaces
|
|
48
|
+
an existing shared retry object. Preserve previous files unless the user asked
|
|
49
|
+
for their removal.
|
|
50
|
+
|
|
51
|
+
For HTML pages, use the installed HTML Space's linked Save HTML Skill, which owns
|
|
52
|
+
its complete save/update/sharing workflow. Suggest installing that Space when
|
|
53
|
+
absent. Deliver its record view link, not the raw file storage location.
|
|
54
|
+
|
|
55
|
+
## Fresh memory evidence
|
|
56
|
+
|
|
57
|
+
A native memory hit is a pointer with source/link, captured time, content type
|
|
58
|
+
and freshness. Records carry captured/current revisions; view snapshots may
|
|
59
|
+
have unknown data freshness or withheld historical content. When stale/unknown,
|
|
60
|
+
or for sensitive values such as identity numbers or amounts, render the returned
|
|
61
|
+
`render` link and verify the answer from current Markdown or image. Cite the fresh
|
|
62
|
+
view-qualified link. An old excerpt is not enough and an access denial is final
|
|
63
|
+
for that path.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Structured native query grammar
|
|
2
|
+
|
|
3
|
+
Discover and inspect `LOCAL_NOTIS_DATABASE_QUERY`; use its current input schema.
|
|
4
|
+
For native Spaces use `protocol: 1`, the exact discovery `target`, and `input`.
|
|
5
|
+
The native query lives directly inside `input`, without another request/query wrapper.
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
{
|
|
9
|
+
"protocol": 1,
|
|
10
|
+
"target": {"space_id": "<exact-discovered-space-id>", "binding": "<exact-discovered-binding-id>"},
|
|
11
|
+
"input": {
|
|
12
|
+
"mode": "rows",
|
|
13
|
+
"filter": {"field": {"property": "Status"}, "op": "equals", "value": "Done"},
|
|
14
|
+
"sorts": [{"field": {"column": "updated_at"}, "direction": "desc"}],
|
|
15
|
+
"page_size": 20,
|
|
16
|
+
"offset": 0,
|
|
17
|
+
"include_content": false
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Use the complete Space binding target returned by schema discovery. Every
|
|
23
|
+
database lives in a Space: a bare `database_id` gets `resolved_target` from
|
|
24
|
+
`LOCAL_NOTIS_DATABASE_GET_DATABASE`, and a bare-id query answers
|
|
25
|
+
`database_in_spaces` with its links. Never remove its record context or substitute
|
|
26
|
+
another database/owner after a denial. `action_id`, when present, retains the
|
|
27
|
+
saved action's narrower authority rather than granting direct Editor access.
|
|
28
|
+
|
|
29
|
+
## Fields and filters
|
|
30
|
+
|
|
31
|
+
Choose one field reference: `{property_id}`, an unambiguous `{property}` label,
|
|
32
|
+
or a supported `{column}` such as `record_key`, `id`, `title`, `created_at`,
|
|
33
|
+
`updated_at`, `last_edited_time`, `content_type`, `file_type`. Current property
|
|
34
|
+
names take precedence; use stable IDs when names are ambiguous.
|
|
35
|
+
|
|
36
|
+
A condition is `{field, op, value}`. Empty checks (`is_empty`, `is_not_empty`)
|
|
37
|
+
omit `value`. Combine with `{and: [conditions]}` or `{or: [conditions]}`; groups
|
|
38
|
+
must be nonempty. Invalid conditions fail—they never become an unrestricted query.
|
|
39
|
+
|
|
40
|
+
| Type | Operators |
|
|
41
|
+
| --- | --- |
|
|
42
|
+
| Record identity | `equals`, `not_equals`, `in` |
|
|
43
|
+
| Text/title/URL | `equals`, `not_equals`, `contains`, `not_contains`, `starts_with`, `ends_with`, empty checks |
|
|
44
|
+
| Number | `equals`, `not_equals`, `greater_than`, `less_than`, inclusive variants, empty checks |
|
|
45
|
+
| Checkbox | `equals`, `not_equals` with real booleans |
|
|
46
|
+
| Select/status | `equals`, `not_equals`, empty checks; use a current option ID or unambiguous label |
|
|
47
|
+
| Multi-select/relation | `contains`, `does_not_contain`, empty checks |
|
|
48
|
+
| Date | `equals`, `before`, `after`, `on_or_before`, `on_or_after`, empty checks; use ISO dates/timestamps |
|
|
49
|
+
|
|
50
|
+
A relation filter uses the related record identity returned by its native read,
|
|
51
|
+
not a guessed title. Do not encode a relation as a scalar text equality. Avoid
|
|
52
|
+
assuming every application uses the same status labels or Inbox semantics.
|
|
53
|
+
|
|
54
|
+
## Pagination and projection
|
|
55
|
+
|
|
56
|
+
Use bounded pages (1–500) and the returned continuation/offset until complete.
|
|
57
|
+
Native row results expose `rows`, stable `record_key`, revision and schema
|
|
58
|
+
metadata; do not assume the older `documents` result shape. Sorts use
|
|
59
|
+
`{field, direction: 'asc'|'desc'}`. Multi-select/relation fields cannot be sorted.
|
|
60
|
+
|
|
61
|
+
Use `include_content: false` for lists. Include bodies only when needed.
|
|
62
|
+
Archived rows are excluded unless `include_archived: true`; binned/inaccessible
|
|
63
|
+
rows remain excluded by authority. Do not turn a missing page into a zero count.
|
|
64
|
+
|
|
65
|
+
## Count, aggregate and search
|
|
66
|
+
|
|
67
|
+
- `mode: 'count'` applies the same authorized filter and returns its count.
|
|
68
|
+
- `mode: 'aggregate'` adds `aggregate: {op: 'sum'|'avg'|'min'|'max', field}` over
|
|
69
|
+
a numeric field. It is computed within the same native scope.
|
|
70
|
+
- `mode: 'search'` adds `search: {text, fields: [field, ...]}` over supported text
|
|
71
|
+
fields; this is structured native search, not semantic memory retrieval.
|
|
72
|
+
- For one current body, use `input: {mode: 'document_body', record_key}` with the
|
|
73
|
+
exact body-capable target. The response includes Markdown and current revisions.
|
|
74
|
+
|
|
75
|
+
For visual dashboards or a link's selected params, resolve/render the view rather
|
|
76
|
+
than guessing that an independent query recreates its presentation or period.
|
|
@@ -1,19 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@notis/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": "./src/index.ts",
|
|
8
|
+
"./interactions": "./src/interactions.ts",
|
|
8
9
|
"./config": "./src/config.ts",
|
|
9
10
|
"./vite": "./src/vite.ts",
|
|
10
11
|
"./ui": "./src/ui.ts",
|
|
11
12
|
"./styles.css": "./src/styles.css"
|
|
12
13
|
},
|
|
13
14
|
"files": [
|
|
14
|
-
"src"
|
|
15
|
-
"template"
|
|
15
|
+
"src"
|
|
16
16
|
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"type-check": "tsc --noEmit"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"react-markdown": "^10.1.0",
|
|
22
|
+
"remark-gfm": "^4.0.1"
|
|
23
|
+
},
|
|
17
24
|
"peerDependencies": {
|
|
18
25
|
"react": ">=18.0.0",
|
|
19
26
|
"react-dom": ">=18.0.0",
|
|
@@ -21,6 +28,9 @@
|
|
|
21
28
|
"@vitejs/plugin-react": ">=4.0.0"
|
|
22
29
|
},
|
|
23
30
|
"devDependencies": {
|
|
24
|
-
"
|
|
31
|
+
"@types/node": "^20.0.0",
|
|
32
|
+
"@types/react": "^19.2.18",
|
|
33
|
+
"@types/react-dom": "^19.2.7",
|
|
34
|
+
"typescript": "^7.0.2"
|
|
25
35
|
}
|
|
26
36
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** Serializable context shared with the agent. Apps own annotation storage. */
|
|
2
|
+
export interface ContextAttachment {
|
|
3
|
+
/** Durable HTTPS URL supplied by the app. Sent through the host media pipeline. */
|
|
4
|
+
url: string;
|
|
5
|
+
name: string;
|
|
6
|
+
mimeType: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface AgentContextContent {
|
|
10
|
+
title?: string;
|
|
11
|
+
/** Emoji, Phosphor icon name, or HTTPS image URL. Never executable markup. */
|
|
12
|
+
icon?: string;
|
|
13
|
+
/** An app-defined kind, not a closed list of annotation types. */
|
|
14
|
+
kind?: string;
|
|
15
|
+
text?: string;
|
|
16
|
+
comment?: string;
|
|
17
|
+
preview?: { format: 'text' | 'markdown'; content: string };
|
|
18
|
+
/** Any JSON-serializable value: coordinates, chart state, loaded records, etc. */
|
|
19
|
+
data?: unknown;
|
|
20
|
+
attachments?: ContextAttachment[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface AgentContextItem extends AgentContextContent {
|
|
24
|
+
/** Stable within this app. The host scopes IDs to the originating app. */
|
|
25
|
+
id: string;
|
|
26
|
+
resource?: import('./runtime').ContextResource | null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Host-stamped origin. Apps describe resources; the host identifies the app/view. */
|
|
30
|
+
export interface AgentContextSource {
|
|
31
|
+
appId: string;
|
|
32
|
+
appName: string;
|
|
33
|
+
viewSlug: string;
|
|
34
|
+
viewName: string;
|
|
35
|
+
viewUrl?: string | null;
|
|
36
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/** Framework-neutral ESM loading shared by Portal and credentialless verification. */
|
|
2
|
+
import type React from 'react';
|
|
3
|
+
|
|
4
|
+
export function rewriteReactImports(source: string): string {
|
|
5
|
+
// Create inline data URLs that re-export from window globals.
|
|
6
|
+
// This avoids the "Cannot find module" error with blob URL imports.
|
|
7
|
+
const reactShim = `data:text/javascript,${encodeURIComponent(
|
|
8
|
+
'const R = window.React; export default R; export const { createContext, createRef, useState, useEffect, useContext, useCallback, useMemo, useRef, useReducer, useLayoutEffect, useId, memo, forwardRef, createElement, Fragment, Children, cloneElement, isValidElement, lazy, Suspense, startTransition, useTransition, useDeferredValue, useSyncExternalStore, useInsertionEffect, useDebugValue, useImperativeHandle, Component, PureComponent } = R;'
|
|
9
|
+
)}`;
|
|
10
|
+
const jsxShim = `data:text/javascript,${encodeURIComponent(
|
|
11
|
+
'const J = window.__ReactJSXRuntime || {}; const R = window.React; export const jsx = J.jsx || R.createElement; export const jsxs = J.jsxs || R.createElement; export const jsxDEV = J.jsxDEV || R.createElement; export const Fragment = J.Fragment || R.Fragment;'
|
|
12
|
+
)}`;
|
|
13
|
+
const reactDomShim = `data:text/javascript,${encodeURIComponent(
|
|
14
|
+
'const RD = window.ReactDOM; export default RD; export const { createPortal, flushSync, unstable_batchedUpdates } = RD;'
|
|
15
|
+
)}`;
|
|
16
|
+
const reactDomClientShim = `data:text/javascript,${encodeURIComponent(
|
|
17
|
+
'const RDC = window.ReactDOMClient; export default RDC; export const { createRoot, hydrateRoot } = RDC;'
|
|
18
|
+
)}`;
|
|
19
|
+
|
|
20
|
+
let result = source;
|
|
21
|
+
// Replace bare side-effect imports
|
|
22
|
+
result = result.replace(/import\s*["']react-dom\/client["'];?/g, `import "${reactDomClientShim}";`);
|
|
23
|
+
result = result.replace(/import\s*["']react-dom["'];?/g, `import "${reactDomShim}";`);
|
|
24
|
+
result = result.replace(/import\s*["']react\/jsx-runtime["'];?/g, `import "${jsxShim}";`);
|
|
25
|
+
result = result.replace(/import\s*["']react\/jsx-dev-runtime["'];?/g, `import "${jsxShim}";`);
|
|
26
|
+
result = result.replace(/import\s*["']react["'];?/g, `import "${reactShim}";`);
|
|
27
|
+
|
|
28
|
+
// Replace import statements with data URL shims
|
|
29
|
+
// Handle: import { ... } from "react/jsx-runtime"
|
|
30
|
+
result = result.replace(/from\s*["']react\/jsx-runtime["']/g, `from "${jsxShim}"`);
|
|
31
|
+
result = result.replace(/from\s*["']react\/jsx-dev-runtime["']/g, `from "${jsxShim}"`);
|
|
32
|
+
// Handle react-dom/client before react-dom
|
|
33
|
+
result = result.replace(/from\s*["']react-dom\/client["']/g, `from "${reactDomClientShim}"`);
|
|
34
|
+
// Handle: import ... from "react-dom"
|
|
35
|
+
result = result.replace(/from\s*["']react-dom["']/g, `from "${reactDomShim}"`);
|
|
36
|
+
// Handle: import ... from "react" (must come after react-dom and react/jsx-runtime)
|
|
37
|
+
result = result.replace(/from\s*["']react["']/g, `from "${reactShim}"`);
|
|
38
|
+
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function resolveBundleRouteExport(
|
|
43
|
+
moduleExports: Record<string, React.ComponentType>,
|
|
44
|
+
exportCandidates: string[],
|
|
45
|
+
strict = false,
|
|
46
|
+
): { name: string; component: React.ComponentType } | null {
|
|
47
|
+
const namedExport = exportCandidates.find((name) => Boolean(moduleExports[name]));
|
|
48
|
+
if (namedExport) {
|
|
49
|
+
return { name: namedExport, component: moduleExports[namedExport] };
|
|
50
|
+
}
|
|
51
|
+
if (strict) return null;
|
|
52
|
+
if (moduleExports.default) {
|
|
53
|
+
return { name: 'default', component: moduleExports.default };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Vite may minify an entry export even though the manifest still carries
|
|
57
|
+
// its source name. A bundle with one non-shell component is unambiguous, so
|
|
58
|
+
// keep Dev usable instead of showing "View not available" for that route.
|
|
59
|
+
const componentExports = Object.entries(moduleExports).filter(
|
|
60
|
+
([name, value]) => name !== '__AppShell' && Boolean(value),
|
|
61
|
+
);
|
|
62
|
+
if (componentExports.length !== 1) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
const [name, component] = componentExports[0];
|
|
66
|
+
return { name, component };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export async function importRewrittenBundleSource(source: string): Promise<Record<string, React.ComponentType>> {
|
|
70
|
+
const blob = new Blob([source], { type: 'text/javascript' });
|
|
71
|
+
const url = URL.createObjectURL(blob);
|
|
72
|
+
try {
|
|
73
|
+
return await import(/* webpackIgnore: true */ url) as Record<string, React.ComponentType>;
|
|
74
|
+
} finally { URL.revokeObjectURL(url); }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function importBundleSource(source: string) {
|
|
78
|
+
return importRewrittenBundleSource(rewriteReactImports(source));
|
|
79
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React, { useEffect, useId, useRef, type ReactNode } from 'react';
|
|
4
|
+
|
|
5
|
+
export interface DialogProps {
|
|
6
|
+
open: boolean;
|
|
7
|
+
/** Request dismissal. Keep open unchanged to prevent dismissal while saving. */
|
|
8
|
+
onClose: () => void;
|
|
9
|
+
title: ReactNode;
|
|
10
|
+
description?: ReactNode;
|
|
11
|
+
role?: 'dialog' | 'alertdialog';
|
|
12
|
+
children: ReactNode;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Native top-layer dialog; content and actions remain app-owned. */
|
|
16
|
+
export function Dialog({ open, onClose, title, description, role = 'dialog', children }: DialogProps) {
|
|
17
|
+
const ref = useRef<HTMLDialogElement>(null);
|
|
18
|
+
const titleId = useId();
|
|
19
|
+
const descriptionId = useId();
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (!open) return;
|
|
23
|
+
const dialog = ref.current!;
|
|
24
|
+
dialog.showModal();
|
|
25
|
+
return () => dialog.close();
|
|
26
|
+
}, [open]);
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<dialog
|
|
30
|
+
ref={ref}
|
|
31
|
+
className="notis-dialog"
|
|
32
|
+
role={role}
|
|
33
|
+
aria-labelledby={titleId}
|
|
34
|
+
aria-describedby={description == null ? undefined : descriptionId}
|
|
35
|
+
onCancel={(event) => {
|
|
36
|
+
event.preventDefault();
|
|
37
|
+
onClose();
|
|
38
|
+
}}
|
|
39
|
+
onKeyDown={(event) => event.stopPropagation()}
|
|
40
|
+
>
|
|
41
|
+
<h2 id={titleId} className="notis-dialog-title">{title}</h2>
|
|
42
|
+
{description != null && <p id={descriptionId} className="notis-dialog-description">{description}</p>}
|
|
43
|
+
{children}
|
|
44
|
+
</dialog>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { CSSProperties, ReactElement } from 'react';
|
|
4
|
+
import { useNotisRuntime } from '../provider';
|
|
5
|
+
import { useDocument } from '../hooks/useDocument';
|
|
6
|
+
import { Markdown } from './Markdown';
|
|
7
|
+
import { RecordComponentUnavailable } from './RecordComponents';
|
|
8
|
+
import type { NotisDocumentEditorProps } from '../runtime';
|
|
9
|
+
|
|
10
|
+
const fallbackNoticeStyle: CSSProperties = {
|
|
11
|
+
marginTop: '0.75rem',
|
|
12
|
+
fontSize: '12px',
|
|
13
|
+
color: 'hsl(var(--muted-foreground))',
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const fallbackFrameStyle: CSSProperties = {
|
|
17
|
+
borderRadius: '0.5rem',
|
|
18
|
+
border: '1px dashed hsl(var(--border))',
|
|
19
|
+
padding: '1rem',
|
|
20
|
+
fontSize: '13px',
|
|
21
|
+
color: 'hsl(var(--muted-foreground))',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const fallbackTitleStyle: CSSProperties = {
|
|
25
|
+
margin: '0 0 0.75rem',
|
|
26
|
+
fontSize: '1.5rem',
|
|
27
|
+
fontWeight: 700,
|
|
28
|
+
letterSpacing: '-0.01em',
|
|
29
|
+
color: 'hsl(var(--foreground))',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Read-only stand-in used when no host editor is available (standalone dev
|
|
34
|
+
* harness, snapshots). Markdown content renders via `Markdown`; other content
|
|
35
|
+
* types get a typed placeholder.
|
|
36
|
+
*/
|
|
37
|
+
function DocumentEditorFallback({ documentId, variant = 'full', className }: NotisDocumentEditorProps) {
|
|
38
|
+
const runtime = useNotisRuntime();
|
|
39
|
+
const { document, loading, error } = useDocument(documentId ?? null);
|
|
40
|
+
// Listing screenshots should show the app as it looks in the portal, so the
|
|
41
|
+
// harness-only editing notice stays out of scenario captures.
|
|
42
|
+
const isScreenshot = Boolean(runtime?.context?.screenshotScenario);
|
|
43
|
+
|
|
44
|
+
if (loading) {
|
|
45
|
+
return <div className={className} style={fallbackFrameStyle}>Loading document…</div>;
|
|
46
|
+
}
|
|
47
|
+
if (error || !document) {
|
|
48
|
+
return (
|
|
49
|
+
<div className={className} style={fallbackFrameStyle}>
|
|
50
|
+
{error?.message ?? 'Document not found.'}
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const isMarkdown = document.contentType === 'markdown' || !document.contentType;
|
|
56
|
+
return (
|
|
57
|
+
<div className={className}>
|
|
58
|
+
{variant === 'full' ? <h1 style={fallbackTitleStyle}>{document.title}</h1> : null}
|
|
59
|
+
{isMarkdown ? (
|
|
60
|
+
<>
|
|
61
|
+
<Markdown value={document.contentMarkdown ?? ''} />
|
|
62
|
+
{isScreenshot ? null : (
|
|
63
|
+
<p style={fallbackNoticeStyle}>Read-only preview — editing is available inside the Notis portal.</p>
|
|
64
|
+
)}
|
|
65
|
+
</>
|
|
66
|
+
) : document.contentType === 'view' ? (
|
|
67
|
+
<div style={fallbackFrameStyle}>
|
|
68
|
+
{document.viewType === 'report'
|
|
69
|
+
? 'Interactive report'
|
|
70
|
+
: document.viewType === 'html'
|
|
71
|
+
? 'Interactive HTML document'
|
|
72
|
+
: 'View document'} — open it in its Notis Space for
|
|
73
|
+
the full native experience.
|
|
74
|
+
</div>
|
|
75
|
+
) : (
|
|
76
|
+
<div style={fallbackFrameStyle}>
|
|
77
|
+
{document.fileType ? `${document.fileType.toUpperCase()} document` : 'File document'} — open it in
|
|
78
|
+
the Notis portal to view.
|
|
79
|
+
</div>
|
|
80
|
+
)}
|
|
81
|
+
</div>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Embeds the document editor for a document anywhere in an app view. The host
|
|
87
|
+
* (portal) provides the implementation through `runtime.ui.DocumentEditor` and
|
|
88
|
+
* dispatches on the document's content type (markdown -> rich text editor,
|
|
89
|
+
* files -> matching viewer, views -> their trusted native Documents surface).
|
|
90
|
+
* Outside the portal this falls back to a read-only preview or an explicit
|
|
91
|
+
* open-in-Space notice for native views.
|
|
92
|
+
*
|
|
93
|
+
* ```tsx
|
|
94
|
+
* <DocumentEditor documentId={entry.id} variant="body" className="min-h-[320px]" />
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export function DocumentEditor(props: NotisDocumentEditorProps): ReactElement {
|
|
98
|
+
const runtime = useNotisRuntime();
|
|
99
|
+
const HostEditor = runtime?.ui?.DocumentEditor;
|
|
100
|
+
if (HostEditor) {
|
|
101
|
+
return <HostEditor {...props} />;
|
|
102
|
+
}
|
|
103
|
+
// A Space must never fall back to the legacy account-wide document reader.
|
|
104
|
+
if (runtime?.resource?.kind === 'space' || props.recordKey) return <RecordComponentUnavailable className={props.className} />;
|
|
105
|
+
return <DocumentEditorFallback {...props} />;
|
|
106
|
+
}
|