create-lykomi-app 0.9.22 → 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.
- package/README.md +6 -3
- package/package.json +1 -1
- package/presets/neo4j/files/vite.config.ts +2 -2
- package/presets/neo4j/manifest.json +1 -1
- package/template/AGENTS.md +10 -5
- package/template/README.md +25 -34
- package/template/agent/_env.example +1 -1
- package/template/agent/app/main.py +10 -0
- package/template/package.json +6 -5
- package/template/public/favicon-dark.svg +1 -0
- package/template/public/favicon.svg +1 -4
- package/template/public/fonts/Manrope-Variable.ttf +0 -0
- package/template/public/fonts/OFL.txt +93 -0
- package/template/src/index.css +9 -0
- package/template/src/main.tsx +8 -12
- package/template/src/pages/chat-page.tsx +1 -7
- package/template/src/pages/guide-page.tsx +134 -0
- package/template/src/pages/guide-windows.ts +32 -0
- package/template/src/pages/index.tsx +4 -3
- package/template/src/pages/starter-prompts.tsx +30 -0
- package/template/src/starter-module.ts +51 -0
- package/template/src/theme.ts +82 -0
- package/template/vite.config.ts +2 -2
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ LYKOMI UI is maintained at [lykomi/ui](https://github.com/lykomi/ui).
|
|
|
10
10
|
npm create lykomi-app my-agent-app
|
|
11
11
|
cd my-agent-app
|
|
12
12
|
cp agent/.env.example agent/.env
|
|
13
|
+
# Edit agent/.env with your project endpoint and model deployment.
|
|
13
14
|
az login
|
|
14
15
|
pnpm dev
|
|
15
16
|
```
|
|
@@ -20,7 +21,7 @@ Set these in `agent/.env`:
|
|
|
20
21
|
|
|
21
22
|
```bash
|
|
22
23
|
FOUNDRY_PROJECT_ENDPOINT=https://<account>.services.ai.azure.com/api/projects/<project>
|
|
23
|
-
FOUNDRY_MODEL
|
|
24
|
+
FOUNDRY_MODEL=<model-deployment-name>
|
|
24
25
|
```
|
|
25
26
|
|
|
26
27
|
The generated app starts:
|
|
@@ -29,11 +30,13 @@ The generated app starts:
|
|
|
29
30
|
- Agent service: `http://localhost:8020`
|
|
30
31
|
- AG-UI endpoint: `/api/agent`
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
Home opens with a chat composer and clickable prompts. Guide demonstrates a page beside the persistent chat. The bundled agent can call `hello_world` and `get_weather`.
|
|
34
|
+
|
|
35
|
+
The lower interface menu links to the Guide in local demo mode. Connecting a real auth provider makes it the signed-in user menu.
|
|
33
36
|
|
|
34
37
|
## Theme The App
|
|
35
38
|
|
|
36
|
-
Generated apps
|
|
39
|
+
Generated apps use LYKOMI's neutral and deep-petrol palette, light and dark V2 favicons, a locally bundled Manrope font, and `LYKOMI UI` for the shell and browser title. Edit `src/theme.ts` to change branding, colors, radius, and typography.
|
|
37
40
|
|
|
38
41
|
## Two Ways To Use It
|
|
39
42
|
|
package/package.json
CHANGED
|
@@ -79,11 +79,11 @@ export default defineConfig({
|
|
|
79
79
|
)
|
|
80
80
|
return "lykomi-workbench";
|
|
81
81
|
if (pkg.startsWith("@lykomi/web-ui/")) return "vendor-lykomi-ui";
|
|
82
|
-
if (pkg.startsWith("@lykomi/web-shell/")) return "vendor-
|
|
82
|
+
if (pkg.startsWith("@lykomi/web-shell/")) return "vendor-lykomi-shell";
|
|
83
83
|
if (pkg.startsWith("@lykomi/agent/")) return "vendor-lykomi-agent";
|
|
84
84
|
// Voice is optional at runtime and heavy at build; its own chunk
|
|
85
85
|
// keeps the main path light and the size gate honest.
|
|
86
|
-
if (pkg.startsWith("@lykomi/web-voice/")) return "vendor-
|
|
86
|
+
if (pkg.startsWith("@lykomi/web-voice/")) return "vendor-lykomi-voice";
|
|
87
87
|
// Before the react rule: recharts drags react-smooth + the d3
|
|
88
88
|
// sub-packages along, and "react-smooth" would land in
|
|
89
89
|
// vendor-react and push it past the chunk-size gate.
|
package/template/AGENTS.md
CHANGED
|
@@ -4,7 +4,11 @@ This is a LYKOMI UI starter app: React chat UI, AG-UI transport, and a Microsoft
|
|
|
4
4
|
|
|
5
5
|
```text
|
|
6
6
|
src/main.tsx LYKOMI UI shell bootstrap
|
|
7
|
-
src/pages/chat-page.tsx Chat
|
|
7
|
+
src/pages/chat-page.tsx Chat route
|
|
8
|
+
src/pages/starter-prompts.tsx Home prompts
|
|
9
|
+
src/pages/guide-page.tsx Example content page
|
|
10
|
+
src/starter-module.ts Navigation, agent context, interface menu
|
|
11
|
+
src/theme.ts App palette and branding
|
|
8
12
|
src/index.css Tailwind v4 plus @lykomi/web-ui/styles.css
|
|
9
13
|
agent/app/main.py FastAPI app and AG-UI endpoint
|
|
10
14
|
agent/app/agent.py FoundryChatClient + Agent configuration
|
|
@@ -28,7 +32,7 @@ Set these in `agent/.env`:
|
|
|
28
32
|
|
|
29
33
|
```bash
|
|
30
34
|
FOUNDRY_PROJECT_ENDPOINT=https://<account>.services.ai.azure.com/api/projects/<project>
|
|
31
|
-
FOUNDRY_MODEL=
|
|
35
|
+
FOUNDRY_MODEL=your-model-deployment
|
|
32
36
|
```
|
|
33
37
|
|
|
34
38
|
`FoundryChatClient` reads the Microsoft Agent Framework env vars directly. The Python agent passes `DefaultAzureCredential`; prefer `az login` locally and managed identity or service principal env vars in hosted environments.
|
|
@@ -50,11 +54,12 @@ FOUNDRY_MODEL=gpt-5.4
|
|
|
50
54
|
# UI
|
|
51
55
|
|
|
52
56
|
- Keep the starter chat-first. Do not add landing pages, marketing heroes, or domain-specific clutter.
|
|
53
|
-
- Configure quick starts in `src/pages/
|
|
54
|
-
- Configure app name, browser title, logos, colors, radius, and fonts
|
|
57
|
+
- Configure quick starts in `src/pages/starter-prompts.tsx`.
|
|
58
|
+
- Configure app name, browser title, logos, colors, radius, and fonts in `src/theme.ts`.
|
|
59
|
+
- Keep the Guide page and its `ChatModule` as an example of page context and persistent chat; replace them when building your app.
|
|
55
60
|
- Keep the default generated app name and browser title as `LYKOMI UI` unless the app is intentionally rebranded.
|
|
56
61
|
- Use LYKOMI UI components from `@lykomi/web-ui` and primitives from `@lykomi/web-primitives`.
|
|
57
|
-
- Keep `@source "../node_modules/@
|
|
62
|
+
- Keep `@source "../node_modules/@lykomi/.../dist"` entries in `src/index.css`; Tailwind v4 needs them to scan package component classes.
|
|
58
63
|
- Keep `@import "@lykomi/web-ui/styles.css";`; it provides the LYKOMI UI theme tokens.
|
|
59
64
|
|
|
60
65
|
# Attachments
|
package/template/README.md
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# __PROJECT_NAME__
|
|
2
2
|
|
|
3
|
-
LYKOMI UI starter: a
|
|
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
|
|
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
|
|
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,6 +42,8 @@ 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
|
|
@@ -65,33 +69,16 @@ Template changes such as new files or folder structure are not applied automatic
|
|
|
65
69
|
|
|
66
70
|
## Theme The App
|
|
67
71
|
|
|
68
|
-
|
|
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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
definition: {
|
|
79
|
-
branding: {
|
|
80
|
-
appName: "LYKOMI UI",
|
|
81
|
-
appTitle: "LYKOMI 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
84
|
Keep `@import "@lykomi/web-ui/styles.css";` in `src/index.css`; it exposes the package theme tokens to Tailwind.
|
|
@@ -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=
|
|
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:
|
|
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
|
|
@@ -176,14 +160,21 @@ Agent Framework connects the MCP server when the agent runs. The AG-UI endpoint
|
|
|
176
160
|
agent/ FastAPI + Microsoft Agent Framework AG-UI endpoint
|
|
177
161
|
agent/app/tools.py Add agent tools here
|
|
178
162
|
src/main.tsx LYKOMI UI shell bootstrap
|
|
179
|
-
src/
|
|
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
|
|
180
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=
|
|
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.
|
|
@@ -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
|
)
|
package/template/package.json
CHANGED
|
@@ -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
|
-
"@lykomi/agent": "0.
|
|
30
|
-
"@lykomi/web-primitives": "0.
|
|
31
|
-
"@lykomi/web-shell": "0.
|
|
32
|
-
"@lykomi/web-ui": "0.
|
|
33
|
-
"@lykomi/web-voice": "0.1.
|
|
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",
|
|
@@ -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
|
|
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>
|
|
Binary file
|
|
@@ -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.
|
package/template/src/index.css
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
@import "tailwindcss";
|
|
2
2
|
@import "@lykomi/web-ui/styles.css";
|
|
3
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
|
+
}
|
|
12
|
+
|
|
4
13
|
/* Tailwind v4 needs explicit @source paths to scan package output
|
|
5
14
|
* for utility classes. Without these, the LYKOMI UI components render
|
|
6
15
|
* without spacing, borders, or theme tokens. */
|
package/template/src/main.tsx
CHANGED
|
@@ -2,7 +2,11 @@ import "./index.css";
|
|
|
2
2
|
import { bootstrapShell } from "@lykomi/web-shell";
|
|
3
3
|
import type { AuthAdapter } from "@lykomi/web-shell";
|
|
4
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: "LYKOMI UI",
|
|
40
|
-
appTitle: "LYKOMI UI",
|
|
41
|
-
},
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
],
|
|
40
|
+
theme: [starterTheme],
|
|
45
41
|
});
|
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
import { ChatContent } from "@lykomi/web-ui";
|
|
2
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
|
-
];
|
|
8
|
-
|
|
9
3
|
export function ChatPage() {
|
|
10
|
-
return <ChatContent
|
|
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
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
|
|
18
|
-
//
|
|
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
|
];
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ThreadPrimitive } from "@assistant-ui/react";
|
|
2
|
+
import { Button } from "@lykomi/web-primitives";
|
|
3
|
+
import { useChatBubbleStore } from "@lykomi/web-ui";
|
|
4
|
+
|
|
5
|
+
const prompts = [
|
|
6
|
+
"Say hello world using the hello_world tool",
|
|
7
|
+
"Check the weather in Seattle",
|
|
8
|
+
"What tools can you use?",
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
/** One click demonstrates a real AG-UI turn with the bundled tools. */
|
|
12
|
+
export function StarterPrompts() {
|
|
13
|
+
return (
|
|
14
|
+
<div className="flex flex-wrap justify-center gap-2" aria-label="Try a prompt">
|
|
15
|
+
{prompts.map((prompt) => (
|
|
16
|
+
<ThreadPrimitive.Suggestion
|
|
17
|
+
key={prompt}
|
|
18
|
+
prompt={prompt}
|
|
19
|
+
send
|
|
20
|
+
asChild
|
|
21
|
+
onClick={() => useChatBubbleStore.getState().open()}
|
|
22
|
+
>
|
|
23
|
+
<Button variant="outline" size="sm" className="h-auto min-h-9 text-wrap py-2 text-sm">
|
|
24
|
+
{prompt}
|
|
25
|
+
</Button>
|
|
26
|
+
</ThreadPrimitive.Suggestion>
|
|
27
|
+
))}
|
|
28
|
+
</div>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { guideWindows } from "./pages/guide-windows.ts";
|
|
2
|
+
import { useEffect } from "react";
|
|
3
|
+
import { BookOpen } from "lucide-react";
|
|
4
|
+
import { useNavigate } from "react-router";
|
|
5
|
+
import { defineChatModule } from "@lykomi/web-shell";
|
|
6
|
+
import { useNavStore } from "@lykomi/web-ui";
|
|
7
|
+
|
|
8
|
+
function useStarterNavigation() {
|
|
9
|
+
const navigate = useNavigate();
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
const protect = (event: BeforeUnloadEvent) => {
|
|
12
|
+
if (guideWindows.get().tabs.some((tab) => tab.dirty)) {
|
|
13
|
+
event.preventDefault();
|
|
14
|
+
event.returnValue = "";
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
window.addEventListener("beforeunload", protect);
|
|
18
|
+
useNavStore.getState().addGroup({
|
|
19
|
+
label: "Starter",
|
|
20
|
+
hideLabel: true,
|
|
21
|
+
items: [
|
|
22
|
+
{
|
|
23
|
+
to: "/guide",
|
|
24
|
+
label: "Guide",
|
|
25
|
+
icon: BookOpen,
|
|
26
|
+
description: "Starter guide for the agent, tools, and UI modules",
|
|
27
|
+
contextInstructions:
|
|
28
|
+
"The user is reading the starter guide. Explain how to replace the sample tools, " +
|
|
29
|
+
"add a page, or connect a service when asked. Do not claim a service is configured " +
|
|
30
|
+
"unless the user has supplied its settings.",
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
});
|
|
34
|
+
useNavStore.getState().setUserMenuAction({
|
|
35
|
+
id: "starter-guide",
|
|
36
|
+
label: "About this starter",
|
|
37
|
+
onSelect: () => navigate("/guide"),
|
|
38
|
+
});
|
|
39
|
+
return () => {
|
|
40
|
+
window.removeEventListener("beforeunload", protect);
|
|
41
|
+
useNavStore.getState().removeGroup("Starter");
|
|
42
|
+
useNavStore.getState().removeUserMenuAction("starter-guide");
|
|
43
|
+
};
|
|
44
|
+
}, [navigate]);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** A small module demonstrates how an app owns its routes and navigation. */
|
|
48
|
+
export const starterModule = defineChatModule({
|
|
49
|
+
name: "starter-guide",
|
|
50
|
+
useInit: useStarterNavigation,
|
|
51
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { ThemeLayer } from "@lykomi/web-ui";
|
|
2
|
+
|
|
3
|
+
/** The LYKOMI starter palette. Replace or extend this layer for your app. */
|
|
4
|
+
export const starterTheme: ThemeLayer = {
|
|
5
|
+
id: "lykomi-starter",
|
|
6
|
+
name: "LYKOMI UI",
|
|
7
|
+
definition: {
|
|
8
|
+
light: {
|
|
9
|
+
background: "#FAFAF9",
|
|
10
|
+
foreground: "#0B0C0E",
|
|
11
|
+
card: "#FFFFFF",
|
|
12
|
+
cardForeground: "#0B0C0E",
|
|
13
|
+
primary: "#3F666B",
|
|
14
|
+
primaryForeground: "#FFFFFF",
|
|
15
|
+
secondary: "#E8E8E5",
|
|
16
|
+
secondaryForeground: "#0B0C0E",
|
|
17
|
+
muted: "#EDEDEB",
|
|
18
|
+
mutedForeground: "#626369",
|
|
19
|
+
accent: "#E5EEEF",
|
|
20
|
+
accentForeground: "#3F666B",
|
|
21
|
+
border: "#DCDDD9",
|
|
22
|
+
input: "#DCDDD9",
|
|
23
|
+
ring: "#3F666B",
|
|
24
|
+
},
|
|
25
|
+
dark: {
|
|
26
|
+
background: "#0D0D10",
|
|
27
|
+
foreground: "#EBEBF4",
|
|
28
|
+
card: "#151519",
|
|
29
|
+
cardForeground: "#EBEBF4",
|
|
30
|
+
primary: "#F4F4F4",
|
|
31
|
+
primaryForeground: "#101010",
|
|
32
|
+
secondary: "#1E1E22",
|
|
33
|
+
secondaryForeground: "#EBEBF4",
|
|
34
|
+
muted: "#1E1E22",
|
|
35
|
+
mutedForeground: "#B8B8C2",
|
|
36
|
+
accent: "#1E1D2C",
|
|
37
|
+
accentForeground: "#EBEBF4",
|
|
38
|
+
border: "#2A2A2E",
|
|
39
|
+
input: "#2A2A2E",
|
|
40
|
+
ring: "#8C8C8C",
|
|
41
|
+
},
|
|
42
|
+
sidebar: {
|
|
43
|
+
light: {
|
|
44
|
+
sidebar: "#F2F2F0",
|
|
45
|
+
sidebarForeground: "#0B0C0E",
|
|
46
|
+
sidebarPrimary: "#3F666B",
|
|
47
|
+
sidebarPrimaryForeground: "#FFFFFF",
|
|
48
|
+
sidebarAccent: "#E4E4E1",
|
|
49
|
+
sidebarAccentForeground: "#0B0C0E",
|
|
50
|
+
sidebarSelected: "#DCE9E7",
|
|
51
|
+
sidebarSelectedForeground: "#3F666B",
|
|
52
|
+
sidebarIcon: "#626369",
|
|
53
|
+
sidebarBorder: "#DCDDD9",
|
|
54
|
+
sidebarRing: "#3F666B",
|
|
55
|
+
},
|
|
56
|
+
dark: {
|
|
57
|
+
sidebar: "#111115",
|
|
58
|
+
sidebarForeground: "#E5E5E7",
|
|
59
|
+
sidebarPrimary: "#F4F4F4",
|
|
60
|
+
sidebarPrimaryForeground: "#101010",
|
|
61
|
+
sidebarAccent: "#1E1E22",
|
|
62
|
+
sidebarAccentForeground: "#F4F4F4",
|
|
63
|
+
sidebarSelected: "#1E1D2C",
|
|
64
|
+
sidebarSelectedForeground: "#F4F4F4",
|
|
65
|
+
sidebarIcon: "#C0C0C3",
|
|
66
|
+
sidebarBorder: "#2A2A2E",
|
|
67
|
+
sidebarRing: "#B4B4B4",
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
typography: {
|
|
71
|
+
bodyFont: '"Manrope", ui-sans-serif, system-ui, sans-serif',
|
|
72
|
+
headingFont: '"Manrope", ui-sans-serif, system-ui, sans-serif',
|
|
73
|
+
},
|
|
74
|
+
radius: { radius: "0.375rem" },
|
|
75
|
+
branding: {
|
|
76
|
+
appName: "LYKOMI UI",
|
|
77
|
+
appTitle: "LYKOMI UI",
|
|
78
|
+
faviconUrl: "/favicon.svg",
|
|
79
|
+
darkFaviconUrl: "/favicon-dark.svg",
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
};
|
package/template/vite.config.ts
CHANGED
|
@@ -61,11 +61,11 @@ export default defineConfig({
|
|
|
61
61
|
)
|
|
62
62
|
return "lykomi-workbench";
|
|
63
63
|
if (pkg.startsWith("@lykomi/web-ui/")) return "vendor-lykomi-ui";
|
|
64
|
-
if (pkg.startsWith("@lykomi/web-shell/")) return "vendor-
|
|
64
|
+
if (pkg.startsWith("@lykomi/web-shell/")) return "vendor-lykomi-shell";
|
|
65
65
|
if (pkg.startsWith("@lykomi/agent/")) return "vendor-lykomi-agent";
|
|
66
66
|
// Voice is optional at runtime and heavy at build; its own chunk
|
|
67
67
|
// keeps the main path light and the size gate honest.
|
|
68
|
-
if (pkg.startsWith("@lykomi/web-voice/")) return "vendor-
|
|
68
|
+
if (pkg.startsWith("@lykomi/web-voice/")) return "vendor-lykomi-voice";
|
|
69
69
|
// Before the react rule: recharts drags react-smooth + the d3
|
|
70
70
|
// sub-packages along, and "react-smooth" would land in
|
|
71
71
|
// vendor-react and push it past the chunk-size gate.
|