bleam 0.0.12 → 0.0.14

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 (57) hide show
  1. package/dist/ai.cjs +5 -4
  2. package/dist/ai.js +4 -3
  3. package/dist/app-storage-D8W4n8ey.cjs +39 -0
  4. package/dist/app-storage-Isi5Bo0R.js +34 -0
  5. package/dist/cli.cjs +149 -23
  6. package/dist/cli.d.cts +13 -0
  7. package/dist/cli.d.ts +13 -0
  8. package/dist/cli.js +149 -23
  9. package/dist/elements-DX_YUveu.d.ts +341 -0
  10. package/dist/elements-gEI8YGl1.d.cts +341 -0
  11. package/dist/elements.cjs +1031 -0
  12. package/dist/elements.d.cts +2 -0
  13. package/dist/elements.d.ts +2 -0
  14. package/dist/elements.js +999 -0
  15. package/dist/{files-BXVkPrPN.js → files-DErLhzCB.js} +5 -11
  16. package/dist/{files-DxaQ-Nv0.cjs → files-lMk-CpL_.cjs} +5 -11
  17. package/dist/files.cjs +1 -1
  18. package/dist/files.js +1 -1
  19. package/dist/schema.cjs +1 -1
  20. package/dist/schema.d.cts +1 -1
  21. package/dist/schema.d.ts +1 -1
  22. package/dist/schema.js +1 -1
  23. package/dist/secrets.cjs +146 -0
  24. package/dist/secrets.d.cts +14 -0
  25. package/dist/secrets.d.ts +14 -0
  26. package/dist/secrets.js +142 -0
  27. package/dist/{state-LssDgpff.cjs → state-Bm9GiRnU.cjs} +61 -47
  28. package/dist/{state-Dh3HLixb.js → state-D8Firo9w.js} +60 -41
  29. package/dist/state.cjs +3 -2
  30. package/dist/state.d.cts +1 -1
  31. package/dist/state.d.ts +1 -1
  32. package/dist/state.js +3 -2
  33. package/dist/window.d.cts +1 -1
  34. package/dist/window.d.ts +1 -1
  35. package/package.json +14 -11
  36. package/templates/basic/app/index.tsx +7 -129
  37. package/templates/foundation-models/app/index.tsx +100 -41
  38. package/templates/image-generation/app/index.tsx +2 -2
  39. package/templates/native/ios/Podfile.lock +6 -0
  40. package/templates/native/modules/bleam-runtime/ios/AIModule.swift +7 -8
  41. package/templates/native/package.json +6 -1
  42. package/templates/native/yarn.lock +5392 -3749
  43. package/templates/state/app/index.tsx +2 -2
  44. package/templates/text-generation/app/index.tsx +4 -4
  45. package/templates/updates/README.md +1 -1
  46. package/dist/native-sqlite-xcGdamRD.js +0 -64
  47. package/dist/native-sqlite-yQLD5s9i.cjs +0 -66
  48. package/dist/ui-1WepaMS4.d.cts +0 -92
  49. package/dist/ui-D7bRLYee.d.ts +0 -92
  50. package/dist/ui.cjs +0 -364
  51. package/dist/ui.d.cts +0 -2
  52. package/dist/ui.d.ts +0 -2
  53. package/dist/ui.js +0 -357
  54. /package/dist/{schema-B5BfdswF.js → schema-B7ELMpuI.js} +0 -0
  55. /package/dist/{schema-BnVZOXfu.cjs → schema-B7SLUBLN.cjs} +0 -0
  56. /package/dist/{schema-D5eImHxu.d.cts → schema-BWsDPc6c.d.cts} +0 -0
  57. /package/dist/{schema-SSjokbtw.d.ts → schema-DsXZBnvc.d.ts} +0 -0
@@ -1,41 +1,11 @@
1
- import { t as ensureNativeScript } from "./native-runtime-C85Nuc4F.js";
2
1
  import { t as crypto } from "./crypto-BB92-Upx.js";
3
- import { l as schema } from "./schema-B5BfdswF.js";
2
+ import { t as currentAppStorageKey } from "./app-storage-Isi5Bo0R.js";
3
+ import { l as schema } from "./schema-B7ELMpuI.js";
4
4
  import { useAtom, useAtomValue, useSetAtom } from "jotai/react";
