create-lykomi-app 0.9.21 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +21 -18
  2. package/dist/index.mjs +4 -4
  3. package/package.json +4 -4
  4. package/presets/foundry-iq/files/AGENTS.md +4 -4
  5. package/presets/foundry-iq/files/README.md +3 -3
  6. package/presets/foundry-iq/files/agent/app/agent.py +1 -1
  7. package/presets/foundry-iq/files/agent/app/foundry_iq.py +1 -1
  8. package/presets/foundry-iq/files/agent/app/main.py +1 -1
  9. package/presets/foundry-iq/files/src/main.tsx +4 -4
  10. package/presets/foundry-iq/files/src/pages/chat-page.tsx +1 -1
  11. package/presets/foundry-iq/files/src/tools/foundry-iq-module.tsx +1 -1
  12. package/presets/foundry-iq/files/src/tools/search-results-tool-ui.tsx +2 -2
  13. package/presets/neo4j/files/AGENTS.md +6 -6
  14. package/presets/neo4j/files/README.md +2 -2
  15. package/presets/neo4j/files/agent/app/graph_payload.py +2 -2
  16. package/presets/neo4j/files/agent/app/graph_tools.py +1 -1
  17. package/presets/neo4j/files/agent/app/main.py +1 -1
  18. package/presets/neo4j/files/agent/tests/test_graph_payload.py +1 -1
  19. package/presets/neo4j/files/src/index.css +7 -7
  20. package/presets/neo4j/files/src/main.tsx +3 -3
  21. package/presets/neo4j/files/src/pages/chat-page.tsx +1 -1
  22. package/presets/neo4j/files/src/tools/graph-module.tsx +2 -2
  23. package/presets/neo4j/files/vite.config.ts +10 -10
  24. package/presets/neo4j/manifest.json +1 -1
  25. package/template/AGENTS.md +17 -12
  26. package/template/README.md +32 -41
  27. package/template/agent/_env.example +1 -1
  28. package/template/agent/app/agent.py +1 -1
  29. package/template/agent/app/main.py +11 -1
  30. package/template/agent/app/tools.py +1 -1
  31. package/template/index.html +1 -1
  32. package/template/package.json +7 -6
  33. package/template/public/favicon-dark.svg +1 -0
  34. package/template/public/favicon.svg +1 -4
  35. package/template/public/fonts/Manrope-Variable.ttf +0 -0
  36. package/template/public/fonts/OFL.txt +93 -0
  37. package/template/src/index.css +15 -6
  38. package/template/src/main.tsx +11 -15
  39. package/template/src/pages/chat-page.tsx +2 -8
  40. package/template/src/pages/guide-page.tsx +134 -0
  41. package/template/src/pages/guide-windows.ts +32 -0
  42. package/template/src/pages/index.tsx +5 -4
  43. package/template/src/pages/starter-prompts.tsx +30 -0
  44. package/template/src/starter-module.ts +51 -0
  45. package/template/src/theme.ts +82 -0
  46. package/template/vite.config.ts +9 -9
@@ -1,12 +1,14 @@
1
1
  # __PROJECT_NAME__
2
2
 
3
- Foundry UI starter: a chat-first React app with a Microsoft Agent Framework service exposed through AG-UI.
3
+ LYKOMI UI starter: a React app with a Home composer, a contextual Guide page,
4
+ and a Microsoft Agent Framework service exposed through AG-UI.
4
5
 
5
6
  ## Quick Start
6
7
 
7
8
  ```bash
8
9
  pnpm install
9
10
  cp agent/.env.example agent/.env
11
+ # Set your Foundry project endpoint and model deployment in agent/.env.
10
12
  az login
11
13
  pnpm dev
12
14
  ```
@@ -25,14 +27,14 @@ Edit `agent/.env`:
25
27
 
26
28
  ```bash
27
29
  FOUNDRY_PROJECT_ENDPOINT=https://<account>.services.ai.azure.com/api/projects/<project>
28
- FOUNDRY_MODEL=gpt-5.4
30
+ FOUNDRY_MODEL=<model-deployment-name>
29
31
  ```
30
32
 
31
33
  `FoundryChatClient` reads the Microsoft Agent Framework environment variables directly. The starter passes `DefaultAzureCredential`, so local development works with `az login`. In Azure, use managed identity or the standard service principal environment variables.
32
34
 
33
35
  ## Try It
34
36
 
35
- The first screen is the chat. Try:
37
+ Home opens with the chat composer and three clickable prompts. The Guide route shows how a page sits beside the same persistent conversation, with real workbench tabs and resizable panels. Try:
36
38
 
37
39
  - `Say hello world using the hello_world tool`
38
40
  - `Check the weather in Seattle`
