Make the app an installable PWA with phone layout and Web Push

Add a generated manifest, icon set, offline page and service worker that never caches authenticated HTML, plus install and update UI. Turn the rail into a drawer below md with container-query internals, dvh and safe-area handling, and reconnect the socket after visibility/network changes. Add Web Push subscriptions, presence-aware delivery, notification settings and tests.
This commit is contained in:
2026-10-19 17:12:16 +01:00
parent 61098a114c
commit dbfe48796b
100 changed files with 3573 additions and 117 deletions
+88
View File
@@ -2077,6 +2077,94 @@ const schema: BUN_SQLITE_DatabaseSchemaType = {
},
],
},
{
tableName: "user_presence",
fields: [
{
fieldName: "user_id",
fieldDescription: `The user. One row each`,
dataType: "INTEGER",
unique: true,
foreignKey: {
cascadeDelete: true,
destinationTableName: "users",
destinationTableColumnName: "id",
destinationTableColumnType: "INTEGER",
foreignKeyName: "user_presence_user_fk",
},
},
{
fieldName: "last_seen_at",
fieldDescription: `Unix timestamp of the last open socket or keepalive, for skipping push notifications while the app is open`,
dataType: "INTEGER",
},
],
indexes: [
{
indexName: "idx_user_presence_last_seen_at",
indexTableFields: ["last_seen_at"],
},
],
},
{
tableName: "push_subscriptions",
fields: [
{
fieldName: "user_id",
fieldDescription: `The user the device belongs to`,
dataType: "INTEGER",
foreignKey: {
cascadeDelete: true,
destinationTableName: "users",
destinationTableColumnName: "id",
destinationTableColumnType: "INTEGER",
foreignKeyName: "push_subscriptions_user_fk",
},
},
{
fieldName: "endpoint",
fieldDescription: `The push service URL the browser gave. Unique: one device, one row`,
dataType: "TEXT",
unique: true,
},
{
fieldName: "p256dh",
fieldDescription: `The subscription's public key`,
dataType: "TEXT",
},
{
fieldName: "auth",
fieldDescription: `The subscription's auth secret`,
dataType: "TEXT",
},
{
fieldName: "user_agent",
fieldDescription: `What the device said it was, for the settings list`,
dataType: "TEXT",
},
{
fieldName: "platform",
fieldDescription: `The browser's platform, for the settings list`,
dataType: "TEXT",
},
{
fieldName: "last_used_at",
fieldDescription: `Unix timestamp of the last notification sent to it`,
dataType: "INTEGER",
},
{
fieldName: "last_error",
fieldDescription: `Why the last send failed, if it did`,
dataType: "TEXT",
},
],
indexes: [
{
indexName: "idx_push_subscriptions_user_id",
indexTableFields: ["user_id"],
},
],
},
{
tableName: "envs",
fields: [
+73 -1
View File
@@ -36,6 +36,8 @@ export const BunSQLiteTables = [
"bot_custom_tools",
"workspace_custom_tools",
"logs",
"user_presence",
"push_subscriptions",
"envs",
] as const
@@ -1262,6 +1264,76 @@ export type BUN_SQLITE_COBBLE_LOGS = {
ip_address?: string;
}
export type BUN_SQLITE_COBBLE_USER_PRESENCE = {
/**
* The unique identifier of the record.
*/
id?: number | "";
/**
* The time when the record was created. (Unix Timestamp)
*/
created_at?: number | "";
/**
* The time when the record was updated. (Unix Timestamp)
*/
updated_at?: number | "";
/**
* The user. One row each
*/
user_id?: number | "";
/**
* Unix timestamp of the last open socket or keepalive, for skipping push notifications while the app is open
*/
last_seen_at?: number | "";
}
export type BUN_SQLITE_COBBLE_PUSH_SUBSCRIPTIONS = {
/**
* The unique identifier of the record.
*/
id?: number | "";
/**
* The time when the record was created. (Unix Timestamp)
*/
created_at?: number | "";
/**
* The time when the record was updated. (Unix Timestamp)
*/
updated_at?: number | "";
/**
* The user the device belongs to
*/
user_id?: number | "";
/**
* The push service URL the browser gave. Unique: one device, one row
*/
endpoint?: string;
/**
* The subscription's public key
*/
p256dh?: string;
/**
* The subscription's auth secret
*/
auth?: string;
/**
* What the device said it was, for the settings list
*/
user_agent?: string;
/**
* The browser's platform, for the settings list
*/
platform?: string;
/**
* Unix timestamp of the last notification sent to it
*/
last_used_at?: number | "";
/**
* Why the last send failed, if it did
*/
last_error?: string;
}
export type BUN_SQLITE_COBBLE_ENVS = {
/**
* The unique identifier of the record.
@@ -1309,4 +1381,4 @@ export type BUN_SQLITE_COBBLE_ENVS = {
expose_to_computers?: 0 | 1 | "";
}
export type BUN_SQLITE_COBBLE_ALL_TYPEDEFS = BUN_SQLITE_COBBLE_USERS & BUN_SQLITE_COBBLE_USER_TYPES & BUN_SQLITE_COBBLE_SSO_LOGIN_CODES & BUN_SQLITE_COBBLE_USER_PASSWORDS & BUN_SQLITE_COBBLE_MEDIA & BUN_SQLITE_COBBLE_MEDIA_PARADIGMS & BUN_SQLITE_COBBLE_BOTS & BUN_SQLITE_COBBLE_BOT_SESSIONS & BUN_SQLITE_COBBLE_BOT_MESSAGES & BUN_SQLITE_COBBLE_BOT_TASKS & BUN_SQLITE_COBBLE_BOT_TASK_STEPS & BUN_SQLITE_COBBLE_BOTS_QUEUE & BUN_SQLITE_COBBLE_BOTS_QUEUE_UPDATES & BUN_SQLITE_COBBLE_CRON_JOBS & BUN_SQLITE_COBBLE_WORKSPACES & BUN_SQLITE_COBBLE_WORKSPACE_BOTS & BUN_SQLITE_COBBLE_WORKSPACE_USERS & BUN_SQLITE_COBBLE_BOT_USERS & BUN_SQLITE_COBBLE_BOT_USER_MEMORIES & BUN_SQLITE_COBBLE_BOT_USER_FACES & BUN_SQLITE_COBBLE_BOT_MESSAGE_MEDIA & BUN_SQLITE_COBBLE_CONTAINERS & BUN_SQLITE_COBBLE_COMPUTERS & BUN_SQLITE_COBBLE_BOT_COMPUTERS & BUN_SQLITE_COBBLE_WORKSPACE_COMPUTERS & BUN_SQLITE_COBBLE_HARNESS_PROVIDERS & BUN_SQLITE_COBBLE_HARNESS_MODELS & BUN_SQLITE_COBBLE_HARNESS_SETTINGS & BUN_SQLITE_COBBLE_BOT_HARNESS_SETTINGS & BUN_SQLITE_COBBLE_WORKSPACE_HARNESS_SETTINGS & BUN_SQLITE_COBBLE_MCP_SERVERS & BUN_SQLITE_COBBLE_CUSTOM_TOOLS & BUN_SQLITE_COBBLE_BOT_MCP_SERVERS & BUN_SQLITE_COBBLE_WORKSPACE_MCP_SERVERS & BUN_SQLITE_COBBLE_BOT_CUSTOM_TOOLS & BUN_SQLITE_COBBLE_WORKSPACE_CUSTOM_TOOLS & BUN_SQLITE_COBBLE_LOGS & BUN_SQLITE_COBBLE_ENVS
export type BUN_SQLITE_COBBLE_ALL_TYPEDEFS = BUN_SQLITE_COBBLE_USERS & BUN_SQLITE_COBBLE_USER_TYPES & BUN_SQLITE_COBBLE_SSO_LOGIN_CODES & BUN_SQLITE_COBBLE_USER_PASSWORDS & BUN_SQLITE_COBBLE_MEDIA & BUN_SQLITE_COBBLE_MEDIA_PARADIGMS & BUN_SQLITE_COBBLE_BOTS & BUN_SQLITE_COBBLE_BOT_SESSIONS & BUN_SQLITE_COBBLE_BOT_MESSAGES & BUN_SQLITE_COBBLE_BOT_TASKS & BUN_SQLITE_COBBLE_BOT_TASK_STEPS & BUN_SQLITE_COBBLE_BOTS_QUEUE & BUN_SQLITE_COBBLE_BOTS_QUEUE_UPDATES & BUN_SQLITE_COBBLE_CRON_JOBS & BUN_SQLITE_COBBLE_WORKSPACES & BUN_SQLITE_COBBLE_WORKSPACE_BOTS & BUN_SQLITE_COBBLE_WORKSPACE_USERS & BUN_SQLITE_COBBLE_BOT_USERS & BUN_SQLITE_COBBLE_BOT_USER_MEMORIES & BUN_SQLITE_COBBLE_BOT_USER_FACES & BUN_SQLITE_COBBLE_BOT_MESSAGE_MEDIA & BUN_SQLITE_COBBLE_CONTAINERS & BUN_SQLITE_COBBLE_COMPUTERS & BUN_SQLITE_COBBLE_BOT_COMPUTERS & BUN_SQLITE_COBBLE_WORKSPACE_COMPUTERS & BUN_SQLITE_COBBLE_HARNESS_PROVIDERS & BUN_SQLITE_COBBLE_HARNESS_MODELS & BUN_SQLITE_COBBLE_HARNESS_SETTINGS & BUN_SQLITE_COBBLE_BOT_HARNESS_SETTINGS & BUN_SQLITE_COBBLE_WORKSPACE_HARNESS_SETTINGS & BUN_SQLITE_COBBLE_MCP_SERVERS & BUN_SQLITE_COBBLE_CUSTOM_TOOLS & BUN_SQLITE_COBBLE_BOT_MCP_SERVERS & BUN_SQLITE_COBBLE_WORKSPACE_MCP_SERVERS & BUN_SQLITE_COBBLE_BOT_CUSTOM_TOOLS & BUN_SQLITE_COBBLE_WORKSPACE_CUSTOM_TOOLS & BUN_SQLITE_COBBLE_LOGS & BUN_SQLITE_COBBLE_USER_PRESENCE & BUN_SQLITE_COBBLE_PUSH_SUBSCRIPTIONS & BUN_SQLITE_COBBLE_ENVS