diff --git a/app/src/ui/layouts/AppShell/Breadcrumbs2.tsx b/app/src/ui/layouts/AppShell/Breadcrumbs2.tsx
index b495b435..2b7c5621 100644
--- a/app/src/ui/layouts/AppShell/Breadcrumbs2.tsx
+++ b/app/src/ui/layouts/AppShell/Breadcrumbs2.tsx
@@ -4,6 +4,7 @@ import { Link, useLocation } from "wouter";
import { IconButton } from "../../components/buttons/IconButton";
import { Dropdown } from "../../components/overlay/Dropdown";
import { useEvent } from "../../hooks/use-event";
+import { useNavigate } from "ui/lib/routes";
type Breadcrumb = {
label: string | Element;
@@ -17,26 +18,11 @@ export type Breadcrumbs2Props = {
};
export const Breadcrumbs2 = ({ path: _path, backTo, onBack }: Breadcrumbs2Props) => {
- const [_, navigate] = useLocation();
- const location = window.location.pathname;
+ const [, , _goBack] = useNavigate();
const path = Array.isArray(_path) ? _path : [_path];
- const loc = location.split("/").filter((v) => v !== "");
const hasBack = path.length > 1;
- const goBack = onBack
- ? onBack
- : useEvent(() => {
- if (backTo) {
- navigate(backTo, { replace: true });
- return;
- } else if (_path.length > 0 && _path[0]?.href) {
- navigate(_path[0].href, { replace: true });
- return;
- }
-
- const href = loc.slice(0, path.length + 1).join("/");
- navigate(`~/${href}`, { replace: true });
- });
+ const goBack = onBack ? onBack : () => _goBack({ fallback: backTo });
const crumbs = useMemo(
() =>
diff --git a/app/src/ui/layouts/AppShell/Header.tsx b/app/src/ui/layouts/AppShell/Header.tsx
index 0b313c5a..629d5374 100644
--- a/app/src/ui/layouts/AppShell/Header.tsx
+++ b/app/src/ui/layouts/AppShell/Header.tsx
@@ -185,9 +185,7 @@ function UserMenu() {
}
}
- if (!options.theme) {
- items.push(() =>
);
- }
+ items.push(() =>
);
items.push(() => (
{getVersion()}
diff --git a/app/src/ui/lib/routes.ts b/app/src/ui/lib/routes.ts
index b94e550f..42b896ba 100644
--- a/app/src/ui/lib/routes.ts
+++ b/app/src/ui/lib/routes.ts
@@ -102,13 +102,29 @@ export function useNavigate() {
}
const _url = options?.absolute ? `~/${basepath}${url}`.replace(/\/+/g, "/") : url;
+ const state = {
+ ...options?.state,
+ referrer: location,
+ };
+
navigate(options?.query ? withQuery(_url, options?.query) : _url, {
replace: options?.replace,
- state: options?.state,
+ state,
});
});
},
location,
+ (opts?: { fallback?: string }) => {
+ const state = window.history.state;
+ if (state?.referrer) {
+ //window.history.replaceState(state, "", state.referrer);
+ navigate(state.referrer, { replace: true });
+ } else if (opts?.fallback) {
+ navigate(opts.fallback, { replace: true });
+ } else {
+ window.history.back();
+ }
+ },
] as const;
}
diff --git a/app/src/ui/routes/data/data.$entity.$id.tsx b/app/src/ui/routes/data/data.$entity.$id.tsx
index 978573ab..e531452d 100644
--- a/app/src/ui/routes/data/data.$entity.$id.tsx
+++ b/app/src/ui/routes/data/data.$entity.$id.tsx
@@ -31,7 +31,7 @@ function DataEntityUpdateImpl({ params }) {
const entityId = params.id as PrimaryFieldType;
const [error, setError] = useState
(null);
- const [navigate] = useNavigate();
+ const [navigate, _, _goBack] = useNavigate();
useBrowserTitle(["Data", entity.label, `#${entityId}`]);
const targetRelations = relations.listableRelationsOf(entity);
@@ -52,9 +52,8 @@ function DataEntityUpdateImpl({ params }) {
},
);
- function goBack() {
- window.history.go(-1);
- }
+ const backHref = routes.data.entity.list(entity.name);
+ const goBack = () => _goBack({ fallback: backHref });
async function onSubmitted(changeSet?: EntityData) {
//return;
@@ -162,10 +161,8 @@ function DataEntityUpdateImpl({ params }) {
className="pl-3"
>
{$q.isLoading ? (
diff --git a/app/src/ui/routes/data/data.$entity.create.tsx b/app/src/ui/routes/data/data.$entity.create.tsx
index bb83eaf6..07b2bb58 100644
--- a/app/src/ui/routes/data/data.$entity.create.tsx
+++ b/app/src/ui/routes/data/data.$entity.create.tsx
@@ -8,13 +8,14 @@ import { useBrowserTitle } from "ui/hooks/use-browser-title";
import { useSearch } from "ui/hooks/use-search";
import * as AppShell from "ui/layouts/AppShell/AppShell";
import { Breadcrumbs2 } from "ui/layouts/AppShell/Breadcrumbs2";
-import { routes } from "ui/lib/routes";
+import { routes, useNavigate } from "ui/lib/routes";
import { EntityForm } from "ui/modules/data/components/EntityForm";
import { useEntityForm } from "ui/modules/data/hooks/useEntityForm";
import { s } from "core/object/schema";
export function DataEntityCreate({ params }) {
const { $data } = useBkndData();
+ const [navigate, _, _goBack] = useNavigate();
const entity = $data.entity(params.entity as string);
if (!entity) {
return ;
@@ -30,9 +31,8 @@ export function DataEntityCreate({ params }) {
// @todo: use entity schema for prefilling
const search = useSearch(s.object({}), {});
- function goBack() {
- window.history.go(-1);
- }
+ const backHref = routes.data.entity.list(entity.name);
+ const goBack = () => _goBack({ fallback: backHref });
async function onSubmitted(changeSet?: EntityData) {
console.log("create:changeSet", changeSet);
@@ -80,12 +80,7 @@ export function DataEntityCreate({ params }) {
>
}
>
-
+
{error && (
diff --git a/bun.lock b/bun.lock
index 6e309474..8d730fcd 100644
--- a/bun.lock
+++ b/bun.lock
@@ -15,7 +15,7 @@
},
"app": {
"name": "bknd",
- "version": "0.15.0-rc.8",
+ "version": "0.15.0",
"bin": "./dist/cli/index.js",
"dependencies": {
"@cfworker/json-schema": "^4.1.1",
@@ -125,6 +125,7 @@
"version": "0.5.1",
"devDependencies": {
"@types/bun": "latest",
+ "bknd": "workspace:*",
"tsdx": "^0.14.1",
"typescript": "^5.0.0",
},
@@ -1220,7 +1221,7 @@
"@types/babel__traverse": ["@types/babel__traverse@7.20.6", "", { "dependencies": { "@babel/types": "^7.20.7" } }, "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg=="],
- "@types/bun": ["@types/bun@1.2.17", "", { "dependencies": { "bun-types": "1.2.17" } }, "sha512-l/BYs/JYt+cXA/0+wUhulYJB6a6p//GTPiJ7nV+QHa8iiId4HZmnu/3J/SowP5g0rTiERY2kfGKXEK5Ehltx4Q=="],
+ "@types/bun": ["@types/bun@1.2.18", "", { "dependencies": { "bun-types": "1.2.18" } }, "sha512-Xf6RaWVheyemaThV0kUfaAUvCNokFr+bH8Jxp+tTZfx7dAPA8z9ePnP9S9+Vspzuxxx9JRAXhnyccRj3GyCMdQ=="],
"@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="],
@@ -4026,7 +4027,7 @@
"@testing-library/jest-dom/chalk": ["chalk@3.0.0", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg=="],
- "@types/bun/bun-types": ["bun-types@1.2.17", "", { "dependencies": { "@types/node": "*" } }, "sha512-ElC7ItwT3SCQwYZDYoAH+q6KT4Fxjl8DtZ6qDulUFBmXA8YB4xo+l54J9ZJN+k2pphfn9vk7kfubeSd5QfTVJQ=="],
+ "@types/bun/bun-types": ["bun-types@1.2.18", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-04+Eha5NP7Z0A9YgDAzMk5PHR16ZuLVa83b26kH5+cp1qZW4F6FmAURngE7INf4tKOvCE69vYvDEwoNl1tGiWw=="],
"@types/pg/pg-types": ["pg-types@4.0.2", "", { "dependencies": { "pg-int8": "1.0.1", "pg-numeric": "1.0.2", "postgres-array": "~3.0.1", "postgres-bytea": "~3.0.0", "postgres-date": "~2.1.0", "postgres-interval": "^3.0.0", "postgres-range": "^1.1.1" } }, "sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng=="],
diff --git a/docs/integration/docker.mdx b/docs/integration/docker.mdx
index 387c2516..9b5bc5e2 100644
--- a/docs/integration/docker.mdx
+++ b/docs/integration/docker.mdx
@@ -36,4 +36,44 @@ docker run -p 1337:1337 -e ARGS="--db-url file:/data/data.db" bknd
To mount the data directory to the host, you can use the `-v` flag:
```bash
docker run -p 1337:1337 -v /path/to/data:/data bknd
-```
\ No newline at end of file
+```
+
+## Docker compose example
+
+If you want to use docker compose and build the image directly from the git repository.
+
+```yaml compose.yml
+services:
+ bknd:
+ pull_policy: build
+ build: https://github.com/bknd-io/bknd.git#main:docker
+ ports:
+ - 1337:1337
+ environment:
+ ARGS: "--db-url file:/data/data.db"
+ volumes:
+ - ${DATA_DIR:-.}/data:/data
+```
+
+
+The docker compose file can be extended to build a specific version of bknd.
+Extend the `build` section with `args` and `labels`.
+Inside `args`, you can pass a `VERSION` build argument, and use `labels` so the built image receives a unique identifier.
+
+```yaml compose.yml
+services:
+ bknd:
+ pull_policy: build
+ build:
+ context: https://github.com/bknd-io/bknd.git#main:docker
+ args:
+ VERSION:
+ labels:
+ - x-bknd-version=
+ ports:
+ - 1337:1337
+ environment:
+ ARGS: "--db-url file:/data/data.db"
+ volumes:
+ - ${DATA_DIR:-.}/data:/data
+```
diff --git a/docs/usage/database.mdx b/docs/usage/database.mdx
index dc520d21..8a43de33 100644
--- a/docs/usage/database.mdx
+++ b/docs/usage/database.mdx
@@ -107,8 +107,8 @@ import { libsql } from "bknd/data";
export default {
connection: libsql({
- url: "libsql://your-database-url.turso.io",
- authToken: "your-auth-token",
+ url: "libsql://.turso.io",
+ authToken: "",
}),
} as const satisfies BkndConfig;
```
@@ -120,11 +120,13 @@ import type { BkndConfig } from "bknd";
import { libsql } from "bknd/data";
import { createClient } from "@libsql/client";
+const client = createClient({
+ url: "libsql://.turso.io",
+ authToken: "",
+})
+
export default {
- connection: libsql(createClient({
- url: "libsql://your-database-url.turso.io",
- authToken: "your-auth-token",
- })),
+ connection: libsql(client),
} as const satisfies BkndConfig;
```