@@ -40,16 +42,18 @@ The first screen is the chat. Try:
40
42
 
41
43
  The bundled tools live in `agent/app/tools.py`.
42
44
 
45
+ The starter registers Guide navigation, page context, and an “About this starter” item in the lower interface menu through `src/starter-module.ts`. Open **Working draft** and **Architecture**, edit the draft, then select **Panels**. Pin, reorder and resize windows; switch back to **Tabs** or visit Home and return. The draft stays in memory until you close it or reload. Closing an edited draft asks before discarding it. `src/pages/guide-windows.ts` owns the app data; `WindowWorkbench` and `createWindowStore` from `@lykomi/web-ui` provide the shared window behaviour. The active window context, including the draft text, accompanies an AG-UI turn. Connect your own storage service before treating the draft as saved. Local demo auth has no user identity, so that control is an interface menu; after you wire real auth, the same menu shows the signed-in user and sign-out action.
46
+
43
47
  ## Coding With AI Agents
44
48
 
45
49
  This app ships `AGENTS.md` (+ a `CLAUDE.md` shim) for coding agents. Claude
46
- Code users can also install the Foundry UI plugin — grounding for the
50
+ Code users can also install the LYKOMI UI plugin — grounding for the
47
51
  fast-moving assistant-ui / AG-UI / Agent Framework stack plus a ChatModule
48
52
  authoring skill:
49
53
 
50
54
  ```
51
55
  /plugin marketplace add lykomi/ui
52
- /plugin install foundry-ui@foundry-ui
56
+ /plugin install lykomi-ui@lykomi-ui
53
57
  ```
54
58
 
55
59
  ## Update Modes
@@ -58,43 +62,26 @@ You can use this starter in two modes:
58
62
 
59
63
  | Mode | How | Best for | Updates |
60
64
  | -------------- | ---------------------------------------------------------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
61
- | Package-backed | Keep `@iloveagents/foundry-*` dependencies in `package.json` | Apps that want central Foundry UI fixes | Run `pnpm update @iloveagents/foundry-web-ui @iloveagents/foundry-web-shell @iloveagents/foundry-web-primitives @iloveagents/foundry-agent` |
65
+ | Package-backed | Keep `@lykomi/*` dependencies in `package.json` | Apps that want central LYKOMI UI fixes | Run `pnpm update @lykomi/web-ui @lykomi/web-shell @lykomi/web-primitives @lykomi/agent` |
62
66
  | Full copy | Copy this repo or turn the generated app into your own GitHub template | Apps that need deep customization | You own the code and manually merge upstream ideas |
63
67
 
64
68
  Template changes such as new files or folder structure are not applied automatically to an existing app. Package updates improve the shared UI, shell, and AG-UI transport without replacing your local `agent/` or `src/` files.
65
69
 
66
70
  ## Theme The App
67
71
 
68
- App name, browser title, logos, colors, radius, and fonts are configured through the `theme` layers in `src/main.tsx`.
72
+ The starter uses LYKOMI's neutral and deep-petrol palette, light and dark V2 favicons, and a locally bundled, OFL-licensed Manrope font. Change the app name, browser title, colors, radius, and fonts in `src/theme.ts`; add logo assets under `public/` and reference them in the theme's `branding` entry.
69
73
 
70
74
  ```ts
71
- bootstrapShell({
72
- pages: customerPages,
73
- authProvider: localDemoAuth,
74
- agentFetch: fetch,
75
- theme: [
76
- {
77
- id: "app-branding",
78
- definition: {
79
- branding: {
80
- appName: "Foundry UI",
81
- appTitle: "Foundry UI",
82
- logoUrl: "/logo.svg",
83
- darkLogoUrl: "/logo-dark.svg",
84
- },
85
- light: {
86
- primary: "oklch(0.54 0.21 282)",
87
- },
88
- radius: {
89
- radius: "0.5rem",
90
- },
91
- },
92
- },
93
- ],
94
- });
75
+ export const starterTheme: ThemeLayer = {
76
+ id: "my-app",
77
+ definition: {
78
+ light: { primary: "#3F666B" },
79
+ branding: { appName: "My app", appTitle: "My app" },
80
+ },
81
+ };
95
82
  ```
96
83
 
97
- Keep `@import "@iloveagents/foundry-web-ui/styles.css";` in `src/index.css`; it exposes the package theme tokens to Tailwind.
84
+ Keep `@import "@lykomi/web-ui/styles.css";` in `src/index.css`; it exposes the package theme tokens to Tailwind.
98
85
 
99
86
  ## Talk To It
100
87
 
@@ -103,7 +90,7 @@ variable and a microphone appears in the composer:
103
90
 
