Skip to content

Commit e9a0d48

Browse files
committed
Add activity log
1 parent 7382c18 commit e9a0d48

File tree

9 files changed

+224
-1
lines changed

9 files changed

+224
-1
lines changed

typescript/packages/lookslike-high-level/src/data.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ import { shaderManager } from './spells/20_shader_editor.jsx'
6363
import { schemaGenerator } from './spells/21_model_builder.jsx'
6464
import { search } from './spells/22_search.jsx'
6565
import { notebook } from './spells/23_notes.jsx'
66+
import { quotedb } from './spells/24_quotes.jsx'
67+
import { activity } from './spells/25_activity.jsx'
68+
import { activityRef } from "./sugar/activity.js";
6669

6770
export type Charm = {
6871
[NAME]?: string;
@@ -264,6 +267,8 @@ addCharms([
264267
schemaGenerator.spawn({ schemaGenerator: 1 }),
265268
search.spawn({ search: 1 }),
266269
notebook.spawn({ notes: 1 }),
270+
quotedb.spawn({ quotes: 1 }),
271+
activity.spawn(activityRef),
267272
FetchService.spawn() as any,
268273
GmailService.spawn() as any,
269274
ViewService.spawn() as any,

typescript/packages/lookslike-high-level/src/spells/02_counter.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { event, events, set, subview } from "../sugar.js";
33
import { description, Description } from "./stickers/describe.jsx";
44
import { mixin } from "../sugar/mixin.js";
55
import { Chattable, chatUiResolver } from "./stickers/chat.jsx";
6+
import { log } from "../sugar/activity.js";
67

78
const resolveEmpty = select({ self: $.self }).not(q => q.match($.self, "clicks", $._));
89

@@ -65,7 +66,7 @@ export const rules = behavior({
6566

6667
onClick: event(CounterEvent.onClick)
6768
.with(resolveClicks)
68-
.update(({ self, clicks }) => set(self, { clicks: clicks + 1 }))
69+
.update(({ self, clicks }) => ([...set(self, { clicks: clicks + 1 }), ...log(self, 'Incremented counter')]))
6970
.commit(),
7071
});
7172

typescript/packages/lookslike-high-level/src/spells/16_contacts.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { z } from "zod";
99
import { Reference } from "merkle-reference";
1010
import { importEntity, resolve, tagWithSchema } from "../sugar/sugar.jsx";
1111
import { Ref, UiFragment } from "../sugar/zod.js";
12+
import { log } from "../sugar/activity.js";
1213

1314
const Contact = z.object({
1415
name: z.string().min(1).max(255).describe("The name of the contact"),
@@ -86,6 +87,7 @@ export const addressBook = typedBehavior(
8687
const { self: id, instructions } = importEntity(contact, Contact)
8788
cmd.add(...instructions);
8889
cmd.add(...Transact.assert(self, { contacts: id }));
90+
cmd.add(...log(self, 'Added new contact'))
8991
}),
9092

9193
onEditContact: event("~/on/edit-contact")

typescript/packages/lookslike-high-level/src/spells/20_shader_editor.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { Reference } from "merkle-reference";
1212
import { importEntity, resolve, tagWithSchema } from "../sugar/sugar.jsx";
1313
import { Ref, UiFragment } from "../sugar/zod.js";
1414
import { llm, RESPONSE } from "../effects/fetch.jsx";
15+
import { log } from "../sugar/activity.js";
1516

1617
const adjectives = ['indigo', 'azure', 'crimson', 'emerald', 'golden', 'silver', 'obsidian', 'sapphire'];
1718
const nouns = ['crossfire', 'thunder', 'storm', 'blade', 'phoenix', 'dragon', 'whisper', 'shadow'];
@@ -108,6 +109,7 @@ const shaderEditor = typedBehavior(Shader, {
108109
const shader = ev.detail.value;
109110
cmd.add(...Transact.set(self, shader))
110111
cmd.add(...tagWithSchema(self, Shader))
112+
cmd.add(...log(self, 'Edited shader'))
111113
}),
112114

113115
onModifyWithAI: event("~/on/modify-with-ai")
@@ -252,6 +254,7 @@ export const shaderManager = typedBehavior(
252254
const currentIndex = shaders.findIndex(s => (s as any).self.toString() === editingShader?.toString());
253255
if (currentIndex > 0) {
254256
cmd.add(...Transact.set(self, { editingShader: (shaders[currentIndex - 1] as any).self }));
257+
cmd.add(...log(self, 'Show prev shader'));
255258
}
256259
}),
257260

@@ -262,6 +265,7 @@ export const shaderManager = typedBehavior(
262265
const currentIndex = shaders.findIndex(s => (s as any).self.toString() === editingShader?.toString());
263266
if (currentIndex < shaders.length - 1) {
264267
cmd.add(...Transact.set(self, { editingShader: (shaders[currentIndex + 1] as any).self }));
268+
cmd.add(...log(self, 'Show next shader'))
265269
}
266270
}),
267271

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import {
2+
h,
3+
Session,
4+
refer,
5+
} from "@commontools/common-system";
6+
import { event, subview, Transact } from "../sugar.js";
7+
import { Charm, initRules, typedBehavior } from "./spell.jsx";
8+
import { z } from "zod";
9+
import { Reference } from "merkle-reference";
10+
import { importEntity, resolve, tagWithSchema } from "../sugar/sugar.jsx";
11+
import { Ref, UiFragment } from "../sugar/zod.js";
12+
import { log } from "../sugar/activity.js";
13+
14+
const Quote = z.object({
15+
quote: z.string().max(1024 * 16).describe("The quote text"),
16+
source: z.string().max(255).describe("The source of the quote"),
17+
}).describe('Quote');
18+
19+
const QuoteDB = z.object({
20+
focused: Ref.describe("The quote that is currently being edited"),
21+
quotes: z.array(Quote).describe("The quotes that have been added"),
22+
'~/common/ui/list': UiFragment.describe("The UI fragment for the quotes list, if present")
23+
})
24+
25+
type EditEvent = {
26+
detail: { item: Reference }
27+
};
28+
29+
type SubmitEvent = {
30+
detail: { value: z.infer<typeof Quote> }
31+
};
32+
33+
const quoteEditor = typedBehavior(Quote, {
34+
render: ({ self, quote, source }) => (
35+
<div entity={self}>
36+
<common-form
37+
schema={Quote}
38+
value={{ quote, source }}
39+
onsubmit="~/on/save"
40+
/>
41+
<details>
42+
<pre>{JSON.stringify({ quote, source }, null, 2)}</pre>
43+
</details>
44+
</div>
45+
),
46+
rules: _ => ({
47+
onSave: event("~/on/save")
48+
.transact(({ self, event }, cmd) => {
49+
const ev = Session.resolve<SubmitEvent>(event);
50+
let quote = ev.detail.value;
51+
if (!quote.source) {
52+
quote.source = 'Unknown';
53+
}
54+
55+
cmd.add(...Transact.set(self, quote))
56+
}),
57+
})
58+
})
59+
60+
export const quotedb = typedBehavior(
61+
QuoteDB.pick({
62+
'~/common/ui/list': true
63+
}), {
64+
render: ({ self, '~/common/ui/list': quoteList }) => (
65+
<div entity={self} name="Quotes">
66+
<div>
67+
<common-form
68+
schema={Quote}
69+
reset
70+
onsubmit="~/on/add-quote"
71+
/>
72+
</div>
73+
74+
<br />
75+
<br />
76+
{subview(quoteList)}
77+
</div>
78+
),
79+
rules: schema => ({
80+
init: initRules.init,
81+
82+
onAddQuote: event("~/on/add-quote")
83+
.transact(({ self, event }, cmd) => {
84+
const ev = Session.resolve<SubmitEvent>(event);
85+
let quote = ev.detail.value;
86+
if (!quote.source) {
87+
quote.source = 'Unknown';
88+
}
89+
90+
const { self: id, instructions } = importEntity(quote, Quote)
91+
cmd.add(...instructions);
92+
cmd.add(...Transact.assert(self, { quotes: id }));
93+
cmd.add(...log(self, 'Added quote'))
94+
}),
95+
96+
onEditQuote: event("~/on/edit-quote")
97+
.transact(({ self, event }, cmd) => {
98+
const ev = Session.resolve<EditEvent>(event);
99+
cmd.add(...Transact.set(self, { focused: ev.detail.item }))
100+
cmd.add(...tagWithSchema(self, Quote))
101+
}),
102+
103+
onDeleteQuote: event("~/on/delete-quote")
104+
.transact(({ self, event }, cmd) => {
105+
debugger
106+
const ev = Session.resolve<EditEvent>(event);
107+
cmd.add(...Transact.remove(self, { quotes: ev.detail.item }))
108+
}),
109+
110+
onCloseEditor: event("~/on/close-editor")
111+
.with(resolve(QuoteDB.pick({ focused: true })))
112+
.transact(({ self, focused }, cmd) => {
113+
cmd.add(...Transact.remove(self, { focused }))
114+
}),
115+
116+
renderQuoteList: resolve(QuoteDB.pick({ quotes: true }))
117+
.update(({ self, quotes }) => {
118+
return [
119+
{
120+
Upsert: [self, '~/common/ui/list', <common-table
121+
schema={Quote}
122+
data={quotes}
123+
onedit="~/on/edit-quote"
124+
ondelete="~/on/delete-quote"
125+
/> as any]
126+
}
127+
]
128+
}).commit(),
129+
130+
}),
131+
});
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import {
2+
h,
3+
Session,
4+
refer,
5+
} from "@commontools/common-system";
6+
import { event, subview, Transact } from "../sugar.js";
7+
import { Charm, initRules, typedBehavior } from "./spell.jsx";
8+
import { z } from "zod";
9+
import { Reference } from "merkle-reference";
10+
import { importEntity, resolve, tagWithSchema } from "../sugar/sugar.jsx";
11+
import { Ref, UiFragment } from "../sugar/zod.js";
12+
import { log, LogEntry } from "../sugar/activity.js";
13+
14+
const ActivityLog = z.object({
15+
log: z.array(LogEntry).describe('The log of activities')
16+
})
17+
18+
export const activity = typedBehavior(
19+
ActivityLog.pick({
20+
log: true
21+
}), {
22+
render: ({ self, log }) => {
23+
debugger
24+
return (
25+
<table entity={self} title="Activity Log" style="width: 100%; border-spacing: 0">
26+
<tbody>
27+
{[...log].filter(entry => !isNaN(new Date(entry.modified).getTime())).sort((a, b) => Number(new Date(b.modified)) - Number(new Date(a.modified))).map((entry, i) => (
28+
<tr style={`margin-bottom: 8px; background: ${i % 2 === 0 ? '#fff' : '#f5f5f5'}; border-bottom: 1px solid #eee`}>
29+
<td style="font-size: 10px; white-space: nowrap; padding: 8px 16px 8px 0">
30+
{new Date(entry.modified).toLocaleString()}
31+
</td>
32+
<td style="padding: 8px 0">{entry.message}</td>
33+
<td style="text-align: right; padding: 8px 0">
34+
<code style="background: #eee; padding: 2px 6px; border-radius: 4px; font-size: 10px; font-family: monospace">
35+
{entry.target?.toString()}
36+
</code>
37+
</td>
38+
</tr>
39+
))}
40+
</tbody>
41+
</table>
42+
)
43+
},
44+
rules: schema => ({
45+
init: initRules.init,
46+
}),
47+
});

typescript/packages/lookslike-high-level/src/spells/stickers/describe.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { behavior, $, select } from "@commontools/common-system";
22
import { llm, RESPONSE } from "../../effects/fetch.jsx";
33
import { field } from "../../sugar.js";
4+
import { log } from "../../sugar/activity.js";
45

56
export const description = field("llmDescription", "");
67

@@ -18,6 +19,7 @@ export const Description = (
1819
llm(values.self, "my/describe", {
1920
prompt: promptFn(Object.fromEntries(fields.map(f => [f, values[f]]))),
2021
}).json(),
22+
...log(values.self, 'Generating description of: ' + promptFn(Object.fromEntries(fields.map(f => [f, values[f]]))))
2123
])
2224
.commit(),
2325

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { refer, Reference } from "merkle-reference";
2+
import z from 'zod'
3+
import { Ref } from "./zod.js";
4+
import { importEntity } from "./sugar.jsx";
5+
import { Instruction } from "@commontools/common-system";
6+
7+
export const activityRef = refer({ activity: 1 })
8+
9+
export const LogEntry = z.object({
10+
modified: z.string(),
11+
message: z.string(),
12+
target: Ref
13+
}).describe('LogEntry')
14+
15+
export function log(entity: Reference, message: string): Instruction[] {
16+
console.log('ACTIVITY', entity, message)
17+
const entry = {
18+
modified: (new Date()).toString(),
19+
message,
20+
target: entity
21+
}
22+
23+
const { self, instructions } = importEntity(entry, LogEntry)
24+
25+
return [
26+
...instructions,
27+
{ Assert: [activityRef, 'log', self] },
28+
{ Assert: [entity, 'common/activity', self] }
29+
]
30+
}

typescript/packages/lookslike-high-level/src/sugar/sugar.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { field } from "./query.js";
1616
import { fromString, Reference } from "merkle-reference";
1717
import { defaultTo } from "./default.js";
1818
import { Transact } from "../sugar.js";
19+
import { log } from "./activity.js";
1920

2021
export function list<T extends z.ZodObject<any>>(
2122
schema: T

0 commit comments

Comments
 (0)