oomi-ai 0.2.48 → 0.2.50

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.
@@ -2,11 +2,14 @@ import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
2
2
  import { HomePage } from "./pages/HomePage";
3
3
  import { ScenePage } from "./pages/ScenePage";
4
4
 
5
+ const isSpatialRuntime = __XR_ENV_BASE__.startsWith("/webspatial/avp");
6
+
5
7
  export default function App() {
6
8
  return (
7
9
  <Router basename={__XR_ENV_BASE__}>
8
10
  <Routes>
9
- <Route index element={<HomePage />} />
11
+ <Route index element={isSpatialRuntime ? <ScenePage /> : <HomePage />} />
12
+ <Route path="home" element={<HomePage />} />
10
13
  <Route path="scene" element={<ScenePage />} />
11
14
  </Routes>
12
15
  </Router>
@@ -2,10 +2,10 @@
2
2
  font-family: "Segoe UI", system-ui, sans-serif;
3
3
  line-height: 1.5;
4
4
  font-weight: 400;
5
- color: #2a251f;
5
+ color: #1f1b16;
6
6
  background:
7
- radial-gradient(circle at top, rgba(205, 183, 143, 0.32), transparent 36%),
8
- linear-gradient(180deg, #f7f2e8 0%, #efe7d8 100%);
7
+ radial-gradient(circle at top left, rgba(222, 207, 180, 0.5), transparent 34%),
8
+ linear-gradient(180deg, #faf7f1 0%, #efe7db 100%);
9
9
  font-synthesis: none;
10
10
  text-rendering: optimizeLegibility;
11
11
  -webkit-font-smoothing: antialiased;
@@ -16,17 +16,38 @@
16
16
  box-sizing: border-box;
17
17
  }
18
18
 
19
+ html,
20
+ body,
21
+ #root {
22
+ min-height: 100%;
23
+ }
24
+
19
25
  body {
20
26
  margin: 0;
21
27
  min-width: 320px;
22
28
  min-height: 100vh;
29
+ background:
30
+ radial-gradient(circle at top left, rgba(255, 255, 255, 0.7), transparent 28%),
31
+ radial-gradient(circle at top right, rgba(228, 216, 196, 0.62), transparent 26%),
32
+ linear-gradient(180deg, #faf7f1 0%, #eee5d8 100%);
23
33
  }
24
34
 
25
35
  button,
26
36
  a {
27
37
  font: inherit;
38
+ color: inherit;
39
+ text-decoration: none;
28
40
  }
29
41
 
30
42
  button {
31
43
  cursor: pointer;
32
44
  }
45
+
46
+ html.is-spatial,
47
+ html.is-spatial body {
48
+ background: transparent;
49
+ }
50
+
51
+ html.is-spatial #root {
52
+ min-height: 100vh;
53
+ }
@@ -1,151 +1,124 @@
1
- import { useEffect } from "react";
2
1
  import { Link } from "react-router-dom";
3
2
  import "../App.css";
4
3
  import { personaConfig } from "../persona/config";
5
4
  import { personaNotes } from "../persona/notes";
6
5
  import {
7
6
  WEBSPATIAL_FORK_COMMIT,
8
- configurePersonaScene,
9
7
  detectSpatialEnvironment,
10
8
  openPersonaScene,
11
- xrStyle,
12
9
  } from "../spatial";
13
10
 
14
- const previewCards = [
11
+ const browserCards = [
15
12
  {
16
- tone: "ocean",
17
- title: "Focused surface",
18
- body: "Use the persona site as a real operator board, planner, studio, or dashboard instead of another chat clone.",
19
- depth: 34,
20
- material: "translucent",
13
+ title: "Browser route stays valid",
14
+ body: "Keep a normal web route for previews, fallback browsers, and non-spatial editing.",
21
15
  },
22
16
  {
23
- tone: "coral",
24
- title: "Depth-authored cards",
25
- body: "Key controls should declare their own depth and material so AndroidXR reads them as layered surfaces.",
26
- depth: 84,
27
- material: "thin",
17
+ title: "XR route stays separate",
18
+ body: "The mounted scene component should own scene bootstrap, diagnostics, and the real spatial surfaces.",
28
19
  },
29
20
  {
30
- tone: "violet",
31
- title: "Persistent handoff",
32
- body: "Let Oomi keep the live conversation thread while the persona page stays focused on structured work.",
33
- depth: 140,
34
- material: "thick",
21
+ title: "Oomi opens the entry URL",
22
+ body: "When the runtime is spatial, the entry route should land directly in the scene instead of a flat homepage.",
35
23
  },
36
24
  ];
37
25
 
38
- type HomePageProps = {
39
- sceneMode?: boolean;
40
- };
41
-
42
- export function HomePage({ sceneMode = false }: HomePageProps) {
26
+ export function HomePage() {
43
27
  const environment = detectSpatialEnvironment();
44
28
 
45
- useEffect(() => {
46
- configurePersonaScene();
47
- }, []);
48
-
49
29
  return (
50
- <main className={`persona-shell${sceneMode ? " persona-scene-root" : ""}`}>
51
- <section className="persona-header">
52
- <div className="persona-panel persona-hero">
30
+ <main className="persona-home">
31
+ <section className="home-grid">
32
+ <article className="home-panel home-hero">
53
33
  <div>
54
- <p className="persona-eyebrow">{sceneMode ? "Spatial Persona Surface" : "WebSpatial Persona"}</p>
55
- <h1 className="persona-title">{personaConfig.name}</h1>
56
- <p className="persona-description">{personaConfig.description}</p>
34
+ <p className="home-eyebrow">Oomi Persona Surface</p>
35
+ <h1 className="home-title">{personaConfig.name}</h1>
36
+ <p className="home-description">{personaConfig.description}</p>
37
+ <p className="home-supporting-copy">
38
+ This browser route exists for non-spatial parity. In XR mode, the index route should
39
+ open directly into the mounted scene so Oomi lands on a real WebSpatial workspace
40
+ instead of another 2D page.
41
+ </p>
57
42
  </div>
58
43
 
59
- <div className="persona-actions">
60
- {!sceneMode ? (
61
- <>
62
- <button className="persona-button" onClick={openPersonaScene}>
63
- Launch Spatial Surface
64
- </button>
65
- <Link className="persona-link" to="/scene" target="_blank">
66
- Open Spatial Preview
67
- </Link>
68
- </>
69
- ) : (
70
- <Link className="persona-button" to="/">
71
- Return To Browser View
72
- </Link>
73
- )}
44
+ <div className="home-actions">
45
+ <button className="home-primary-button" onClick={openPersonaScene}>
46
+ Launch XR Workspace
47
+ </button>
48
+ <Link className="home-secondary-link" to="/scene" target="_blank" rel="noreferrer">
49
+ Open Scene Route
50
+ </Link>
74
51
  </div>
75
52
 
76
- <div className="persona-preview-strip">
77
- {previewCards.map(card => (
78
- <article
79
- key={card.title}
80
- className="persona-preview-card"
81
- data-tone={card.tone}
82
- enable-xr
83
- style={xrStyle(card.depth, card.material)}
84
- >
85
- <h3>{card.title}</h3>
53
+ <div className="home-card-grid">
54
+ {browserCards.map(card => (
55
+ <article key={card.title} className="home-panel home-feature-card">
56
+ <h2>{card.title}</h2>
86
57
  <p>{card.body}</p>
87
58
  </article>
88
59
  ))}
89
60
  </div>
90
- </div>
61
+ </article>
91
62
 
92
- <aside className="persona-panel persona-runtime">
93
- <p className="persona-eyebrow">Runtime</p>
94
- <div className="runtime-list">
95
- <div className="runtime-row">
96
- <span className="runtime-label">Slug</span>
97
- <span className="runtime-value">{personaConfig.slug}</span>
63
+ <aside className="home-panel home-runtime">
64
+ <p className="home-eyebrow">Runtime Snapshot</p>
65
+ <div className="home-runtime-list">
66
+ <div className="home-runtime-row">
67
+ <span>Slug</span>
68
+ <span>{personaConfig.slug}</span>
69
+ </div>
70
+ <div className="home-runtime-row">
71
+ <span>SDK</span>
72
+ <span>@webspatial/react-sdk {environment.sdkVersion}</span>
98
73
  </div>
99
- <div className="runtime-row">
100
- <span className="runtime-label">SDK</span>
101
- <span className="runtime-value">@webspatial/react-sdk {environment.sdkVersion}</span>
74
+ <div className="home-runtime-row">
75
+ <span>Bridge</span>
76
+ <span>{environment.hasBridge ? "available" : "waiting"}</span>
102
77
  </div>
103
- <div className="runtime-row">
104
- <span className="runtime-label">Bridge</span>
105
- <span className="runtime-value">{environment.hasBridge ? "available" : "waiting"}</span>
78
+ <div className="home-runtime-row">
79
+ <span>Native</span>
80
+ <span>{environment.nativeVersion ?? "browser fallback"}</span>
106
81
  </div>
107
- <div className="runtime-row">
108
- <span className="runtime-label">Native</span>
109
- <span className="runtime-value">{environment.nativeVersion ?? "browser fallback"}</span>
82
+ <div className="home-runtime-row">
83
+ <span>Fork</span>
84
+ <span>{WEBSPATIAL_FORK_COMMIT.slice(0, 7)}</span>
110
85
  </div>
111
- <div className="runtime-row">
112
- <span className="runtime-label">Fork</span>
113
- <span className="runtime-value">{WEBSPATIAL_FORK_COMMIT.slice(0, 7)}</span>
86
+ <div className="home-runtime-row">
87
+ <span>Spatial mode</span>
88
+ <span>{environment.isWebSpatial ? "active" : "not active"}</span>
114
89
  </div>
115
90
  </div>
116
91
  </aside>
117
92
  </section>
118
93
 
119
- <section className="persona-grid">
120
- <article className="persona-panel persona-card">
121
- <h2>What Good XR Feels Like</h2>
94
+ <section className="home-card-grid">
95
+ <article className="home-panel home-copy-card">
96
+ <h2>Template Contract</h2>
122
97
  <p>
123
- Treat the generated persona site like a spatial work surface. The XR route should keep
124
- the actual persona shell intact, then layer depth, materials, and anchored surfaces on
125
- top instead of swapping in a disconnected demo board.
98
+ XR mode should default into <code>ScenePage</code>, mount scene bootstrap from that
99
+ component, and render multiple authored panels with explicit depth and material.
126
100
  </p>
127
101
  <p>
128
- {sceneMode
129
- ? "This route is the AndroidXR work surface. It should feel like the same persona site, just spatialized."
130
- : "This home route exists for browser parity and one-click launch into the spatial work surface."}
102
+ The scaffold should teach agents to build surfaces, not just pages wrapped in one
103
+ captured DOM card.
131
104
  </p>
132
105
  </article>
133
106
 
134
- <article className="persona-panel persona-card">
107
+ <article className="home-panel home-copy-card">
135
108
  <h2>Editing Notes</h2>
136
- <ul className="persona-note-list">
109
+ <ul className="home-note-list">
137
110
  {personaNotes.map(note => (
138
111
  <li key={note}>{note}</li>
139
112
  ))}
140
113
  </ul>
141
114
  </article>
142
115
 
143
- <article className="persona-panel persona-card persona-scene-card">
144
- <h2>Scene Contract</h2>
116
+ <article className="home-panel home-copy-card">
117
+ <h2>Spatial Defaults</h2>
145
118
  <p>
146
- The page should import WebSpatial helpers, self-configure the scene, and keep using
147
- explicit <code>enable-xr</code>, <code>--xr-back</code>, and
148
- <code> --xr-background-material</code> values on important surfaces.
119
+ Preserve the transparent spatial shell, the vendored WebSpatial fork, and the DOM
120
+ capture hooks in <code>main.tsx</code>. Those pieces are part of the runtime contract,
121
+ not optional polish.
149
122
  </p>
150
123
  </article>
151
124
  </section>
@@ -1,5 +1,158 @@
1
- import { HomePage } from "./HomePage";
1
+ import { useEffect } from "react";
2
+ import { Link } from "react-router-dom";
3
+ import "../App.css";
4
+ import { personaConfig } from "../persona/config";
5
+ import { personaNotes } from "../persona/notes";
6
+ import {
7
+ WEBSPATIAL_FORK_COMMIT,
8
+ configurePersonaScene,
9
+ detectSpatialEnvironment,
10
+ xrStyle,
11
+ } from "../spatial";
12
+
13
+ const spatialWorkflows = [
14
+ {
15
+ title: "Primary workspace panel",
16
+ body: "Use this as the main operator surface for the persona's task instead of duplicating a generic homepage.",
17
+ },
18
+ {
19
+ title: "Secondary workspace panel",
20
+ body: "Reserve a separate surface for supporting context, previews, or step-by-step controls so the XR scene reads as a composed environment.",
21
+ },
22
+ ];
23
+
24
+ const sceneChips = [
25
+ "header surface",
26
+ "workspace panel",
27
+ "status panel",
28
+ "completion panel",
29
+ "tool tray",
30
+ ];
2
31
 
3
32
  export function ScenePage() {
4
- return <HomePage sceneMode />;
33
+ const environment = detectSpatialEnvironment();
34
+
35
+ useEffect(() => {
36
+ configurePersonaScene();
37
+
38
+ const snapshot = detectSpatialEnvironment();
39
+ console.info("[persona] spatial runtime", snapshot);
40
+ }, []);
41
+
42
+ return (
43
+ <main className="scene-shell">
44
+ <header className="scene-header" enable-xr style={xrStyle(14, "thin")}>
45
+ <div>
46
+ <p className="scene-eyebrow">Spatial Scene</p>
47
+ <h1 className="scene-title">{personaConfig.name}</h1>
48
+ <p className="scene-copy">
49
+ {personaConfig.description} This route is the default experience when Oomi opens the
50
+ app inside the WebSpatial runtime, so it should behave like a real XR workspace instead
51
+ of a browser homepage.
52
+ </p>
53
+ </div>
54
+
55
+ <div className="scene-badge-stack">
56
+ <span className="scene-badge">{environment.isWebSpatial ? "WebSpatial active" : "Browser preview"}</span>
57
+ <span className="scene-badge">SDK {environment.sdkVersion}</span>
58
+ <span className="scene-badge">Fork {WEBSPATIAL_FORK_COMMIT.slice(0, 7)}</span>
59
+ </div>
60
+ </header>
61
+
62
+ <section className="scene-workspace-grid">
63
+ <article className="scene-surface scene-workspace-primary" enable-xr style={xrStyle(40, "thick")}>
64
+ <p className="scene-surface-label">Primary Workspace</p>
65
+ <h2 className="scene-surface-title">Make spatial panels the default, not the afterthought</h2>
66
+ <p className="scene-copy">
67
+ The mounted scene component is where the persona should bootstrap the scene, verify the
68
+ runtime, and lay out the main work surfaces. If the scene route only wraps a normal
69
+ home page, the app will still feel flat in-headset even when the SDK is bundled.
70
+ </p>
71
+
72
+ <div className="scene-step-grid">
73
+ {spatialWorkflows.map(workflow => (
74
+ <article key={workflow.title} className="scene-step-card">
75
+ <h3>{workflow.title}</h3>
76
+ <p>{workflow.body}</p>
77
+ </article>
78
+ ))}
79
+ </div>
80
+ </article>
81
+
82
+ <div className="scene-secondary-stack">
83
+ <aside className="scene-surface" enable-xr style={xrStyle(24, "thin")}>
84
+ <p className="scene-surface-label">Runtime Diagnostics</p>
85
+ <div className="scene-status-grid">
86
+ <div className="scene-status-row">
87
+ <span>isWebSpatial</span>
88
+ <span>{environment.isWebSpatial ? "true" : "false"}</span>
89
+ </div>
90
+ <div className="scene-status-row">
91
+ <span>hasBridge</span>
92
+ <span>{environment.hasBridge ? "true" : "false"}</span>
93
+ </div>
94
+ <div className="scene-status-row">
95
+ <span>hasWebSpatialData</span>
96
+ <span>{environment.hasWebSpatialData ? "true" : "false"}</span>
97
+ </div>
98
+ <div className="scene-status-row">
99
+ <span>nativeVersion</span>
100
+ <span>{environment.nativeVersion ?? "browser fallback"}</span>
101
+ </div>
102
+ </div>
103
+ </aside>
104
+
105
+ <aside className="scene-surface" enable-xr style={xrStyle(18, "thin")}>
106
+ <p className="scene-surface-label">Tool Tray</p>
107
+ <div className="scene-chip-row">
108
+ {sceneChips.map(chip => (
109
+ <span key={chip} className="scene-tool-chip">
110
+ {chip}
111
+ </span>
112
+ ))}
113
+ </div>
114
+ </aside>
115
+ </div>
116
+ </section>
117
+
118
+ <section className="scene-support-grid">
119
+ <article className="scene-surface" enable-xr style={xrStyle(18, "thin")}>
120
+ <p className="scene-surface-label">Spatial Surface Checklist</p>
121
+ <h2 className="scene-surface-title">At least three authored panels</h2>
122
+ <p className="scene-copy">
123
+ Add <code>enable-xr</code> and <code>xrStyle()</code> to meaningful panels like the
124
+ scene header, the primary workspace, and supporting status or completion surfaces.
125
+ </p>
126
+ </article>
127
+
128
+ <article className="scene-surface" enable-xr style={xrStyle(28, "regular")}>
129
+ <p className="scene-surface-label">Completion Panel</p>
130
+ <h2 className="scene-surface-title">Keep the host transparent</h2>
131
+ <p className="scene-copy">
132
+ The XR shell should recede behind the panels. Use <code>html.is-spatial</code> styles
133
+ so the host background stays transparent while the authored panels carry material.
134
+ </p>
135
+ </article>
136
+
137
+ <article className="scene-surface" enable-xr style={xrStyle(22, "thin")}>
138
+ <p className="scene-surface-label">Agent Notes</p>
139
+ <ul className="scene-note-list">
140
+ {personaNotes.map(note => (
141
+ <li key={note}>{note}</li>
142
+ ))}
143
+ </ul>
144
+ </article>
145
+ </section>
146
+
147
+ <footer className="scene-footer">
148
+ <p className="scene-footer-copy">
149
+ Browser editing still lives at the non-spatial route while XR mode defaults directly into
150
+ the scene.
151
+ </p>
152
+ <Link className="scene-link" to="/home">
153
+ Open browser route
154
+ </Link>
155
+ </footer>
156
+ </main>
157
+ );
5
158
  }
@@ -1,7 +1,10 @@
1
1
  export const personaNotes = [
2
2
  "Keep persona-specific logic inside src/persona/ unless Oomi explicitly instructs otherwise.",
3
- "Preserve the WebSpatial router basename, the scene self-init helper, and the scene-launch flow.",
3
+ "Preserve the WebSpatial router basename, and default the index route to ScenePage whenever the runtime is running under the XR base path.",
4
+ "Call configurePersonaScene() from the mounted scene component and log detectSpatialEnvironment() when the scene boots.",
4
5
  "Keep using the vendored AndroidXR-enabled WebSpatial fork instead of switching back to the stock npm packages.",
5
- "Author key surfaces with explicit enable-xr, --xr-back, and --xr-background-material values so the page reads as spatial instead of flat.",
6
+ "Author multiple meaningful surfaces with explicit enable-xr, --xr-back, and --xr-background-material values so the app reads as spatial instead of one captured webpage.",
7
+ "Keep html.is-spatial shell styles transparent so the host recedes and the panels carry the visual material.",
8
+ "Keep the managed runtime compatible with `oomi personas launch-managed`; do not require manual npm run dev for Oomi to consider the persona live.",
6
9
  "Do not remove public/oomi.runtime.json, public/oomi.health.json, or public/manifest.webmanifest.",
7
10
  ];
@@ -27,7 +27,7 @@ export type XrStyle = CSSProperties & {
27
27
 
28
28
  export function xrStyle(back: number, material: string): XrStyle {
29
29
  return {
30
- "--xr-back": `${back}px`,
30
+ "--xr-back": String(back),
31
31
  "--xr-background-material": material,
32
32
  };
33
33
  }