diff --git a/AGENTS.md b/AGENTS.md
index f1d36a6c5..f5bc8ce2e 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -16,7 +16,7 @@
- Runtime: Node 24 through `mise exec node@24 --`; pnpm 11.2.1 through Corepack.
- Install with `pnpm install`, create the documented `.env` symlinks, then run `pnpm db:migrate`.
- Start local development with `pnpm dev:start`. Use `pnpm dev:start -d` for detached mode and `pnpm dev:stop` to stop only that workspace.
-- Shared local infrastructure is machine-level: one Meilisearch at `http://localhost:7700` and one Chrome/CDP at `http://localhost:9222`. Manage it explicitly with `pnpm dev:infra:up`, `pnpm dev:infra:status`, and `pnpm dev:infra:down`.
+- Shared local infrastructure is machine-level: one Meilisearch at `http://localhost:7700` and one Chrome/CDP at the configured development port, `9250` by default. Override it with `MARKA_DEV_CHROME_PORT`.
- Parallel worktrees keep separate SQLite/assets data and unique web ports. `scripts/setup-worktree.sh` assigns each worktree a unique `MEILI_INDEX_PREFIX`; both `bookmarks` and `bookmarks_vectors` use that namespace on the shared Meilisearch server.
- `pnpm dev:start` defaults the main workspace namespace to `main_`. An unset `MEILI_INDEX_PREFIX` is a compatibility fallback for the original `bookmarks` and `bookmarks_vectors` names; manual `web` or `workers` starts outside `pnpm dev:start` must set an explicit unique prefix.
- Run focused checks before broad checks when practical. Standard checks are `pnpm format:fix`, `pnpm lint`, `pnpm typecheck`, and `pnpm test`.
@@ -126,7 +126,7 @@ Useful variants:
Local-dev ownership model:
- `web` + `workers` run natively per workspace
- one machine-level Meilisearch container is shared at `http://localhost:7700`
-- one machine-level Chrome container is shared at `http://localhost:9222`
+- one machine-level Chrome container is shared at `http://localhost:9250` by default; `MARKA_DEV_CHROME_PORT` changes this endpoint.
- `pnpm dev:start` automatically ensures those shared containers exist
- `pnpm dev:stop` never stops shared infrastructure because other worktrees may still use it
- the shared Chrome image is `ghcr.io/karakeep-app/karakeep-chrome:release`
@@ -152,7 +152,7 @@ pnpm workers
Notes:
- Meilisearch and headless Chrome are optional for booting the app, but required for full search/crawling behavior.
-- shared infra binds only to localhost; if ports `7700` or `9222` are occupied by something else, the helper fails rather than silently reusing an unknown service
+- shared infra binds only to localhost; if the configured ports `7700` or `MARKA_DEV_CHROME_PORT` are occupied by something else, the helper fails rather than silently reusing an unknown service
- If `next dev` crashes with a stale Turbopack/instrumentation issue, clear `apps/web/.next`.
### Pull prod state to local dev
diff --git a/apps/browser-extension/index.html b/apps/browser-extension/index.html
index 2d43e6b1e..94855e014 100644
--- a/apps/browser-extension/index.html
+++ b/apps/browser-extension/index.html
@@ -4,7 +4,7 @@
-
Karakeep
+ Marka
diff --git a/apps/browser-extension/manifest.json b/apps/browser-extension/manifest.json
index ee984766b..88e84371e 100644
--- a/apps/browser-extension/manifest.json
+++ b/apps/browser-extension/manifest.json
@@ -1,7 +1,7 @@
{
"manifest_version": 3,
- "name": "Karakeep",
- "description": "An extension to bookmark links to karakeep.app",
+ "name": "Marka",
+ "description": "An extension to bookmark links to your Marka server",
"version": "1.2.11",
"icons": {
"16": "public/logo-16.png",
@@ -65,7 +65,7 @@
"suggested_key": {
"default": "Ctrl+Shift+E"
},
- "description": "Send the current page URL to Karakeep."
+ "description": "Send the current page URL to Marka."
}
}
}
diff --git a/apps/browser-extension/src/Logo.tsx b/apps/browser-extension/src/Logo.tsx
index b7c470ca8..0b544a9ac 100644
--- a/apps/browser-extension/src/Logo.tsx
+++ b/apps/browser-extension/src/Logo.tsx
@@ -4,10 +4,10 @@ import logoImg from "../public/logo-full.png";
export default function Logo() {
return (
-
+
diff --git a/apps/browser-extension/src/background/background.ts b/apps/browser-extension/src/background/background.ts
index 0acaa9197..5f52cbdd8 100644
--- a/apps/browser-extension/src/background/background.ts
+++ b/apps/browser-extension/src/background/background.ts
@@ -57,20 +57,20 @@ function registerContextMenus(settings: Settings) {
removeContextMenus();
chrome.contextMenus.create({
id: OPEN_KARAKEEP_ID,
- title: "Open Karakeep",
+ title: "Open Marka",
contexts: ["action"],
});
chrome.contextMenus.create({
id: ADD_LINK_TO_KARAKEEP_ID,
- title: "Add to Karakeep",
+ title: "Add to Marka",
contexts: ["link", "page", "selection", "image"],
});
if (settings?.showCountBadge) {
chrome.contextMenus.create({
id: VIEW_PAGE_IN_KARAKEEP,
- title: "View this page in Karakeep",
+ title: "View this page in Marka",
contexts: ["action", "page"],
});
if (settings?.useBadgeCache) {
diff --git a/apps/cli/package.json b/apps/cli/package.json
index 4b6996aa9..d9bda1e27 100644
--- a/apps/cli/package.json
+++ b/apps/cli/package.json
@@ -2,7 +2,7 @@
"$schema": "https://json.schemastore.org/package.json",
"name": "@karakeep/cli",
"version": "0.32.0",
- "description": "Command Line Interface (CLI) for Karakeep",
+ "description": "Command Line Interface (CLI) for Marka",
"license": "GNU Affero General Public License version 3",
"type": "module",
"keywords": [
diff --git a/apps/cli/src/commands/auth.ts b/apps/cli/src/commands/auth.ts
index c780e0ae8..0a17e15fa 100644
--- a/apps/cli/src/commands/auth.ts
+++ b/apps/cli/src/commands/auth.ts
@@ -102,12 +102,12 @@ authCmd
opts.serverAddr ??
(await promptForValue(
rl,
- "Karakeep server address",
+ "Marka server address",
existingAuth.serverAddr ?? DEFAULT_SERVER_ADDR,
));
const apiKey =
opts.apiKey ??
- (await promptForValue(rl, "Karakeep API key", existingAuth.apiKey));
+ (await promptForValue(rl, "Marka API key", existingAuth.apiKey));
rl.close();
if (!serverAddr || !apiKey) {
diff --git a/apps/cli/src/commands/dump.ts b/apps/cli/src/commands/dump.ts
index 6f473182a..d52011c54 100644
--- a/apps/cli/src/commands/dump.ts
+++ b/apps/cli/src/commands/dump.ts
@@ -138,7 +138,7 @@ export const dumpCmd = new Command()
try {
line("");
- line(`${chalk.bold("Karakeep Dump")}`);
+ line(`${chalk.bold("Marka Dump")}`);
line(`${chalk.gray("Server:")} ${globals.serverAddr}`);
line(`${chalk.gray("Output:")} ${outFile}`);
line("");
diff --git a/apps/cli/src/commands/migrate.ts b/apps/cli/src/commands/migrate.ts
index 1e090acbc..5574351c3 100644
--- a/apps/cli/src/commands/migrate.ts
+++ b/apps/cli/src/commands/migrate.ts
@@ -112,7 +112,7 @@ export const migrateCmd = new Command()
try {
line("");
- line(`${chalk.bold("Karakeep Migration")}`);
+ line(`${chalk.bold("Marka Migration")}`);
line(`${chalk.gray("From:")} ${globals.serverAddr}`);
line(`${chalk.gray("To: ")} ${opts.destServer}`);
line("");
diff --git a/apps/cli/src/commands/wipe.ts b/apps/cli/src/commands/wipe.ts
index 713ac452a..5977e6177 100644
--- a/apps/cli/src/commands/wipe.ts
+++ b/apps/cli/src/commands/wipe.ts
@@ -94,7 +94,7 @@ export const wipeCmd = new Command()
try {
line("");
- line(`${chalk.bold("Karakeep Wipe")}`);
+ line(`${chalk.bold("Marka Wipe")}`);
line(`${chalk.gray("Server:")} ${globals.serverAddr}`);
line("");
diff --git a/apps/mcp/README.md b/apps/mcp/README.md
index ff822f7a9..f0159b51c 100644
--- a/apps/mcp/README.md
+++ b/apps/mcp/README.md
@@ -1,6 +1,6 @@
-# Karakeep MCP server
+# Marka MCP server
-This package contains the MCP server for Karakeep.
+This package contains the MCP server for Marka.
It exposes bookmark/list/tag operations for external MCP-compatible tools and agents.
diff --git a/apps/mcp/package.json b/apps/mcp/package.json
index 972ef17d7..1ef96886c 100644
--- a/apps/mcp/package.json
+++ b/apps/mcp/package.json
@@ -2,7 +2,7 @@
"$schema": "https://json.schemastore.org/package.json",
"name": "@karakeep/mcp",
"version": "0.32.0",
- "description": "MCP server for Karakeep",
+ "description": "MCP server for Marka",
"license": "GNU Affero General Public License version 3",
"type": "module",
"keywords": [
diff --git a/apps/mcp/src/shared.ts b/apps/mcp/src/shared.ts
index b2ffff053..547cb6ee8 100644
--- a/apps/mcp/src/shared.ts
+++ b/apps/mcp/src/shared.ts
@@ -27,7 +27,7 @@ export const karakeepClient = createKarakeepClient({
});
export const mcpServer = new McpServer({
- name: "Karakeep",
+ name: "Marka",
version: "0.23.0",
});
diff --git a/apps/mobile/app.config.js b/apps/mobile/app.config.js
index 793a774c0..820ba7f0b 100644
--- a/apps/mobile/app.config.js
+++ b/apps/mobile/app.config.js
@@ -4,11 +4,11 @@ export default {
expo: {
...(IS_DEV
? {
- name: "Karakeep (Dev)",
+ name: "Marka (Dev)",
scheme: "karakeep-dev",
}
: {
- name: "Karakeep",
+ name: "Marka",
scheme: "karakeep",
}),
slug: "hoarder",
diff --git a/apps/mobile/app/dashboard/(tabs)/_layout.tsx b/apps/mobile/app/dashboard/(tabs)/_layout.tsx
index d25710bd7..be41cbaa6 100644
--- a/apps/mobile/app/dashboard/(tabs)/_layout.tsx
+++ b/apps/mobile/app/dashboard/(tabs)/_layout.tsx
@@ -49,15 +49,7 @@ export default function TabLayout() {
-
-
- }
- />
-
-
+
- Enter the URL of your Karakeep server
+ Enter the URL of your Marka server
({
default: ({ children, href, ...props }: React.ComponentProps<"a">) => (
@@ -35,14 +35,42 @@ vi.mock("@/server/auth", () => ({
import Header from "./Header";
describe("Header", () => {
+ afterEach(() => {
+ cleanup();
+ });
+
it("uses a left-aligned Marka wordmark in the original reserved logo slot", async () => {
render(await Header());
+ const header = screen.getByRole("banner");
const link = screen.getByRole("link", { name: "Marka" });
+ const search = screen.getByRole("textbox", { name: "Search" });
expect(link.getAttribute("href")).toBe("/dashboard/bookmarks");
expect(link.classList.contains("justify-start")).toBe(true);
expect(link.classList.contains("w-56")).toBe(true);
expect(link.classList.contains("pl-2")).toBe(true);
expect(screen.getByAltText("Marka").getAttribute("height")).toBe("30");
+ expect(header.firstElementChild?.className).toContain(
+ "lg:grid-cols-[minmax(0,1fr)_minmax(0,2fr)_minmax(0,1fr)]",
+ );
+ expect(search.parentElement?.className).toContain("lg:col-start-2");
+ });
+
+ it("groups the status rectangle and profile avatar with the avatar on top", async () => {
+ render(await Header());
+
+ const profile = screen.getByRole("button", { name: "Profile" });
+ const avatarLayer = profile.parentElement;
+ const controls = avatarLayer?.parentElement;
+
+ expect(avatarLayer?.className).toContain("absolute");
+ expect(avatarLayer?.className).toContain("right-0");
+ expect(avatarLayer?.className).toContain("top-1/2");
+ expect(avatarLayer?.className).toContain("-translate-y-1/2");
+ expect(avatarLayer?.className).toContain("size-10");
+ expect(avatarLayer?.className).toContain("z-10");
+ expect(controls?.className).toContain("relative");
+ expect(controls?.className).toContain("h-10");
+ expect(controls?.className).toContain("pr-5");
});
});
diff --git a/apps/web/components/dashboard/header/Header.tsx b/apps/web/components/dashboard/header/Header.tsx
index c95f38c5b..5f1ea001b 100644
--- a/apps/web/components/dashboard/header/Header.tsx
+++ b/apps/web/components/dashboard/header/Header.tsx
@@ -15,7 +15,7 @@ export default async function Header() {
return (
-
+
-
+
-
+
-
+
+
+
diff --git a/apps/web/components/dashboard/header/ProcessingStatusIndicator.tsx b/apps/web/components/dashboard/header/ProcessingStatusIndicator.tsx
index 957d5ab9e..c2b94fea6 100644
--- a/apps/web/components/dashboard/header/ProcessingStatusIndicator.tsx
+++ b/apps/web/components/dashboard/header/ProcessingStatusIndicator.tsx
@@ -481,7 +481,7 @@ export default function ProcessingStatusIndicator() {