5
5
  import { atom, getDefaultStore } from "jotai/vanilla";
6
6
  import { atomFamily } from "jotai-family";
7
+ import { openDatabaseAsync } from "expo-sqlite";
7
8
 
8
- //#region src/app-storage.ts
9
- let cachedStorageKey;
10
- function launchArgument(name) {
11
- const argumentsList = NSProcessInfo.processInfo.arguments();
12
- const count = Number(argumentsList.count ?? 0);
13
- for (let index = 0; index < count - 1; index += 1) if (String(argumentsList.objectAtIndex(index)) === name) return String(argumentsList.objectAtIndex(index + 1));
14
- }
15
- function storageKey$1(name) {
16
- return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "app";
17
- }
18
- function currentAppStorageKey() {
19
- if (cachedStorageKey) return cachedStorageKey;
20
- ensureNativeScript();
21
- const configPath = launchArgument("--bleam-project-config");
22
- if (configPath) try {
23
- const text = String(NSString.stringWithContentsOfFileEncodingError(configPath, NSUTF8StringEncoding, null));
24
- const config = JSON.parse(text);
25
- if (typeof config.id === "string" && config.id) {
26
- cachedStorageKey = config.id;
27
- return cachedStorageKey;
28
- }
29
- if (typeof config.name === "string" && config.name) {
30
- cachedStorageKey = storageKey$1(config.name);
31
- return cachedStorageKey;
32
- }
33
- } catch {}
34
- cachedStorageKey = "app";
35
- return cachedStorageKey;
36
- }
37
-
38
- //#endregion
39
9
  //#region src/state/atom.ts
40
10
  const storedAtomsKey = Symbol.for("bleam.state.storedAtoms.v1");