104
91
  ```bash
105
92
  # .env
106
- VITE_VOICE_PROXY_URL=ws://localhost:8080/ws?model=gpt-realtime
93
+ VITE_VOICE_PROXY_URL=ws://localhost:8080/ws?model=your-voice-deployment
107
94
  ```
108
95
 
109
96
  Leave it unset and nothing is registered — no microphone, no cost, the app is
@@ -116,12 +103,9 @@ docker run -p 8080:8080 \
116
103
  -e FOUNDRY_RESOURCE_NAME=your-foundry-resource \
117
104
  -e FOUNDRY_API_KEY="…" \
118
105
  -e ALLOWED_ORIGINS="http://localhost:8010" \
119
- ghcr.io/iloveagents/foundry-voice-live-proxy:latest
106
+ ghcr.io/iloveagents/foundry-voice-live-proxy:0.7.0
120
107
  ```
121
108
 
122
- > The default model (`gpt-realtime`) is available in **East US 2** and
123
- > **Sweden Central**.
124
-
125
109
  The default mode is **relay**: Voice Live transcribes what you say and speaks
126
110
  the answer, but *your* agent still answers it. A spoken turn is an ordinary
127
111
  turn — same tools, same retrieval, same history — so nothing about your agent
@@ -134,7 +118,7 @@ answer directly instead: much faster, but it only knows what your client-side
134
118
  tools tell it. On laptop speakers add `halfDuplex: true` so the assistant does
135
119
  not hear itself.
136
120
 
137
- Full options: [`@iloveagents/foundry-web-voice`](https://www.npmjs.com/package/@iloveagents/foundry-web-voice).
121
+ Full options: [`@lykomi/web-voice`](https://www.npmjs.com/package/@lykomi/web-voice).
138
122
 
139
123
  ## Add A Tool
140
124
 
@@ -175,15 +159,22 @@ Agent Framework connects the MCP server when the agent runs. The AG-UI endpoint
175
159
  ```text
176
160
  agent/ FastAPI + Microsoft Agent Framework AG-UI endpoint
177
161
  agent/app/tools.py Add agent tools here
178
- src/main.tsx Foundry UI shell bootstrap
179
- src/pages/chat-page.tsx Chat screen and quick starts
180
- src/index.css Tailwind v4 + Foundry UI theme resource and package dist scanning
162
+ src/main.tsx LYKOMI UI shell bootstrap
163
+ src/theme.ts App colors, typography, and branding
164
+ src/pages/starter-prompts.tsx Home quick starts
165
+ src/pages/guide-page.tsx A page beside the persistent chat
166
+ src/starter-module.ts Guide navigation and agent context
167
+ src/pages/chat-page.tsx Chat route
168
+ src/index.css Tailwind v4 + LYKOMI UI theme resource and package dist scanning
169
+ public/fonts/ Manrope and its OFL license
181
170
  ```
182
171
 
183
172
  ## Auth
184
173
 
185
174
  This starter uses local pass-through auth so the demo runs without an Entra app registration.
186
175
 
176
+ The AG-UI endpoint declares `single_user=True` for this local demo. Before serving multiple users, replace that setting with an authorization scope resolver and protect the API with your chosen auth scheme. A browser-only MSAL setup does not protect the FastAPI endpoint by itself.
177
+
187
178
  For production MSAL auth, remove `authProvider` and `agentFetch` from `src/main.tsx`, then set:
188
179
 
189
180
  ```bash
@@ -1,6 +1,6 @@
1
1
  # Copy to agent/.env and fill these values for Microsoft Agent Framework.
2
2
  FOUNDRY_PROJECT_ENDPOINT=https://your-project.services.ai.azure.com/api/projects/your-project
3
- FOUNDRY_MODEL=gpt-5.4
3
+ FOUNDRY_MODEL=your-model-deployment
4
4
 
5
5
  # DefaultAzureCredential uses `az login` locally, service principal env vars in CI,
6
6
  # and managed identity in Azure. Uncomment tenant/client values only when needed.
@@ -32,7 +32,7 @@ def get_agent() -> Agent:
32
32
  client=client,
33
33
  name="HelloAgent",
34
34
  instructions=(
35
- "You are a concise starter assistant for a Foundry UI app. "
35
+ "You are a concise starter assistant for a LYKOMI UI app. "
36
36
  "Use hello_world when the user asks for hello world or a hello demo. "
37
37
  "Use get_weather when weather is requested."
38
38
  ),
