@adobe/data 0.9.39 → 0.9.41
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/AGENTS.md +84 -16
- package/package.json +1 -1
- package/references/data-lit/package.json +1 -1
- package/references/data-lit-tictactoe/package.json +22 -0
- package/references/data-lit-tictactoe/src/elements/tictactoe-app/tictactoe-app.css.ts +11 -0
- package/references/data-lit-tictactoe/src/elements/tictactoe-app/tictactoe-app.ts +22 -0
- package/references/data-lit-tictactoe/src/elements/tictactoe-board/tictactoe-board-presentation.ts +13 -0
- package/references/data-lit-tictactoe/src/elements/tictactoe-board/tictactoe-board.css.ts +17 -0
- package/references/data-lit-tictactoe/src/elements/tictactoe-board/tictactoe-board.ts +18 -0
- package/references/data-lit-tictactoe/src/elements/tictactoe-cell/tictactoe-cell-presentation.ts +22 -0
- package/references/data-lit-tictactoe/src/elements/tictactoe-cell/tictactoe-cell.css.ts +31 -0
- package/references/data-lit-tictactoe/src/elements/tictactoe-cell/tictactoe-cell.ts +41 -0
- package/references/data-lit-tictactoe/src/elements/tictactoe-hud/tictactoe-hud-presentation.ts +16 -0
- package/references/data-lit-tictactoe/src/elements/tictactoe-hud/tictactoe-hud.css.ts +18 -0
- package/references/data-lit-tictactoe/src/elements/tictactoe-hud/tictactoe-hud.ts +44 -0
- package/references/data-lit-tictactoe/src/main.ts +9 -0
- package/references/data-lit-tictactoe/src/state/agent-plugin.ts +117 -0
- package/references/data-lit-tictactoe/src/state/tictactoe-plugin.ts +56 -0
- package/references/data-lit-tictactoe/src/tictactoe-element.ts +10 -0
- package/references/data-lit-tictactoe/src/types/board-cell.ts +5 -0
- package/references/data-lit-tictactoe/src/types/board-state/board-state.ts +7 -0
- package/references/data-lit-tictactoe/src/types/board-state/create-initial-board.ts +5 -0
- package/references/data-lit-tictactoe/src/types/board-state/current-player.ts +13 -0
- package/references/data-lit-tictactoe/src/types/board-state/derive-status.ts +12 -0
- package/references/data-lit-tictactoe/src/types/board-state/get-cell.ts +7 -0
- package/references/data-lit-tictactoe/src/types/board-state/get-move-count.ts +6 -0
- package/references/data-lit-tictactoe/src/types/board-state/get-winner.ts +14 -0
- package/references/data-lit-tictactoe/src/types/board-state/get-winning-line.ts +25 -0
- package/references/data-lit-tictactoe/src/types/board-state/is-board-full.ts +5 -0
- package/references/data-lit-tictactoe/src/types/board-state/is-cell-playable.ts +13 -0
- package/references/data-lit-tictactoe/src/types/board-state/is-cell-winning.ts +9 -0
- package/references/data-lit-tictactoe/src/types/board-state/is-game-over.ts +8 -0
- package/references/data-lit-tictactoe/src/types/board-state/public.ts +15 -0
- package/references/data-lit-tictactoe/src/types/board-state/schema.ts +10 -0
- package/references/data-lit-tictactoe/src/types/board-state/set-board-cell.ts +13 -0
- package/references/data-lit-tictactoe/src/types/game-status.ts +3 -0
- package/references/data-lit-tictactoe/src/types/move-reject-reason.ts +7 -0
- package/references/data-lit-tictactoe/src/types/play-move-args/can-play-move.ts +33 -0
- package/references/data-lit-tictactoe/src/types/play-move-args/play-move-args.ts +4 -0
- package/references/{data-lit-todo/src/elements/todo-undo-redo/index.ts → data-lit-tictactoe/src/types/play-move-args/public.ts} +1 -1
- package/references/data-lit-tictactoe/src/types/player-mark/player-mark.ts +7 -0
- package/references/{data-lit-todo/src/elements/todo-row/index.ts → data-lit-tictactoe/src/types/player-mark/public.ts} +2 -1
- package/references/data-lit-tictactoe/src/types/player-mark/schema.ts +9 -0
- package/references/data-lit-tictactoe/src/types/winning-line.ts +3 -0
- package/references/data-lit-tictactoe/tsconfig.json +16 -0
- package/references/data-react/package.json +1 -1
- package/references/data-react-hello/package.json +1 -1
- package/references/data-react-hello/src/{App.tsx → app.tsx} +2 -2
- package/references/data-react-hello/src/components/counter/counter-presentation.tsx +13 -0
- package/references/data-react-hello/src/components/counter/counter.tsx +26 -0
- package/references/data-react-hello/src/main.tsx +1 -1
- package/references/data-react-hello/src/state/use-counter-database.ts +6 -0
- package/references/data-react-pixie/package.json +1 -1
- package/references/data-react-pixie/src/{App.tsx → app.tsx} +7 -7
- package/references/data-react-pixie/src/{filter-toggle.tsx → components/filter-selector/filter-selector-presentation.tsx} +9 -15
- package/references/data-react-pixie/src/components/filter-selector/filter-selector.tsx +22 -0
- package/references/data-react-pixie/src/{filters.ts → components/pixie-scene/pixie-filters.ts} +3 -1
- package/references/data-react-pixie/src/components/pixie-scene/pixie-scene-presentation.tsx +18 -0
- package/references/data-react-pixie/src/components/pixie-scene/pixie-scene.tsx +26 -0
- package/references/data-react-pixie/src/components/pixie-scene/pixie-tick.tsx +12 -0
- package/references/data-react-pixie/src/components/sprite/sprite-presentation.tsx +39 -0
- package/references/data-react-pixie/src/components/sprite/sprite.tsx +37 -0
- package/references/data-react-pixie/src/{hooks/use-texture.ts → components/sprite/use-sprite-texture.ts} +3 -1
- package/references/data-react-pixie/src/main.tsx +1 -1
- package/references/data-react-pixie/src/{pixie-plugin.ts → state/pixie-plugin.ts} +4 -7
- package/references/data-react-pixie/src/state/use-pixie-database.ts +6 -0
- package/references/data-react-pixie/src/types/filter-type.ts +3 -0
- package/references/data-react-pixie/src/types/sprite-type/image.ts +14 -0
- package/references/data-react-pixie/src/types/sprite-type/public.ts +4 -0
- package/references/data-react-pixie/src/types/sprite-type/schema.ts +5 -0
- package/references/data-react-pixie/src/types/sprite-type/sprite-type.ts +7 -0
- package/references/data-lit-todo/package.json +0 -30
- package/references/data-lit-todo/src/elements/todo-list/todo-list-presentation.ts +0 -22
- package/references/data-lit-todo/src/elements/todo-list/todo-list.css.ts +0 -12
- package/references/data-lit-todo/src/elements/todo-list/todo-list.ts +0 -45
- package/references/data-lit-todo/src/elements/todo-row/todo-row-presentation.ts +0 -68
- package/references/data-lit-todo/src/elements/todo-row/todo-row.css.ts +0 -49
- package/references/data-lit-todo/src/elements/todo-row/todo-row.ts +0 -46
- package/references/data-lit-todo/src/elements/todo-toolbar/index.ts +0 -2
- package/references/data-lit-todo/src/elements/todo-toolbar/todo-toolbar-presentation.ts +0 -55
- package/references/data-lit-todo/src/elements/todo-toolbar/todo-toolbar.css.ts +0 -34
- package/references/data-lit-todo/src/elements/todo-toolbar/todo-toolbar.ts +0 -62
- package/references/data-lit-todo/src/elements/todo-undo-redo/todo-undo-redo-presentation.ts +0 -41
- package/references/data-lit-todo/src/elements/todo-undo-redo/todo-undo-redo.css.ts +0 -12
- package/references/data-lit-todo/src/elements/todo-undo-redo/todo-undo-redo.ts +0 -37
- package/references/data-lit-todo/src/index.ts +0 -9
- package/references/data-lit-todo/src/main.ts +0 -29
- package/references/data-lit-todo/src/sample-types.ts +0 -14
- package/references/data-lit-todo/src/services/dependent-state-service/create-dependent-state-service.ts +0 -8
- package/references/data-lit-todo/src/services/dependent-state-service/dependent-state/all-todos.ts +0 -5
- package/references/data-lit-todo/src/services/dependent-state-service/dependent-state/complete-todos.ts +0 -5
- package/references/data-lit-todo/src/services/dependent-state-service/dependent-state/incomplete-todos.ts +0 -5
- package/references/data-lit-todo/src/services/dependent-state-service/dependent-state/index.ts +0 -4
- package/references/data-lit-todo/src/services/dependent-state-service/dependent-state-service.ts +0 -4
- package/references/data-lit-todo/src/services/main-service/create-main-service.ts +0 -53
- package/references/data-lit-todo/src/services/main-service/todo-main-service.ts +0 -20
- package/references/data-lit-todo/src/services/state-service/create-todo-database.ts +0 -16
- package/references/data-lit-todo/src/services/state-service/create-todo-store.ts +0 -28
- package/references/data-lit-todo/src/services/state-service/todo-state-service.ts +0 -9
- package/references/data-lit-todo/src/services/state-service/transactions/create-bulk-todos.ts +0 -12
- package/references/data-lit-todo/src/services/state-service/transactions/create-todo.test.ts +0 -17
- package/references/data-lit-todo/src/services/state-service/transactions/create-todo.ts +0 -15
- package/references/data-lit-todo/src/services/state-service/transactions/delete-all-todos.ts +0 -18
- package/references/data-lit-todo/src/services/state-service/transactions/delete-todo.test.ts +0 -25
- package/references/data-lit-todo/src/services/state-service/transactions/delete-todo.ts +0 -11
- package/references/data-lit-todo/src/services/state-service/transactions/drag-todo.ts +0 -19
- package/references/data-lit-todo/src/services/state-service/transactions/index.ts +0 -8
- package/references/data-lit-todo/src/services/state-service/transactions/reorder-todos.ts +0 -13
- package/references/data-lit-todo/src/services/state-service/transactions/toggle-complete.test.ts +0 -78
- package/references/data-lit-todo/src/services/state-service/transactions/toggle-complete.ts +0 -15
- package/references/data-lit-todo/src/todo-element.ts +0 -6
- package/references/data-lit-todo/src/todo-host.ts +0 -29
- package/references/data-lit-todo/src/todo-main-element.ts +0 -40
- package/references/data-lit-todo/src/todo-sample.ts +0 -21
- package/references/data-lit-todo/tsconfig.json +0 -10
- package/references/data-react-hello/src/Counter.tsx +0 -21
- package/references/data-react-pixie/src/hooks/use-database.ts +0 -6
- package/references/data-react-pixie/src/pixi-react.d.ts +0 -1
- package/references/data-react-pixie/src/sprite-container.tsx +0 -23
- package/references/data-react-pixie/src/sprite-urls.ts +0 -9
- package/references/data-react-pixie/src/sprite.tsx +0 -35
- package/references/data-react-pixie/src/tick.tsx +0 -10
- /package/references/data-react-hello/src/{counter-plugin.ts → state/counter-plugin.ts} +0 -0
- /package/references/data-react-pixie/src/{bunny.png → types/sprite-type/bunny.png} +0 -0
- /package/references/data-react-pixie/src/{fox.png → types/sprite-type/fox.png} +0 -0
package/AGENTS.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
Adobe data-oriented programming library. Use this file when editing code in this package or when consuming `@adobe/data` in an application.
|
|
4
4
|
|
|
5
|
+
## References
|
|
6
|
+
|
|
7
|
+
Source for peer packages and sample apps is included under `references/` (when present). Use these as living reference code:
|
|
8
|
+
|
|
9
|
+
- **references/data-lit** — Support functions for Lit based projects.
|
|
10
|
+
- **references/data-lit-tictactoe** — **Exemplary types and state.** Tic-Tac-Toe sample with Lit UI. The `types/` (BoardState namespace, PlayMoveArgs, etc.) and `state/` (tictactoe-plugin, agent-plugin) are a very good reference for proper type namespaces, ECS resources, computed observables, and transactions — even for consumers not using Lit.
|
|
11
|
+
- **references/data-react** — Support functions for React based projects.
|
|
12
|
+
- **references/data-react-hello** — Very simple react hello world sample.
|
|
13
|
+
- **references/data-react-pixie** — Simple sample for react pixie based 2d games.
|
|
14
|
+
|
|
5
15
|
## Stack
|
|
6
16
|
|
|
7
17
|
- **Package manager:** pnpm
|
|
@@ -52,10 +62,52 @@ Source lives under `src/` with one main `index.ts` per area; built output is in
|
|
|
52
62
|
|
|
53
63
|
- **Store:** low-level, synchronous, direct mutations. No transactions, no observability.
|
|
54
64
|
- **Database:** wraps a Store; **transaction-based** and **observable**. All mutations go through transaction functions; undo/redo and observers are supported.
|
|
55
|
-
- **Plugins:** use `Database.Plugin.create()`. Property order is **enforced at runtime
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
65
|
+
- **Plugins:** use `Database.Plugin.create()` from `@adobe/data/ecs`. Property order is **enforced at runtime**; wrong order throws.
|
|
66
|
+
|
|
67
|
+
### Plugin property order (all optional)
|
|
68
|
+
|
|
69
|
+
`extends` → `services` → `components` → `resources` → `archetypes` → `computed` → `transactions` → `actions` → `systems`
|
|
70
|
+
|
|
71
|
+
| Property | Purpose |
|
|
72
|
+
|----------|---------|
|
|
73
|
+
| `extends` | Base plugin to extend (single parent only) |
|
|
74
|
+
| `services` | `(db) => ServiceInstance` — singleton service factories; extended plugin services initialize first |
|
|
75
|
+
| `components` | Schema object for ECS component data. Use `transient: true` for non-persistable values (DOM refs, HTML elements). |
|
|
76
|
+
| `resources` | `{ default: value as Type }` — global state. Use `as Type` for compile-time type; use `null as unknown as Type` for resources initialized later in a system. |
|
|
77
|
+
| `archetypes` | `['comp1', 'comp2']` — standard ECS archetypes for querying and inserting related components |
|
|
78
|
+
| `computed` | `(db) => Observe<T>` — factory returning observable; receives full db |
|
|
79
|
+
| `transactions` | `(store, payload) => void` — synchronous, deterministic atomic mutations |
|
|
80
|
+
| `actions` | `(db, payload) => T` — general functions; may be async |
|
|
81
|
+
| `systems` | `{ create: (db) => fn | void }` — per-frame (60fps) or init-only; optional `schedule: { before, after, during }` for ordering |
|
|
82
|
+
|
|
83
|
+
### Composition
|
|
84
|
+
|
|
85
|
+
- **Single extension:** one plugin via `extends`.
|
|
86
|
+
- **Multiple peers:** use `Database.Plugin.combine(aPlugin, bPlugin, ...)` — `extends` accepts only one plugin.
|
|
87
|
+
- **Type utilities:** `Database.Plugin.ToDatabase<typeof myPlugin>`, `Database.Plugin.ToStore<typeof myPlugin>`.
|
|
88
|
+
|
|
89
|
+
### Transactions and Store API
|
|
90
|
+
|
|
91
|
+
Transactions receive `(store, payload)`. Mutate via:
|
|
92
|
+
|
|
93
|
+
- `store.update(entity, data)` — update entity components
|
|
94
|
+
- `store.resources.x = value` — mutate resources
|
|
95
|
+
- `store.get(entity, 'component')` — read component value
|
|
96
|
+
- `store.read(entity)` — read all entity component values
|
|
97
|
+
- `store.read(entity, archetype)` — read entity components in archetype
|
|
98
|
+
- `store.select(archetype.components, { where })` — query entities
|
|
99
|
+
|
|
100
|
+
### Actions
|
|
101
|
+
|
|
102
|
+
- May be async. Call **at most one transaction per action** to keep undo/redo correct.
|
|
103
|
+
- UI must **never consume return values** — call for side effects only. Data down via Observe, actions up as void.
|
|
104
|
+
|
|
105
|
+
### Plugin naming
|
|
106
|
+
|
|
107
|
+
- **File:** `*-plugin.ts` (kebab-case), e.g. `layout-plugin.ts`
|
|
108
|
+
- **Export:** `*Plugin` (camelCase), e.g. `layoutPlugin`
|
|
109
|
+
- **System:** `plugin_name__system` (snake_case, double underscore)
|
|
110
|
+
- **Init system:** `plugin_name_initialize`
|
|
59
111
|
|
|
60
112
|
## Observables
|
|
61
113
|
|
|
@@ -71,21 +123,37 @@ Source lives under `src/` with one main `index.ts` per area; built output is in
|
|
|
71
123
|
|
|
72
124
|
## Data and schemas
|
|
73
125
|
|
|
74
|
-
- **Data:** immutable, JSON-serializable values.
|
|
75
|
-
- **Schemas:** JSON Schema with `as const` so `FromSchema` (or equivalent) can derive TypeScript types. Use schema namespaces for component
|
|
126
|
+
- **Data:** immutable, JSON-serializable values.
|
|
127
|
+
- **Schemas:** JSON Schema with `as const` so `FromSchema` (or equivalent) can derive TypeScript types. Use schema namespaces for component definitions in ECS if we the types are numeric and we want them stored in linear memory for performance. See Vec2, Vec3 etc. Follow those patterns for numeric values. Do not use an explicit schema for resources, just use `{ default: value as Type }` since there is only one value we don't need linear memory layout.
|
|
128
|
+
|
|
129
|
+
### Data modeling example
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
import { Database } from '@adobe/data/ecs';
|
|
133
|
+
import { F32, Vec3, Vec4 } from '@adobe/data/math';
|
|
134
|
+
|
|
135
|
+
const particleDataPlugin = Database.Plugin.create({
|
|
136
|
+
components: {
|
|
137
|
+
position: Vec3.schema,
|
|
138
|
+
velocity: Vec3.schema,
|
|
139
|
+
color: Vec4.schema,
|
|
140
|
+
mass: F32.schema,
|
|
141
|
+
},
|
|
142
|
+
resources: {
|
|
143
|
+
gravity: { default: 9.8 as number },
|
|
144
|
+
},
|
|
145
|
+
archetypes: {
|
|
146
|
+
Particle: ['position', 'velocity', 'color', 'mass'],
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
- **Components:** per-entity data. Use numeric schemas from `@adobe/data/math` or type namespace schemas for custom shapes.
|
|
152
|
+
- **Resources:** global state. Use only `{ default: value as Type }`.
|
|
153
|
+
- **Archetypes:** one per entity kind. List all components that kind requires.
|
|
76
154
|
|
|
77
155
|
## Gotchas
|
|
78
156
|
|
|
79
157
|
- **Breaking changes:** Before 1.0.0, minor version bumps may include breaking API changes.
|
|
80
158
|
- **ECS plugin order:** Reordering plugin properties (e.g. putting `transactions` before `components`) will throw at runtime.
|
|
81
159
|
- **BlobStore / BlobRef:** Prefer BlobRef for persistence and cache keys; resolve to Blob when needed.
|
|
82
|
-
|
|
83
|
-
## References
|
|
84
|
-
|
|
85
|
-
Source for peer packages and sample apps is included under `references/` (when present). Use these as living reference code:
|
|
86
|
-
|
|
87
|
-
- **references/data-lit** — Extra support for Lit-based projects.
|
|
88
|
-
- **references/data-react** — Same but for React.
|
|
89
|
-
- **references/data-lit-todo** — Sample with best practices for Lit apps.
|
|
90
|
-
- **references/data-react-hello** — Minimal starting React sample.
|
|
91
|
-
- **references/data-react-pixie** — Sample of using React Pixie for 2D rendering.
|
package/package.json
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "data-lit-tictactoe",
|
|
3
|
+
"version": "0.9.41",
|
|
4
|
+
"description": "Tic-Tac-Toe sample - Lit web components with @adobe/data-lit and AgenticService",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"private": true,
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "vite build",
|
|
9
|
+
"dev": "vite",
|
|
10
|
+
"publish-public": "true"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@adobe/data": "workspace:*",
|
|
14
|
+
"@adobe/data-lit": "workspace:*",
|
|
15
|
+
"lit": "^3.3.1"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"typescript": "^5.8.3",
|
|
19
|
+
"vite": "^5.1.1",
|
|
20
|
+
"vite-plugin-checker": "^0.12.0"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
import { customElement } from "lit/decorators.js";
|
|
4
|
+
import { html } from "lit";
|
|
5
|
+
import { TictactoeElement } from "../../tictactoe-element.js";
|
|
6
|
+
import { styles } from "./tictactoe-app.css.js";
|
|
7
|
+
import "../tictactoe-board/tictactoe-board.js";
|
|
8
|
+
import "../tictactoe-hud/tictactoe-hud.js";
|
|
9
|
+
|
|
10
|
+
export const tagName = "tictactoe-app";
|
|
11
|
+
|
|
12
|
+
@customElement(tagName)
|
|
13
|
+
export class TictactoeApp extends TictactoeElement {
|
|
14
|
+
static styles = styles;
|
|
15
|
+
|
|
16
|
+
render() {
|
|
17
|
+
return html`
|
|
18
|
+
<tictactoe-board></tictactoe-board>
|
|
19
|
+
<tictactoe-hud></tictactoe-hud>
|
|
20
|
+
`;
|
|
21
|
+
}
|
|
22
|
+
}
|
package/references/data-lit-tictactoe/src/elements/tictactoe-board/tictactoe-board-presentation.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
import { html } from "lit";
|
|
4
|
+
|
|
5
|
+
export function render() {
|
|
6
|
+
return html`
|
|
7
|
+
<div class="board">
|
|
8
|
+
${[0, 1, 2, 3, 4, 5, 6, 7, 8].map(
|
|
9
|
+
(index) => html`<tictactoe-cell .index=${index}></tictactoe-cell>`,
|
|
10
|
+
)}
|
|
11
|
+
</div>
|
|
12
|
+
`;
|
|
13
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
import { css } from "lit";
|
|
4
|
+
|
|
5
|
+
export const styles = css`
|
|
6
|
+
:host {
|
|
7
|
+
display: block;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.board {
|
|
11
|
+
display: grid;
|
|
12
|
+
grid-template-columns: repeat(3, 100px);
|
|
13
|
+
grid-template-rows: repeat(3, 100px);
|
|
14
|
+
gap: 4px;
|
|
15
|
+
width: fit-content;
|
|
16
|
+
}
|
|
17
|
+
`;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
import { customElement } from "lit/decorators.js";
|
|
4
|
+
import { TictactoeElement } from "../../tictactoe-element.js";
|
|
5
|
+
import { styles } from "./tictactoe-board.css.js";
|
|
6
|
+
import * as presentation from "./tictactoe-board-presentation.js";
|
|
7
|
+
import "../tictactoe-cell/tictactoe-cell.js";
|
|
8
|
+
|
|
9
|
+
export const tagName = "tictactoe-board";
|
|
10
|
+
|
|
11
|
+
@customElement(tagName)
|
|
12
|
+
export class TictactoeBoard extends TictactoeElement {
|
|
13
|
+
static styles = styles;
|
|
14
|
+
|
|
15
|
+
render() {
|
|
16
|
+
return presentation.render();
|
|
17
|
+
}
|
|
18
|
+
}
|
package/references/data-lit-tictactoe/src/elements/tictactoe-cell/tictactoe-cell-presentation.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
import { html } from "lit";
|
|
4
|
+
|
|
5
|
+
export function render(args: {
|
|
6
|
+
cell: string;
|
|
7
|
+
isWinning: boolean;
|
|
8
|
+
isPlayable: boolean;
|
|
9
|
+
playMove: () => void;
|
|
10
|
+
}) {
|
|
11
|
+
const { cell, isWinning, isPlayable, playMove } = args;
|
|
12
|
+
const hasMark = cell === "X" || cell === "O";
|
|
13
|
+
|
|
14
|
+
return html`
|
|
15
|
+
<div
|
|
16
|
+
class="cell ${isWinning ? "winning" : ""} ${isPlayable ? "playable" : ""}"
|
|
17
|
+
@click=${() => isPlayable && playMove()}
|
|
18
|
+
>
|
|
19
|
+
${hasMark ? cell : ""}
|
|
20
|
+
</div>
|
|
21
|
+
`;
|
|
22
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
import { css } from "lit";
|
|
4
|
+
|
|
5
|
+
export const styles = css`
|
|
6
|
+
:host {
|
|
7
|
+
display: block;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.cell {
|
|
11
|
+
width: 100px;
|
|
12
|
+
height: 100px;
|
|
13
|
+
display: flex;
|
|
14
|
+
align-items: center;
|
|
15
|
+
justify-content: center;
|
|
16
|
+
font-size: 48px;
|
|
17
|
+
font-weight: bold;
|
|
18
|
+
background: #1f2937;
|
|
19
|
+
border: 2px solid #6b7280;
|
|
20
|
+
color: #e5e7eb;
|
|
21
|
+
box-sizing: border-box;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.cell.playable {
|
|
25
|
+
cursor: pointer;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.cell.winning {
|
|
29
|
+
background: #2e7d32;
|
|
30
|
+
}
|
|
31
|
+
`;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
import { customElement, property } from "lit/decorators.js";
|
|
4
|
+
import { useObservableValues } from "@adobe/data-lit";
|
|
5
|
+
import { BoardState } from "../../types/board-state/board-state.js";
|
|
6
|
+
import { TictactoeElement } from "../../tictactoe-element.js";
|
|
7
|
+
import { styles } from "./tictactoe-cell.css.js";
|
|
8
|
+
import * as presentation from "./tictactoe-cell-presentation.js";
|
|
9
|
+
|
|
10
|
+
export const tagName = "tictactoe-cell";
|
|
11
|
+
|
|
12
|
+
@customElement(tagName)
|
|
13
|
+
export class TictactoeCell extends TictactoeElement {
|
|
14
|
+
static styles = styles;
|
|
15
|
+
|
|
16
|
+
@property({ type: Number })
|
|
17
|
+
declare index: number;
|
|
18
|
+
|
|
19
|
+
render() {
|
|
20
|
+
const values = useObservableValues(
|
|
21
|
+
() => ({
|
|
22
|
+
board: this.service.observe.resources.board,
|
|
23
|
+
}),
|
|
24
|
+
[],
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
const board = values?.board ?? " ";
|
|
28
|
+
const cell = BoardState.getCell(board, this.index);
|
|
29
|
+
const isWinning = BoardState.isCellWinning(board, this.index);
|
|
30
|
+
const isPlayable = BoardState.isCellPlayable(board, this.index);
|
|
31
|
+
|
|
32
|
+
return presentation.render({
|
|
33
|
+
cell,
|
|
34
|
+
isWinning,
|
|
35
|
+
isPlayable,
|
|
36
|
+
playMove: () => {
|
|
37
|
+
this.service.transactions.playMove({ index: this.index });
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
package/references/data-lit-tictactoe/src/elements/tictactoe-hud/tictactoe-hud-presentation.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
import { html } from "lit";
|
|
4
|
+
|
|
5
|
+
export function render(args: {
|
|
6
|
+
statusText: string;
|
|
7
|
+
restartGame: () => void;
|
|
8
|
+
}) {
|
|
9
|
+
const { statusText, restartGame } = args;
|
|
10
|
+
return html`
|
|
11
|
+
<div class="hud">
|
|
12
|
+
<span>${statusText}</span>
|
|
13
|
+
<button type="button" @click=${restartGame}>Restart</button>
|
|
14
|
+
</div>
|
|
15
|
+
`;
|
|
16
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
import { css } from "lit";
|
|
4
|
+
|
|
5
|
+
export const styles = css`
|
|
6
|
+
.hud {
|
|
7
|
+
padding: 0.5rem 1rem;
|
|
8
|
+
font-family: system-ui, sans-serif;
|
|
9
|
+
display: flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
gap: 0.5rem;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.hud button {
|
|
15
|
+
padding: 0.25rem 0.75rem;
|
|
16
|
+
cursor: pointer;
|
|
17
|
+
}
|
|
18
|
+
`;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
import { customElement } from "lit/decorators.js";
|
|
4
|
+
import { useObservableValues } from "@adobe/data-lit";
|
|
5
|
+
import { BoardState } from "../../types/board-state/board-state.js";
|
|
6
|
+
import { TictactoeElement } from "../../tictactoe-element.js";
|
|
7
|
+
import { styles } from "./tictactoe-hud.css.js";
|
|
8
|
+
import * as presentation from "./tictactoe-hud-presentation.js";
|
|
9
|
+
|
|
10
|
+
export const tagName = "tictactoe-hud";
|
|
11
|
+
|
|
12
|
+
@customElement(tagName)
|
|
13
|
+
export class TictactoeHud extends TictactoeElement {
|
|
14
|
+
static styles = styles;
|
|
15
|
+
|
|
16
|
+
render() {
|
|
17
|
+
const values = useObservableValues(
|
|
18
|
+
() => ({
|
|
19
|
+
board: this.service.observe.resources.board,
|
|
20
|
+
firstPlayer: this.service.observe.resources.firstPlayer,
|
|
21
|
+
}),
|
|
22
|
+
[],
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const board = values?.board ?? " ";
|
|
26
|
+
const firstPlayer = values?.firstPlayer ?? "X";
|
|
27
|
+
|
|
28
|
+
const currentPlayer = BoardState.currentPlayer(board, firstPlayer);
|
|
29
|
+
const status = BoardState.deriveStatus(board);
|
|
30
|
+
const winner = BoardState.getWinner(board);
|
|
31
|
+
|
|
32
|
+
const statusText =
|
|
33
|
+
status === "won" && winner !== null
|
|
34
|
+
? `Winner: ${winner}`
|
|
35
|
+
: status === "draw"
|
|
36
|
+
? "Draw"
|
|
37
|
+
: `Current Player: ${currentPlayer}`;
|
|
38
|
+
|
|
39
|
+
return presentation.render({
|
|
40
|
+
statusText,
|
|
41
|
+
restartGame: () => this.service.transactions.restartGame(),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
import { Database } from "@adobe/data/ecs";
|
|
4
|
+
import { Observe } from "@adobe/data/observe";
|
|
5
|
+
import { AgenticService } from "@adobe/data/service";
|
|
6
|
+
import { BoardState } from "../types/board-state/board-state";
|
|
7
|
+
import type { PlayerMark } from "../types/player-mark/player-mark";
|
|
8
|
+
import { tictactoePlugin } from "./tictactoe-plugin";
|
|
9
|
+
import type { TictactoeDatabase } from "./tictactoe-plugin";
|
|
10
|
+
|
|
11
|
+
const roleDescription = (mark: PlayerMark): string =>
|
|
12
|
+
`You are playing as ${mark} in tic-tac-toe. Play to the best of your ability.`;
|
|
13
|
+
|
|
14
|
+
const createTictactoeAgentService = (
|
|
15
|
+
db: TictactoeDatabase,
|
|
16
|
+
agentMark: PlayerMark,
|
|
17
|
+
): AgenticService => {
|
|
18
|
+
const board = db.observe.resources.board;
|
|
19
|
+
const isGameOver = Observe.withFilter(board, BoardState.isGameOver);
|
|
20
|
+
const currentPlayer = Observe.withFilter(
|
|
21
|
+
board,
|
|
22
|
+
(nextBoard) =>
|
|
23
|
+
BoardState.currentPlayer(nextBoard, db.resources.firstPlayer),
|
|
24
|
+
);
|
|
25
|
+
const yourTurn = Observe.withMap(
|
|
26
|
+
Observe.fromProperties({ isGameOver, currentPlayer }),
|
|
27
|
+
({ isGameOver: over, currentPlayer: cur }) => !over && cur === agentMark,
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
return AgenticService.create({
|
|
31
|
+
interface: {
|
|
32
|
+
role: {
|
|
33
|
+
type: "state",
|
|
34
|
+
schema: { type: "string" as const },
|
|
35
|
+
description: "Your role and objective",
|
|
36
|
+
},
|
|
37
|
+
board: {
|
|
38
|
+
type: "state",
|
|
39
|
+
schema: BoardState.schema,
|
|
40
|
+
description: "Board state",
|
|
41
|
+
},
|
|
42
|
+
yourTurn: {
|
|
43
|
+
type: "state",
|
|
44
|
+
schema: { type: "boolean" as const },
|
|
45
|
+
description: "True when it is your turn to play",
|
|
46
|
+
},
|
|
47
|
+
resetGame: {
|
|
48
|
+
type: "action",
|
|
49
|
+
description: "Reset the game after completion",
|
|
50
|
+
parameters: [],
|
|
51
|
+
},
|
|
52
|
+
winner: {
|
|
53
|
+
type: "state",
|
|
54
|
+
schema: {
|
|
55
|
+
type: "string" as const,
|
|
56
|
+
enum: ["X", "O", "cat", null],
|
|
57
|
+
},
|
|
58
|
+
description: "Winner mark when game over",
|
|
59
|
+
},
|
|
60
|
+
playMove: {
|
|
61
|
+
type: "action",
|
|
62
|
+
description: `Play a ${agentMark} move on the board`,
|
|
63
|
+
parameters: [
|
|
64
|
+
{
|
|
65
|
+
title: "index",
|
|
66
|
+
type: "integer" as const,
|
|
67
|
+
minimum: 0,
|
|
68
|
+
maximum: 8,
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
implementation: {
|
|
74
|
+
role: Observe.fromConstant(roleDescription(agentMark)),
|
|
75
|
+
board,
|
|
76
|
+
yourTurn,
|
|
77
|
+
winner: Observe.withFilter(board, BoardState.getWinner),
|
|
78
|
+
resetGame: async () => {
|
|
79
|
+
db.transactions.restartGame();
|
|
80
|
+
},
|
|
81
|
+
playMove: async (index: number) => {
|
|
82
|
+
db.transactions.playMove({ index });
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
conditional: {
|
|
86
|
+
resetGame: isGameOver,
|
|
87
|
+
playMove: yourTurn,
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const tictactoeLinkDescriptions = { x: "Play as X", o: "Play as O" } as const;
|
|
93
|
+
|
|
94
|
+
const createTictactoeRootAgentService = (
|
|
95
|
+
db: TictactoeDatabase,
|
|
96
|
+
): AgenticService => {
|
|
97
|
+
const root = AgenticService.create({
|
|
98
|
+
interface: {
|
|
99
|
+
x: { type: "link", description: tictactoeLinkDescriptions.x },
|
|
100
|
+
o: { type: "link", description: tictactoeLinkDescriptions.o },
|
|
101
|
+
},
|
|
102
|
+
implementation: {
|
|
103
|
+
x: createTictactoeAgentService(db, "X"),
|
|
104
|
+
o: createTictactoeAgentService(db, "O"),
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
return { ...root, linkDescriptions: tictactoeLinkDescriptions } as AgenticService;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export const agentPlugin = Database.Plugin.create({
|
|
111
|
+
extends: tictactoePlugin,
|
|
112
|
+
services: {
|
|
113
|
+
agent: (db): AgenticService => createTictactoeRootAgentService(db),
|
|
114
|
+
agentX: (db): AgenticService => createTictactoeAgentService(db, "X"),
|
|
115
|
+
agentO: (db): AgenticService => createTictactoeAgentService(db, "O"),
|
|
116
|
+
},
|
|
117
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
import { Database } from "@adobe/data/ecs";
|
|
4
|
+
import { Observe } from "@adobe/data/observe";
|
|
5
|
+
import { BoardState } from "../types/board-state/board-state";
|
|
6
|
+
import type { PlayerMark } from "../types/player-mark/player-mark";
|
|
7
|
+
import { PlayMoveArgs } from "../types/play-move-args/play-move-args";
|
|
8
|
+
|
|
9
|
+
export const tictactoePlugin = Database.Plugin.create({
|
|
10
|
+
resources: {
|
|
11
|
+
board: { default: BoardState.createInitialBoard() },
|
|
12
|
+
firstPlayer: { default: "X" as PlayerMark },
|
|
13
|
+
},
|
|
14
|
+
computed: {
|
|
15
|
+
currentPlayer: (db) =>
|
|
16
|
+
Observe.withFilter(db.observe.resources.board, (board) =>
|
|
17
|
+
BoardState.currentPlayer(board, db.resources.firstPlayer),
|
|
18
|
+
),
|
|
19
|
+
moveCount: (db) =>
|
|
20
|
+
Observe.withFilter(db.observe.resources.board, BoardState.getMoveCount),
|
|
21
|
+
status: (db) =>
|
|
22
|
+
Observe.withFilter(db.observe.resources.board, BoardState.deriveStatus),
|
|
23
|
+
winningLine: (db) =>
|
|
24
|
+
Observe.withFilter(db.observe.resources.board, BoardState.getWinningLine),
|
|
25
|
+
winner: (db) =>
|
|
26
|
+
Observe.withFilter(db.observe.resources.board, BoardState.getWinner),
|
|
27
|
+
isGameOver: (db) =>
|
|
28
|
+
Observe.withFilter(db.observe.resources.board, BoardState.isGameOver),
|
|
29
|
+
},
|
|
30
|
+
transactions: {
|
|
31
|
+
restartGame: (t) => {
|
|
32
|
+
t.resources.firstPlayer =
|
|
33
|
+
t.resources.firstPlayer === "X" ? "O" : "X";
|
|
34
|
+
t.resources.board = BoardState.createInitialBoard();
|
|
35
|
+
},
|
|
36
|
+
playMove: (t, { index }: PlayMoveArgs) => {
|
|
37
|
+
const validation = PlayMoveArgs.canPlayMove({
|
|
38
|
+
board: t.resources.board,
|
|
39
|
+
index,
|
|
40
|
+
});
|
|
41
|
+
if (!validation.ok) return;
|
|
42
|
+
const mark = BoardState.currentPlayer(
|
|
43
|
+
t.resources.board,
|
|
44
|
+
t.resources.firstPlayer,
|
|
45
|
+
);
|
|
46
|
+
const nextBoard = BoardState.setBoardCell({
|
|
47
|
+
board: t.resources.board,
|
|
48
|
+
index,
|
|
49
|
+
mark,
|
|
50
|
+
});
|
|
51
|
+
t.resources.board = nextBoard;
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
export type TictactoeDatabase = Database.FromPlugin<typeof tictactoePlugin>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
import { DatabaseElement } from "@adobe/data-lit";
|
|
4
|
+
import { agentPlugin } from "./state/agent-plugin.js";
|
|
5
|
+
|
|
6
|
+
export class TictactoeElement extends DatabaseElement<typeof agentPlugin> {
|
|
7
|
+
get plugin() {
|
|
8
|
+
return agentPlugin;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// © 2026 Adobe. MIT License. See /LICENSE for details.
|
|
2
|
+
|
|
3
|
+
import type { BoardState } from "./board-state";
|
|
4
|
+
import type { PlayerMark } from "../player-mark/player-mark";
|
|
5
|
+
|
|
6
|
+
export const currentPlayer = (
|
|
7
|
+
board: BoardState,
|
|
8
|
+
firstPlayer: PlayerMark,
|
|
9
|
+
): PlayerMark => {
|
|
10
|
+
const xCount = (board.match(/X/g) ?? []).length;
|
|
11
|
+
const oCount = (board.match(/O/g) ?? []).length;
|
|
12
|
+
return xCount === oCount ? firstPlayer : firstPlayer === "X" ? "O" : "X";
|
|
13
|
+
};
|