41
11
  function storedAtoms() {
@@ -155,6 +125,55 @@ function internalJotaiWritableAtom(value) {
155
125
  }
156
126
  const internalStore = getDefaultStore();
157
127
 
128
+ //#endregion
129
+ //#region src/native-sqlite.ts
130
+ const databases = /* @__PURE__ */ new Map();
131
+ function toExpoParameters(parameters) {
132
+ return parameters;
133
+ }
134
+ function toSQLiteValue(value) {
135
+ if (typeof value === "string" || typeof value === "number" || value === null) return value;
136
+ throw new Error("SQLite blobs are not supported by Bleam state yet");
137
+ }
138
+ function toSQLiteRow(row) {
139
+ const output = {};
140
+ for (const [key, value] of Object.entries(row)) output[key] = toSQLiteValue(value);
141
+ return output;
142
+ }
143
+ var NativeSQLiteDatabase = class NativeSQLiteDatabase {
144
+ constructor(database) {
145
+ this.database = database;
146
+ }
147
+ async execute(query, parameters) {
148
+ await this.database.runAsync(query, toExpoParameters(parameters) ?? []);
149
+ }
150
+ async select(query, parameters) {
151
+ return (await this.database.getAllAsync(query, toExpoParameters(parameters) ?? [])).map(toSQLiteRow);
152
+ }
153
+ async get(query, parameters) {
154
+ const row = await this.database.getFirstAsync(query, toExpoParameters(parameters) ?? []);
155
+ return row ? toSQLiteRow(row) : void 0;
156
+ }
157
+ async transaction(action) {
158
+ let result;
159
+ await this.database.withExclusiveTransactionAsync(async (transaction) => {
160
+ result = await action(new NativeSQLiteDatabase(transaction));
161
+ });
162
+ return result;
163
+ }
164
+ close() {
165
+ return this.database.closeAsync();
166
+ }
167
+ };
168
+ function openNativeSQLiteDatabase(name = "bleam.sqlite3") {
169
+ let database = databases.get(name);
170
+ if (!database) {
171
+ database = openDatabaseAsync(name).then((value) => new NativeSQLiteDatabase(value));
172
+ databases.set(name, database);
173
+ }
174
+ return database;
175
+ }
176
+
158
177
  //#endregion
159
178
  //#region src/state/collection.ts
160
179
  var CollectionVersionConflictError = class extends Error {
@@ -182,18 +201,18 @@ let jsonSqlSupportPromise;
182
201
  let mutationPipeline = Promise.resolve();
183
202
  let transactionCallbackStarting = false;
184
203
  function getDatabase() {
185
- databasePromise ??= import("./native-sqlite-xcGdamRD.js").then((module) => module.openNativeSQLiteDatabase(`${currentAppStorageKey()}.sqlite3`));
204
+ databasePromise ??= Promise.resolve().then(() => openNativeSQLiteDatabase(`${currentAppStorageKey()}.sqlite3`));
186
205
  return databasePromise;
187
206
  }
188
207
  async function initializedDatabase() {
189
208
  initializedPromise ??= (async () => {
190
209
  const database = await getDatabase();
191
210
  await database.execute("PRAGMA journal_mode = WAL");
192
- if (numberValue(await database.get("PRAGMA user_version"), "user_version", 0) !== schemaVersion) await database.transaction(async () => {
193
- await database.execute("DROP TABLE IF EXISTS links");
194
- await database.execute("DROP TABLE IF EXISTS collections");
195
- await createTables(database);
196
- await database.execute(`PRAGMA user_version = ${schemaVersion}`);
211
+ if (numberValue(await database.get("PRAGMA user_version"), "user_version", 0) !== schemaVersion) await database.transaction(async (transaction) => {
212
+ await transaction.execute("DROP TABLE IF EXISTS links");
213
+ await transaction.execute("DROP TABLE IF EXISTS collections");
214
+ await createTables(transaction);
215
+ await transaction.execute(`PRAGMA user_version = ${schemaVersion}`);
197
216
  });
198
217
  else await createTables(database);
199
218
  return database;
@@ -675,7 +694,7 @@ function enqueueMutation(action) {
675
694
  const database = await initializedDatabase();
676
695
  const changes = /* @__PURE__ */ new Map();
677
696
  try {
678
- const value = await database.transaction(() => action(database, changes));
697
+ const value = await database.transaction((transaction) => action(transaction, changes));
679
698
  publishChanges(changes);
680
699
  resolve(value);
681
700
  } catch (error) {
@@ -871,4 +890,4 @@ function useField(collection, id, path) {
871
890
  }
872
891
 
873
892
  //#endregion
874
- export { transactionAtom as a, useRow as c, internalJotaiWritableAtom as d, internalStore as f, currentAppStorageKey as g, useSetAtom$1 as h, queryAtom as i, atom$1 as l, useAtomValue$1 as m, atomCollection as n, useField as o, useAtom$1 as p, mutateAtom as r, useList as s, CollectionVersionConflictError as t, internalJotaiAtom as u };
893
+ export { transactionAtom as a, useRow as c, internalJotaiWritableAtom as d, internalStore as f, useSetAtom$1 as h, queryAtom as i, atom$1 as l, useAtomValue$1 as m, atomCollection as n, useField as o, useAtom$1 as p, mutateAtom as r, useList as s, CollectionVersionConflictError as t, internalJotaiAtom as u };
package/dist/state.cjs CHANGED
@@ -1,7 +1,8 @@
1
1
  require('./native-runtime-CsXnXkQn.cjs');
2
2
  require('./crypto-CT3VZ7lF.cjs');
3
- const require_schema = require('./schema-BnVZOXfu.cjs');
4
- const require_state = require('./state-LssDgpff.cjs');
3
+ require('./app-storage-D8W4n8ey.cjs');
4
+ const require_schema = require('./schema-B7SLUBLN.cjs');
5
+ const require_state = require('./state-Bm9GiRnU.cjs');
5
6
 
6
7
  exports.CollectionVersionConflictError = require_state.CollectionVersionConflictError;
7
8
  exports.array = require_schema.array;
package/dist/state.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as array, C as SchemaOutput, E as SchemaShape, F as number, I as object, P as literal, R as string, _ as ObjectField, b as SchemaInput, c as FieldPath, j as boolean, k as ValueAtFieldPath, s as FieldOutput, w as SchemaPatch } from "./schema-D5eImHxu.cjs";
1
+ import { A as array, C as SchemaOutput, E as SchemaShape, F as number, I as object, P as literal, R as string, _ as ObjectField, b as SchemaInput, c as FieldPath, j as boolean, k as ValueAtFieldPath, s as FieldOutput, w as SchemaPatch } from "./schema-BWsDPc6c.cjs";
2
2
 
3
3
  //#region src/state/atom.d.ts
4
4
  declare const atomValue: unique symbol;
package/dist/state.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as array, C as SchemaOutput, E as SchemaShape, F as number, I as object, P as literal, R as string, _ as ObjectField, b as SchemaInput, c as FieldPath, j as boolean, k as ValueAtFieldPath, s as FieldOutput, w as SchemaPatch } from "./schema-SSjokbtw.js";
1
+ import { A as array, C as SchemaOutput, E as SchemaShape, F as number, I as object, P as literal, R as string, _ as ObjectField, b as SchemaInput, c as FieldPath, j as boolean, k as ValueAtFieldPath, s as FieldOutput, w as SchemaPatch } from "./schema-DsXZBnvc.js";
2
2
  import "jotai/vanilla";
3
3
 
4
4
  //#region src/state/atom.d.ts
package/dist/state.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import "./native-runtime-C85Nuc4F.js";
2
2
  import "./crypto-BB92-Upx.js";
3
- import { c as object, n as array, o as literal, r as boolean, s as number, u as string } from "./schema-B5BfdswF.js";
4
- import { a as transactionAtom, c as useRow, h as useSetAtom, i as queryAtom, l as atom, m as useAtomValue, n as atomCollection, o as useField, p as useAtom, r as mutateAtom, s as useList, t as CollectionVersionConflictError } from "./state-Dh3HLixb.js";
3
+ import "./app-storage-Isi5Bo0R.js";
4
+ import { c as object, n as array, o as literal, r as boolean, s as number, u as string } from "./schema-B7ELMpuI.js";
5
+ import { a as transactionAtom, c as useRow, h as useSetAtom, i as queryAtom, l as atom, m as useAtomValue, n as atomCollection, o as useField, p as useAtom, r as mutateAtom, s as useList, t as CollectionVersionConflictError } from "./state-D8Firo9w.js";
5
6
 
6
7
  export { CollectionVersionConflictError, array, atom, atomCollection, boolean, literal, mutateAtom, number, object, queryAtom, string, transactionAtom, useAtom, useAtomValue, useField, useList, useRow, useSetAtom };
package/dist/window.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { p as SymbolName } from "./ui-1WepaMS4.cjs";
1
+ import { n as SymbolName } from "./elements-gEI8YGl1.cjs";
2
2
  import * as react3 from "react";
3
3
  import { ComponentType } from "react";
4
4
  import { StyleProp, ViewStyle } from "react-native";
package/dist/window.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { p as SymbolName } from "./ui-D7bRLYee.js";
1
+ import { n as SymbolName } from "./elements-DX_YUveu.js";
2
2
  import * as react3 from "react";
3
3
  import { ComponentType } from "react";
4
4
  import { StyleProp, ViewStyle } from "react-native";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bleam",
3
- "version": "0.0.12",
4
- "packageManager": "yarn@1.22.22",
3
+ "version": "0.0.14",
4
+ "packageManager": "yarn@4.17.1",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "dist",
@@ -10,9 +10,7 @@
10
10
  "tsconfig.json",
11
11
  "tsconfig.base.json"
12
12
  ],
13
- "bin": {
14
- "bleam": "dist/cli.js"
15
- },
13
+ "bin": "dist/cli.js",
16
14
  "scripts": {
17
15
  "build": "tsdown",
18
16
  "prepublishOnly": "tsdown",
@@ -30,11 +28,11 @@
30
28
  "dependencies": {
31
29
  "@biomejs/biome": "^2.2.0",
32
30
  "@expo/metro": "56.0.0",
33
- "@nativescript-community/sqlite": "^3.5.9",
34
31
  "@nativescript/react-native": "9.0.0-preview.4",
35
32
  "@types/react": "~19.2.17",
36
33
  "expo": "57",
37
34
  "expo-splash-screen": "57.0.2",
35
+ "expo-sqlite": "~57.0.1",
38
36
  "jotai": "2.20.2",
39
37
  "jotai-family": "1.0.2",
40
38
  "react": "19.2.3",
@@ -97,6 +95,11 @@
97
95
  "import": "./dist/dev-protocol.js",
98
96
  "require": "./dist/dev-protocol.cjs"
99
97
  },
98
+ "./elements": {
99
+ "types": "./dist/elements.d.ts",
100
+ "import": "./dist/elements.js",
101
+ "require": "./dist/elements.cjs"
102
+ },
100
103
  "./files": {
101
104
  "types": "./dist/files.d.ts",
102
105
  "import": "./dist/files.js",
@@ -117,6 +120,11 @@
117
120
  "import": "./dist/schema.js",
118
121
  "require": "./dist/schema.cjs"
119
122
  },
123
+ "./secrets": {
124
+ "types": "./dist/secrets.d.ts",
125
+ "import": "./dist/secrets.js",
126
+ "require": "./dist/secrets.cjs"
127
+ },
120
128
  "./state": {
121
129
  "types": "./dist/state.d.ts",
122
130
  "import": "./dist/state.js",
@@ -127,11 +135,6 @@
127
135
  "import": "./dist/styles.js",
128
136
  "require": "./dist/styles.cjs"
129
137
  },
130
- "./ui": {
131
- "types": "./dist/ui.d.ts",
132
- "import": "./dist/ui.js",
133
- "require": "./dist/ui.cjs"
134
- },
135
138
  "./window": {
136
139
  "types": "./dist/window.d.ts",
137
140
  "import": "./dist/window.js",
@@ -1,139 +1,17 @@
1
- import { atom, useAtom } from 'bleam/state'
2
1
  import { sx } from 'bleam/styles'
3
- import { Button } from 'bleam/ui'
4
- import { Toolbar, Window } from 'bleam/window'
2
+ import { Window } from 'bleam/window'
5
3
  import { Text, View } from 'react-native'
6
4
 
7
5
  const appName = 'Bleam Basic'
8
- const counter = atom(0)
9
6
 
10
7
  export default function App() {
11
- const [count, setCount] = useAtom(counter)
12
-
13
8
  return (
14
- <View style={sx('flex-1')}>
15
- <Window title={appName} titleVisibility="visible" />
16
- <Toolbar toolbarStyle="unified" />
17
-
18
- <View style={sx('flex-1', 'items-center', 'justify-center', 'p-6')}>
19
- <View style={sx('flex-1', 'w-full', 'max-w-5xl', 'max-h-3xl')}>
20
- <View style={sx('flex-1', 'items-center', 'justify-center', 'px-6')}>
21
- <View style={sx('items-center', 'gap-8')}>
22
- <View
23
- style={sx('h-44', 'w-44', 'items-center', 'justify-center')}
24
- >
25
- <View
26
- style={sx(
27
- 'absolute',
28
- 'top-0',
29
- 'h-24',
30
- 'w-32',
31
- 'rounded-2xl',
32
- 'border',
33
- 'border-tertiary-label',
34
- 'bg-system',
35
- 'items-center',
36
- 'justify-center',
37
- 'rotate-30',
38
- )}
39
- >
40
- <Text
41
- style={sx(
42
- 'text-3xl',
43
- 'font-semibold',
44
- 'text-system-cyan',
45
- '-rotate-30',
46
- )}
47
- >
48
- B
49
- </Text>
50
- </View>
51
-
52
- <View
53
- style={sx(
54
- 'absolute',
55
- 'bottom-2',
56
- 'h-24',
57
- 'w-32',
58
- 'rounded-2xl',
59
- 'border-2',
60
- 'border-system-purple',
61
- 'bg-secondary-system',
62
- 'items-center',
63
- 'justify-center',
64
- 'rotate-30',
65
- )}
66
- >
67
- <Text
68
- style={sx(
69
- 'text-3xl',
70
- 'font-bold',
71
- 'text-system-purple',
72
- '-rotate-30',
73
- )}
74
- >
75
- JS
76
- </Text>
77
- </View>
78
- </View>
79
-
80
- <View style={sx('items-center')}>
81
- <Text
82
- style={sx(
83
- 'text-5xl',
84
- 'font-semibold',
85
- 'tracking-tighter',
86
- 'text-label',
87
- 'text-center',
88
- )}
89
- >
90
- Get started
91
- </Text>
92
- <Text
93
- style={sx(
94
- 'mt-6',
95
- 'text-lg',
96
- 'text-secondary-label',
97
- 'text-center',
98
- )}
99
- >
100
- Edit{' '}
101
- <Text
102
- style={sx(
103
- 'rounded-md',
104
- 'italic',
105
- 'px-2',
106
- 'py-1',
107
- 'text-label',
108
- )}
109
- >
110
- app/index.tsx
111
- </Text>{' '}
112
- and save to test{' '}
113
- <Text
114
- style={sx(
115
- 'rounded-md',
116
- 'italic',
117
- 'px-2',
118
- 'py-1',
119
- 'text-label',
120
- )}
121
- >
122
- live reload
123
- </Text>
124
- </Text>
125
- </View>
126
-
127
- <Button
128
- title={`Count is ${count}`}
129
- variant="secondary"
130
- size="regular"
131
- onPress={() => setCount((value) => value + 1)}
132
- />
133
- </View>
134
- </View>
135
- </View>
136
- </View>
9
+ <View style={sx('flex-1', 'items-center', 'justify-center', 'px-6')}>
10
+ <Window title={appName} />
11
+ <Text style={sx('text-3xl', 'font-bold', 'text-label')}>{appName}</Text>
12
+ <Text style={sx('mt-2', 'text-base', 'text-secondary-label')}>
13
+ Open app/index.tsx to start working on your app.
14
+ </Text>
137
15
  </View>
138
16
  )
139
17
  }
@@ -1,13 +1,47 @@
1
1
  import { chat, useChat, useChatList } from 'bleam/ai'
2
+ import {
3
+ Button,
4
+ Conversation,
5
+ ConversationContent,
6
+ ConversationEmptyState,
7
+ ConversationScrollButton,
8
+ Message,
9
+ MessageActions,
10
+ MessageContent,
11
+ MessageStatus,
12
+ MessageText,
13
+ PromptInput,
14
+ PromptInputFooter,
15
+ PromptInputSubmit,
16
+ PromptInputTextarea,
17
+ } from 'bleam/elements'
2
18
  import { sx } from 'bleam/styles'
3
- import { Button } from 'bleam/ui'
4
19
  import { Window } from 'bleam/window'
5
- import { useEffect, useRef, useState } from 'react'
6
- import { Text, TextInput, View } from 'react-native'
20
+ import { Suspense, useEffect, useRef, useState } from 'react'
21
+ import { Text, View } from 'react-native'
7
22
 
8
23
  const appName = 'Bleam Basic'
9
24
 
10
25
  export default function App() {
26
+ return (
27
+ <View style={sx('flex-1', 'p-8', 'gap-6')}>
28
+ <Window title={appName} />
29
+ <Text style={sx('text-3xl', 'font-semibold')}>Foundation Models</Text>
30
+ <Text style={sx('text-secondary-label')}>
31
+ Chats persist on this Mac and use Apple Foundation Models by default.
32
+ </Text>
33
+ <Suspense
34
+ fallback={
35
+ <Text style={sx('text-secondary-label')}>Loading local chats...</Text>
36
+ }
37
+ >
38
+ <ChatSession />
39
+ </Suspense>
40
+ </View>
41
+ )
42
+ }
43
+
44
+ function ChatSession() {
11
45
  const chats = useChatList()
12
46
  const creating = useRef(false)
13
47
  const [error, setError] = useState('')
@@ -25,15 +59,10 @@ export default function App() {
25
59
  }, [chats.length])
26
60
 
27
61
  return (
28
- <View style={sx('flex-1', 'p-8', 'gap-6')}>
29
- <Window title={appName} />
30
- <Text style={sx('text-3xl', 'font-semibold')}>Foundation Models</Text>
31
- <Text style={sx('text-secondary-label')}>
32
- Chats persist on this Mac and use Apple Foundation Models by default.
33
- </Text>
62
+ <View style={sx('flex-1')}>
34
63
  {error ? <Text style={sx('text-system-red')}>{error}</Text> : null}
35
64
  {chats[0] ? (
36
- <Conversation id={chats[0].id} />
65
+ <ChatConversation id={chats[0].id} />
37
66
  ) : (
38
67
  <Text>Creating chat...</Text>
39
68
  )}
@@ -41,7 +70,7 @@ export default function App() {
41
70
  )
42
71
  }
43
72
 
44
- function Conversation({ id }: { id: string }) {
73
+ function ChatConversation({ id }: { id: string }) {
45
74
  const conversation = useChat(id, {
46
75
  system: 'You suggest practical, concise native Mac app ideas.',
47
76
  })
@@ -56,39 +85,69 @@ function Conversation({ id }: { id: string }) {
56
85
  : null
57
86
 
58
87
  return (
59
- <View style={sx('gap-4')}>
60
- <TextInput
88
+ <View style={sx('flex-1', 'gap-4')}>
89
+ <Conversation>
90
+ <ConversationContent
91
+ data={conversation.messages}
92
+ keyExtractor={(message) => message.id}
93
+ ListEmptyComponent={
94
+ <ConversationEmptyState
95
+ title="Start a conversation"
96
+ description="Ask for a practical native Mac app idea."
97
+ />
98
+ }
99
+ renderItem={({ item: message }) => (
100
+ <Message from={message.role}>
101
+ {message.content ? (
102
+ <MessageContent>
103
+ <MessageText>{message.content}</MessageText>
104
+ </MessageContent>
105
+ ) : null}
106
+ {message.status !== 'completed' ? (
107
+ <MessageStatus status={message.status} />
108
+ ) : null}
109
+ {retryable?.id === message.id ? (
110
+ <MessageActions>
111
+ <Button
112
+ label="Retry"
113
+ size="small"
114
+ variant="secondary"
115
+ onPress={() => void conversation.retry(message.id)}
116
+ />
117
+ </MessageActions>
118
+ ) : null}
119
+ </Message>
120
+ )}
121
+ />
122
+ <ConversationScrollButton />
123
+ </Conversation>
124
+
125
+ <PromptInput
126
+ disabled={!conversation.canSend && !conversation.canCancel}
127
+ status={conversation.status}
61
128
  value={prompt}
62
129
  onChangeText={setPrompt}
63
- style={sx('rounded-xl', 'border', 'border-separator', 'p-4')}
64
- />
65
- <View style={sx('flex-row', 'gap-3')}>
66
- <Button
67
- title="Send"
68
- loading={conversation.isGenerating}
69
- disabled={!conversation.canSend}
70
- onPress={() => void conversation.send(prompt)}
71
- />
72
- <Button
73
- title="Cancel"
74
- variant="secondary"
75
- disabled={!conversation.canCancel}
76
- onPress={() => void conversation.cancel()}
77
- />
78
- {retryable ? (
79
- <Button
80
- title="Retry"
81
- variant="secondary"
82
- onPress={() => void conversation.retry(retryable.id)}
130
+ onSubmit={({ text }) => {
131
+ setPrompt('')
132
+ void conversation.send(text)
133
+ }}
134
+ >
135
+ <PromptInputTextarea />
136
+ <PromptInputFooter>
137
+ <View />
138
+ <PromptInputSubmit
139
+ disabled={!conversation.canCancel && !prompt.trim()}
140
+ onPress={({ text }) => {
141
+ if (conversation.canCancel) {
142
+ void conversation.cancel()
143
+ } else {
144
+ setPrompt('')
145
+ void conversation.send(text)
146
+ }
147
+ }}
83
148
  />
84
- ) : null}
85
- </View>
86
- {conversation.messages.map((message) => (
87
- <Text key={message.id} style={sx('text-lg', 'text-label')}>
88
- {message.role === 'user' ? 'You: ' : 'Assistant: '}
89
- {message.content || message.status}
90
- </Text>
91
- ))}
149
+ </PromptInputFooter>
150
+ </PromptInput>
92
151
  </View>
93
152
  )
94
153
  }
@@ -1,6 +1,6 @@
1
1
  import { bonsai, image } from 'bleam/ai'
2
+ import { Button } from 'bleam/elements'
2
3
  import { sx } from 'bleam/styles'
3
- import { Button } from 'bleam/ui'
4
4
  import { Window } from 'bleam/window'
5
5
  import { useState } from 'react'
6
6
  import { Image, Text, View } from 'react-native'
@@ -35,7 +35,7 @@ export default function App() {
35
35
  <Image source={{ uri }} style={sx('w-96', 'h-96', 'rounded-xl')} />
36
36
  ) : null}
37
37
  <Text style={sx('text-secondary-label')}>{status}</Text>
38
- <Button title="Generate image" onPress={create} />
38
+ <Button label="Generate image" onPress={create} />
39
39
  </View>
40
40
  )
41
41
  }
@@ -71,6 +71,8 @@ PODS:
71
71
  - ExpoModulesWorklets (57.0.2):
72
72
  - ExpoModulesCore
73
73
  - ExpoModulesJSI
74
+ - ExpoSQLite (57.0.1):
75
+ - ExpoModulesCore
74
76
  - FBLazyVector (0.86.0)
75
77
  - hermes-engine (250829098.0.14):
76
78
  - hermes-engine/Pre-built (= 250829098.0.14)
@@ -1932,6 +1934,7 @@ DEPENDENCIES:
1932
1934
  - ExpoModulesCore (from `../node_modules/expo-modules-core`)
1933
1935
  - ExpoModulesJSI (from `../node_modules/expo-modules-jsi/apple`)
1934
1936
  - ExpoModulesWorklets (from `../node_modules/expo-modules-core`)
1937
+ - ExpoSQLite (from `../node_modules/expo-sqlite/ios`)
1935
1938
  - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
1936
1939
  - hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`)
1937
1940
  - "NativeScriptNativeApi (from `../node_modules/@nativescript/react-native`)"
@@ -2035,6 +2038,8 @@ EXTERNAL SOURCES:
2035
2038
  :path: "../node_modules/expo-modules-jsi/apple"
2036
2039
  ExpoModulesWorklets:
2037
2040
  :path: "../node_modules/expo-modules-core"
2041
+ ExpoSQLite:
2042
+ :path: "../node_modules/expo-sqlite/ios"
2038
2043
  FBLazyVector:
2039
2044
  :path: "../node_modules/react-native/Libraries/FBLazyVector"
2040
2045
  hermes-engine:
@@ -2202,6 +2207,7 @@ SPEC CHECKSUMS:
2202
2207
  ExpoModulesCore: 747a01dfe7af61ad2e869f80bf983af1caa63378
2203
2208
  ExpoModulesJSI: ef7f197a8e7eeb245557f560ade794bfc1abdc79
2204
2209
  ExpoModulesWorklets: 0340ed9a30017f8597b90a1ef9aa210c43e44c4b
2210
+ ExpoSQLite: ddd65ed74bdebcff20f6766e682f9321ecb28ab8
2205
2211
  FBLazyVector: b3e7ad108f0d882e30445c5527d774e3fd432f3d
2206
2212
  hermes-engine: 6f298675a7f5d45e1a61a3c664a2b9618befb1e1
2207
2213
  NativeScriptNativeApi: b35cf5bb2727adf20eb13d2b1bd8651d0698a362
@@ -9,9 +9,6 @@ import FoundationModels
9
9
 
10
10
  public final class AIModule: Module {
11
11
  @MainActor private let textGeneration = TextGenerationCoordinator()
12
- private static var appGroupIdentifier: String {
13
- Bundle.main.object(forInfoDictionaryKey: "AppGroupIdentifier") as? String ?? "group.dev.bleam.app"
14
- }
15
12
 
16
13
  public func definition() -> ModuleDefinition {
17
14
  Name("AI")
@@ -382,13 +379,15 @@ public final class AIModule: Module {
382
379
 
383
380
  private static func workerRequest(from request: FluxImageRequest) throws -> GenerationWorkerRequest {
384
381
  let jobID = UUID()
385
- guard let appGroupContainer = FileManager.default.containerURL(
386
- forSecurityApplicationGroupIdentifier: appGroupIdentifier
387
- ) else {
388
- throw ModuleError.imageGeneration("Shared generation container is unavailable")
382
+ guard let cacheDirectory = FileManager.default.urls(
383
+ for: .cachesDirectory,
384
+ in: .userDomainMask
385
+ ).first else {
386
+ throw ModuleError.imageGeneration("Generation cache is unavailable")
389
387
  }
390
388
 
391
- let stagingDirectory = appGroupContainer
389
+ let stagingDirectory = cacheDirectory
390
+ .appendingPathComponent("Bleam", isDirectory: true)
392
391
  .appendingPathComponent("GenerationRuns", isDirectory: true)
393
392
  .appendingPathComponent("bleam-generation-jobs", isDirectory: true)
394
393
  .appendingPathComponent(jobID.uuidString, isDirectory: true)