@@ -19,7 +19,7 @@ app.add_middleware(
19
19
  STATE_SCHEMA = {
20
20
  "navigation": {
21
21
  "type": "object",
22
- "description": "Current app route and navigation metadata from Foundry UI.",
22
+ "description": "Current app route and navigation metadata from LYKOMI UI.",
23
23
  "additionalProperties": True,
24
24
  },
25
25
  "selectedItemId": {
@@ -49,6 +49,13 @@ async def health() -> dict[str, str]:
49
49
  return {"status": "ok"}
50
50
 
51
51
 
52
+ @app.get("/api/chat/config")
53
+ async def chat_config() -> dict[str, object]:
54
+ # The scaffold does not set a reasoning level or context-window limit.
55
+ # Leave those values unknown until the app's backend can report them.
56
+ return {}
57
+
58
+
52
59
  try:
53
60
  from agent_framework.ag_ui import add_agent_framework_fastapi_endpoint
54
61
  except ImportError:
@@ -59,4 +66,7 @@ add_agent_framework_fastapi_endpoint(
59
66
  get_agent(),
60
67
  path="/api/agent",
61
68
  state_schema=STATE_SCHEMA,
69
+ # This local starter deliberately has no user sign-in. An authenticated
70
+ # deployment must replace this with an authorization scope resolver.
71
+ single_user=True,
62
72
  )
@@ -10,7 +10,7 @@ def hello_world(
10
10
  name: Annotated[str, Field(description="The person, team, or event to greet.")] = "world",
11
11
  ) -> str:
12
12
  """Return a short demo greeting."""
13
- return f"Hello, {name}. Your Foundry UI agent tool is wired through AG-UI."
13
+ return f"Hello, {name}. Your LYKOMI UI agent tool is wired through AG-UI."
14
14
 
15
15
 
16
16
  @tool(approval_mode="never_require")
@@ -4,7 +4,7 @@
4
4
  <meta charset="UTF-8" />
5
5
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
- <title>Foundry UI</title>
7
+ <title>LYKOMI UI</title>
8
8
  </head>
9
9
  <body>
10
10
  <div id="root"></div>
@@ -26,11 +26,12 @@
26
26
  "@assistant-ui/react": "^0.15.1",
27
27
  "@azure/msal-browser": "^5.6.3",
28
28
  "@azure/msal-react": "^5.2.1",
29
- "@iloveagents/foundry-agent": "0.49.1",
30
- "@iloveagents/foundry-web-primitives": "0.49.1",
31
- "@iloveagents/foundry-web-shell": "0.49.1",
32
- "@iloveagents/foundry-web-ui": "0.49.1",
33
- "@iloveagents/foundry-web-voice": "0.1.32",
29
+ "@lykomi/agent": "0.50.0",
30
+ "@lykomi/web-primitives": "0.50.0",
31
+ "@lykomi/web-shell": "0.50.0",
32
+ "@lykomi/web-ui": "0.50.0",
33
+ "@lykomi/web-voice": "0.1.34",
34
+ "@radix-ui/react-tabs": "^1.1.22",
34
35
  "@tanstack/react-query": "^5.99.0",
35
36
  "lucide-react": "^0.553.0",
36
37
  "react": "^19.2.5",
@@ -57,5 +58,5 @@
57
58
  "typescript-eslint": "^8.58.2",
58
59
  "vite": "^7.2.2"
59
60
  },
60
- "_comment_assistant_ui": "@assistant-ui/react is here only to satisfy the peer @iloveagents/foundry-web-ui declares — the app's own code never imports it. Keep the range in step with that peer (a guard test enforces it), and do NOT pin @assistant-ui/store / core / tap: those pins are what let this template fall a major line behind the packages it scaffolds, so a generated app failed to build on an API the packages had already adopted. assistant-ui's own ranges resolve a consistent graph on their own."
61
+ "_comment_assistant_ui": "@assistant-ui/react is here only to satisfy the peer @lykomi/web-ui declares — the app's own code never imports it. Keep the range in step with that peer (a guard test enforces it), and do NOT pin @assistant-ui/store / core / tap: those pins are what let this template fall a major line behind the packages it scaffolds, so a generated app failed to build on an API the packages had already adopted. assistant-ui's own ranges resolve a consistent graph on their own."
61
62
  }
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="960" height="960" viewBox="0 0 960 960" role="img" aria-label="LYKOMI abstract icon black"><title>LYKOMI abstract icon black</title><g transform="translate(165 1)"><path fill="#FFFFFF" d="M163 273 C86 322 41 387 52 465 C62 541 126 589 176 644 C232 706 233 796 154 900 C247 841 298 768 295 688 C294 611 257 558 203 509 C141 453 102 396 163 273 Z"/><path fill="#FFFFFF" d="M394 58 C357 104 292 137 246 188 C191 249 182 315 203 380 C221 446 254 482 291 525 C359 611 368 704 303 798 C383 727 422 646 405 558 C398 488 352 446 304 401 C250 352 235 307 263 251 C288 202 342 185 367 134 C382 109 390 82 394 58 Z"/><path fill="#FFFFFF" d="M392 176 C371 211 341 242 330 266 C309 319 342 339 389 351 C467 370 539 398 553 480 C581 406 537 360 483 321 C431 281 396 235 392 176 Z"/></g></svg>
@@ -1,4 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
2
- <rect width="32" height="32" rx="7" fill="#0f172a"/>
3
- <path d="M9 17.4 14.2 8h8.9l-5.2 9.4h4.9L15.4 25v-7.6H9Z" fill="#34d399"/>
4
- </svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="960" height="960" viewBox="0 0 960 960" role="img" aria-label="LYKOMI abstract icon black"><title>LYKOMI abstract icon black</title><g transform="translate(165 1)"><path fill="#0B0C0E" d="M163 273 C86 322 41 387 52 465 C62 541 126 589 176 644 C232 706 233 796 154 900 C247 841 298 768 295 688 C294 611 257 558 203 509 C141 453 102 396 163 273 Z"/><path fill="#0B0C0E" d="M394 58 C357 104 292 137 246 188 C191 249 182 315 203 380 C221 446 254 482 291 525 C359 611 368 704 303 798 C383 727 422 646 405 558 C398 488 352 446 304 401 C250 352 235 307 263 251 C288 202 342 185 367 134 C382 109 390 82 394 58 Z"/><path fill="#0B0C0E" d="M392 176 C371 211 341 242 330 266 C309 319 342 339 389 351 C467 370 539 398 553 480 C581 406 537 360 483 321 C431 281 396 235 392 176 Z"/></g></svg>
@@ -0,0 +1,93 @@
1
+ Copyright 2018 The Manrope Project Authors (https://github.com/googlefonts/manrope)
2
+
3
+ This Font Software is licensed under the SIL Open Font License, Version 1.1.
4
+ This license is copied below, and is also available with a FAQ at:
5
+ http://scripts.sil.org/OFL
6
+
7
+
8
+ -----------------------------------------------------------
9
+ SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
10
+ -----------------------------------------------------------
11
+
12
+ PREAMBLE
13
+ The goals of the Open Font License (OFL) are to stimulate worldwide
14
+ development of collaborative font projects, to support the font creation
15
+ efforts of academic and linguistic communities, and to provide a free and
16
+ open framework in which fonts may be shared and improved in partnership
17
+ with others.
18
+
19
+ The OFL allows the licensed fonts to be used, studied, modified and
20
+ redistributed freely as long as they are not sold by themselves. The
21
+ fonts, including any derivative works, can be bundled, embedded,
22
+ redistributed and/or sold with any software provided that any reserved
23
+ names are not used by derivative works. The fonts and derivatives,
24
+ however, cannot be released under any other type of license. The
25
+ requirement for fonts to remain under this license does not apply
26
+ to any document created using the fonts or their derivatives.
27
+
28
+ DEFINITIONS
29
+ "Font Software" refers to the set of files released by the Copyright
30
+ Holder(s) under this license and clearly marked as such. This may
31
+ include source files, build scripts and documentation.
32
+
33
+ "Reserved Font Name" refers to any names specified as such after the
34
+ copyright statement(s).
35
+
36
+ "Original Version" refers to the collection of Font Software components as
37
+ distributed by the Copyright Holder(s).
38
+
39
+ "Modified Version" refers to any derivative made by adding to, deleting,
40
+ or substituting -- in part or in whole -- any of the components of the
41
+ Original Version, by changing formats or by porting the Font Software to a
42
+ new environment.
43
+
44
+ "Author" refers to any designer, engineer, programmer, technical
45
+ writer or other person who contributed to the Font Software.
46
+
47
+ PERMISSION & CONDITIONS
48
+ Permission is hereby granted, free of charge, to any person obtaining
49
+ a copy of the Font Software, to use, study, copy, merge, embed, modify,
50
+ redistribute, and sell modified and unmodified copies of the Font
51
+ Software, subject to the following conditions:
52
+
53
+ 1) Neither the Font Software nor any of its individual components,
54
+ in Original or Modified Versions, may be sold by itself.
55
+
56
+ 2) Original or Modified Versions of the Font Software may be bundled,
57
+ redistributed and/or sold with any software, provided that each copy
58
+ contains the above copyright notice and this license. These can be
59
+ included either as stand-alone text files, human-readable headers or
60
+ in the appropriate machine-readable metadata fields within text or
61
+ binary files as long as those fields can be easily viewed by the user.
62
+
63
+ 3) No Modified Version of the Font Software may use the Reserved Font
64
+ Name(s) unless explicit written permission is granted by the corresponding
65
+ Copyright Holder. This restriction only applies to the primary font name as
66
+ presented to the users.
67
+
68
+ 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
69
+ Software shall not be used to promote, endorse or advertise any
70
+ Modified Version, except to acknowledge the contribution(s) of the
71
+ Copyright Holder(s) and the Author(s) or with their explicit written
72
+ permission.
73
+
74
+ 5) The Font Software, modified or unmodified, in part or in whole,
75
+ must be distributed entirely under this license, and must not be
76
+ distributed under any other license. The requirement for fonts to
77
+ remain under this license does not apply to any document created
78
+ using the Font Software.
79
+
80
+ TERMINATION
81
+ This license becomes null and void if any of the above conditions are
82
+ not met.
83
+
84
+ DISCLAIMER
85
+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
87
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
88
+ OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
89
+ COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90
+ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
91
+ DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
92
+ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
93
+ OTHER DEALINGS IN THE FONT SOFTWARE.
@@ -1,17 +1,26 @@
1
1
  @import "tailwindcss";
2
- @import "@iloveagents/foundry-web-ui/styles.css";
2
+ @import "@lykomi/web-ui/styles.css";
3
+
4
+ /* Manrope is bundled locally under the SIL Open Font License. */
5
+ @font-face {
6
+ font-family: "Manrope";
7
+ src: url("/fonts/Manrope-Variable.ttf") format("truetype");
8
+ font-style: normal;
9
+ font-weight: 200 800;
10
+ font-display: swap;
11
+ }
3
12
 
4
13
  /* Tailwind v4 needs explicit @source paths to scan package output
5
- * for utility classes. Without these, the Foundry UI components render
14
+ * for utility classes. Without these, the LYKOMI UI components render
6
15
  * without spacing, borders, or theme tokens. */
7
- @source "../node_modules/@iloveagents/foundry-web-primitives/dist";
8
- @source "../node_modules/@iloveagents/foundry-web-ui/dist";
9
- @source "../node_modules/@iloveagents/foundry-web-shell/dist";
16
+ @source "../node_modules/@lykomi/web-primitives/dist";
17
+ @source "../node_modules/@lykomi/web-ui/dist";
18
+ @source "../node_modules/@lykomi/web-shell/dist";
10
19
  /* Voice is optional at RUNTIME, not at build time: the package is always a
11
20
  dependency, so its utilities must be scanned or a voice-enabled app renders
12
21
  unstyled. Scanning it costs nothing when voice is off — Tailwind emits only
13
22
  the classes something references. */
14
- @source "../node_modules/@iloveagents/foundry-web-voice/dist";
23
+ @source "../node_modules/@lykomi/web-voice/dist";
15
24
 
16
25
  @plugin "tailwindcss-animate";
17
26
 
@@ -1,8 +1,12 @@
1
1
  import "./index.css";
2
- import { bootstrapShell } from "@iloveagents/foundry-web-shell";
3
- import type { AuthAdapter } from "@iloveagents/foundry-web-shell";
4
- import { createVoiceModule } from "@iloveagents/foundry-web-voice";
2
+ import { bootstrapShell } from "@lykomi/web-shell";
3
+ import type { AuthAdapter } from "@lykomi/web-shell";
4
+ import { createVoiceModule } from "@lykomi/web-voice";
5
+ import { registerChatSlots } from "@lykomi/web-ui";
5
6
  import { customerPages } from "@/pages/index.tsx";
7
+ import { starterTheme } from "./theme.ts";
8
+ import { StarterPrompts } from "./pages/starter-prompts.tsx";
9
+ import { starterModule } from "./starter-module.ts";
6
10
 
7
11
  const localDemoAuth: AuthAdapter = {
8
12
  Provider: ({ children }) => <>{children}</>,
@@ -26,20 +30,12 @@ const voiceModules = voiceProxyUrl
26
30
  ? [createVoiceModule({ connection: { proxyUrl: voiceProxyUrl } })]
27
31
  : [];
28
32
 
33
+ registerChatSlots({ homeStarters: StarterPrompts });
34
+
29
35
  bootstrapShell({
30
- modules: [...voiceModules], // Add your own ChatModules here.
36
+ modules: [starterModule, ...voiceModules], // Add your own ChatModules here.
31
37
  pages: customerPages,
32
38
  authProvider: localDemoAuth,
33
39
  agentFetch: fetch,
34
- theme: [
35
- {
36
- id: "app-branding",
37
- definition: {
38
- branding: {
39
- appName: "Foundry UI",
40
- appTitle: "Foundry UI",
41
- },
42
- },
43
- },
44
- ],
40
+ theme: [starterTheme],
45
41
  });
@@ -1,11 +1,5 @@
1
- import { ChatContent } from "@iloveagents/foundry-web-ui";
2
-
3
- const starterSuggestions = [
4
- "Say hello world using the hello_world tool",
5
- "Check the weather in Seattle",
6
- "What tools can you use?",
7
- ];
1
+ import { ChatContent } from "@lykomi/web-ui";
8
2
 
9
3
  export function ChatPage() {
10
- return <ChatContent starterSuggestions={starterSuggestions} />;
4
+ return <ChatContent />;
11
5
  }
@@ -0,0 +1,134 @@
1
+ import { MessageSquare, Plus } from "lucide-react";
2
+ import { Button, Textarea } from "@lykomi/web-primitives";
3
+ import { WindowWorkbench } from "@lykomi/web-ui/windows";
4
+ import {
5
+ useChatBubbleStore,
6
+ DropdownMenu,
7
+ DropdownMenuTrigger,
8
+ DropdownMenuContent,
9
+ DropdownMenuItem,
10
+ } from "@lykomi/web-ui";
11
+ import { guideWindows, openGuideWindow } from "./guide-windows.ts";
12
+
13
+ export function GuidePage() {
14
+ return (
15
+ <WindowWorkbench
16
+ controller={guideWindows}
17
+ toolbar={
18
+ <DropdownMenu>
19
+ <DropdownMenuTrigger asChild>
20
+ <Button variant="ghost" size="sm" aria-label="Open window">
21
+ <Plus />
22
+ Open
23
+ </Button>
24
+ </DropdownMenuTrigger>
25
+ <DropdownMenuContent align="end">
26
+ <DropdownMenuItem onSelect={() => openGuideWindow("overview")}>Guide</DropdownMenuItem>
27
+ <DropdownMenuItem onSelect={() => openGuideWindow("architecture")}>
28
+ Architecture
29
+ </DropdownMenuItem>
30
+ <DropdownMenuItem onSelect={() => openGuideWindow("draft")}>
31
+ Working draft
32
+ </DropdownMenuItem>
33
+ </DropdownMenuContent>
34
+ </DropdownMenu>
35
+ }
36
+ empty={
37
+ <div className="p-8">
38
+ <Button onClick={() => openGuideWindow("overview")}>Open guide</Button>
39
+ </div>
40
+ }
41
+ getWindowContext={(tab) => ({
42
+ title: tab.title,
43
+ kind: "document",
44
+ windowId: tab.id,
45
+ content:
46
+ tab.text ??
47
+ (tab.kind === "architecture"
48
+ ? "Browser shell → AG-UI → FastAPI agent. Each page supplies its own context."
49
+ : "The starter guide explains agent configuration, page context, tabs and panels."),
50
+ unsavedChanges: Boolean(tab.dirty),
51
+ })}
52
+ renderWindow={(tab) => (
53
+ <main className="mx-auto max-w-3xl px-6 py-8 sm:px-10" aria-label={String(tab.title)}>
54
+ <p className="text-sm font-medium text-primary">LYKOMI UI starter</p>
55
+ {tab.kind === "overview" && (
56
+ <>
57
+ <h1 className="mt-2 text-3xl font-semibold tracking-tight">
58
+ Build an agent app around real work.
59
+ </h1>
60
+ <p className="mt-4 text-base leading-relaxed text-muted-foreground">
61
+ Your pages, a persistent conversation, and a shared workbench. Open a draft and the
62
+ architecture, then switch between tabs and panels. Your work stays in place.
63
+ </p>
64
+ <div className="mt-6 flex flex-wrap gap-2">
65
+ <Button onClick={() => openGuideWindow("draft")}>Open working draft</Button>
66
+ <Button variant="outline" onClick={() => openGuideWindow("architecture")}>
67
+ Open architecture
68
+ </Button>
69
+ </div>
70
+ <section className="mt-8 rounded-xl border border-border bg-card p-6">
71
+ <h2 className="text-lg font-semibold">Connect your agent</h2>
72
+ <p className="mt-2 text-sm leading-relaxed text-muted-foreground">
73
+ Set <code>FOUNDRY_PROJECT_ENDPOINT</code> and <code>FOUNDRY_MODEL</code> in{" "}
74
+ <code>agent/.env</code>, sign in with <code>az login</code>, then run{" "}
75
+ <code>pnpm dev</code>. Replace the sample tools in <code>agent/app/tools.py</code>
76
+ .
77
+ </p>
78
+ </section>
79
+ </>
80
+ )}
81
+ {tab.kind === "architecture" && (
82
+ <>
83
+ <h1 className="mt-2 text-2xl font-semibold">Starter architecture</h1>
84
+ <p className="mt-4 leading-relaxed text-muted-foreground">
85
+ <code>bootstrapShell</code> composes your routes, theme, modules and persistent
86
+ chat. The browser sends AG-UI runs to <code>/api/agent</code>, including the active
87
+ page and window context.
88
+ </p>
89
+ <h2 className="mt-6 text-lg font-semibold">Make the workbench yours</h2>
90
+ <p className="mt-2 leading-relaxed text-muted-foreground">
91
+ <code>createWindowStore</code> owns open windows. <code>WindowWorkbench</code>{" "}
92
+ renders the same content as tabs or panels. Your app supplies the content and
93
+ decides how to save drafts.
94
+ </p>
95
+ </>
96
+ )}
97
+ {tab.kind === "draft" && (
98
+ <>
99
+ <h1 className="mt-2 text-2xl font-semibold">Working draft</h1>
100
+ <p className="mt-3 text-sm text-muted-foreground">
101
+ Edit this draft, switch layouts, reorder its tab, or visit Home. It stays here until
102
+ you close it. This example keeps your work in memory for this browser session.
103
+ </p>
104
+ <label htmlFor="starter-draft" className="mt-6 block text-sm font-medium">
105
+ Your idea
106
+ </label>
107
+ <Textarea
108
+ id="starter-draft"
109
+ className="mt-2 min-h-48"
110
+ value={tab.text ?? ""}
111
+ onChange={(event) =>
112
+ guideWindows.update(tab.id, { text: event.target.value, dirty: true })
113
+ }
114
+ />
115
+ <p className="mt-2 text-xs text-muted-foreground">
116
+ {tab.dirty
117
+ ? "Unsaved changes · this demo has no storage service"
118
+ : "Try editing, then switch to panels"}
119
+ </p>
120
+ </>
121
+ )}
122
+ <Button
123
+ variant="outline"
124
+ className="mt-6 gap-2"
125
+ onClick={() => useChatBubbleStore.getState().open()}
126
+ >
127
+ <MessageSquare className="size-4" aria-hidden />
128
+ Ask the agent about this page
129
+ </Button>
130
+ </main>
131
+ )}
132
+ />
133
+ );
134
+ }
@@ -0,0 +1,32 @@
1
+ import { createWindowStore, type WorkbenchWindow } from "@lykomi/web-ui/windows";
2
+
3
+ export interface GuideWindow extends WorkbenchWindow {
4
+ kind: "overview" | "architecture" | "draft";
5
+ text?: string;
6
+ }
7
+
8
+ // App-owned data; the UI framework owns selection, pinning, ordering and layouts.
9
+ // Keeping this store outside the route also retains drafts when visiting Home.
10
+ export const guideWindows = createWindowStore<GuideWindow>({
11
+ storageKey: "starter.windows.layout",
12
+ });
13
+
14
+ export function openGuideWindow(kind: GuideWindow["kind"]) {
15
+ const title = { overview: "Guide", architecture: "Architecture", draft: "Working draft" }[kind];
16
+ guideWindows.open(
17
+ {
18
+ id: kind,
19
+ kind,
20
+ title,
21
+ independent: true,
22
+ text: kind === "draft" ? "My agent app will help people…" : undefined,
23
+ close: () => {
24
+ const tab = guideWindows.get().tabs.find((entry) => entry.id === kind);
25
+ if (tab?.dirty && !window.confirm("Close this draft and discard your changes?")) return;
26
+ guideWindows.remove(kind);
27
+ },
28
+ },
29
+ { mode: "new-tab" },
30
+ );
31
+ }
32
+ openGuideWindow("overview");
@@ -1,5 +1,6 @@
1
- import type { ShellPage } from "@iloveagents/foundry-web-shell";
1
+ import type { ShellPage } from "@lykomi/web-shell";
2
2
  import { ChatPage } from "./chat-page.tsx";
3
+ import { GuidePage } from "./guide-page.tsx";
3
4
 
4
5
  /**
5
6
  * Customer pages list. Add your own routes here.
@@ -10,12 +11,12 @@ import { ChatPage } from "./chat-page.tsx";
10
11
  */
11
12
  export const customerPages: ShellPage[] = [
12
13
  { path: "/", element: <ChatPage /> },
14
+ { path: "guide", element: <GuidePage /> },
13
15
  // /chat/:aguiThreadId — clicking a Recents chat in the sidebar (or
14
16
  // reloading the URL after sending a message) navigates here. Without
15
17
  // this route react-router has no match for the location and the SPA
16
18
  // renders a blank page on the chats-as-entities resume flow.
17
- // ChatPage itself is URL-agnostic; foundry-ui's
18
- // ChatConversationAwareRuntime reads the thread id from the URL via
19
- // `CHAT_PATH_PATTERN` in spaces-web-ui's `chat-conversation-config`.
19
+ // ChatPage itself is URL-agnostic. A host that persists conversations can
20
+ // read this route parameter and restore the matching thread.
20
21
  { path: "chat/:id", element: <ChatPage />, chatOnly: true },
21
22
  ];