aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.claude/settings.json9
-rw-r--r--.gitignore1
-rw-r--r--GEMINI.md1
-rw-r--r--apps/mobile/app/dashboard/(tabs)/settings.tsx27
-rw-r--r--apps/mobile/app/dashboard/_layout.tsx8
-rw-r--r--apps/mobile/app/dashboard/bookmarks/[slug]/index.tsx9
-rw-r--r--apps/mobile/app/dashboard/settings/bookmark-default-view.tsx68
-rw-r--r--apps/mobile/lib/settings.ts11
-rw-r--r--docs/docs/07-Development/01-setup.md60
-rw-r--r--docs/docs/07-Development/03-database.md2
-rwxr-xr-xstart-dev.sh28
11 files changed, 214 insertions, 10 deletions
diff --git a/.claude/settings.json b/.claude/settings.json
new file mode 100644
index 00000000..4ff39376
--- /dev/null
+++ b/.claude/settings.json
@@ -0,0 +1,9 @@
+{
+ "permissions": {
+ "allow": [
+ "Bash(pnpm typecheck:*)",
+ "Bash(pnpm lint:*)"
+ ],
+ "deny": []
+ }
+}
diff --git a/.gitignore b/.gitignore
index 0ded3a78..43444b4c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -61,3 +61,4 @@ data
# VS-Code
.vscode
auth_failures.log
+.claude/settings.local.json
diff --git a/GEMINI.md b/GEMINI.md
index ccf5930f..2ac6de48 100644
--- a/GEMINI.md
+++ b/GEMINI.md
@@ -62,6 +62,7 @@ The project is organized into `apps` and `packages`:
- `pnpm format`: Format the codebase.
- `pnpm format:fix`: Fix formatting issues.
- `pnpm test`: Run tests.
+- `pnpm db:generate --name description_of_schema_change`: db migration after making schema changes
Starting services:
- `pnpm web`: Start the web application (this doesn't return, unless you kill it).
diff --git a/apps/mobile/app/dashboard/(tabs)/settings.tsx b/apps/mobile/app/dashboard/(tabs)/settings.tsx
index db118df8..b0ba94df 100644
--- a/apps/mobile/app/dashboard/(tabs)/settings.tsx
+++ b/apps/mobile/app/dashboard/(tabs)/settings.tsx
@@ -1,5 +1,5 @@
import { useEffect } from "react";
-import { Pressable, Text, View } from "react-native";
+import { ActivityIndicator, Pressable, Text, View } from "react-native";
import { Slider } from "react-native-awesome-slider";
import { useSharedValue } from "react-native-reanimated";
import { Link } from "expo-router";
@@ -67,6 +67,31 @@ export default function Dashboard() {
</Pressable>
</Link>
</View>
+ <View className="flex w-full flex-row items-center justify-between gap-8 rounded-lg bg-white px-4 py-2 dark:bg-accent">
+ <Link
+ asChild
+ href="/dashboard/settings/bookmark-default-view"
+ className="flex-1"
+ >
+ <Pressable className="flex flex-row justify-between">
+ <Text className="text-lg text-accent-foreground">
+ Default Bookmark View
+ </Text>
+ <View className="flex flex-row items-center gap-2">
+ {isSettingsLoading ? (
+ <ActivityIndicator size="small" />
+ ) : (
+ <Text className="text-lg text-muted-foreground">
+ {settings.defaultBookmarkView === "reader"
+ ? "Reader"
+ : "Browser"}
+ </Text>
+ )}
+ <ChevronRight color="rgb(0, 122, 255)" />
+ </View>
+ </Pressable>
+ </Link>
+ </View>
<Text className="w-full p-1 text-2xl font-bold text-foreground">
Upload Settings
</Text>
diff --git a/apps/mobile/app/dashboard/_layout.tsx b/apps/mobile/app/dashboard/_layout.tsx
index 223101bd..eb1cbe4b 100644
--- a/apps/mobile/app/dashboard/_layout.tsx
+++ b/apps/mobile/app/dashboard/_layout.tsx
@@ -136,6 +136,14 @@ export default function Dashboard() {
headerBackTitle: "Back",
}}
/>
+ <Stack.Screen
+ name="settings/bookmark-default-view"
+ options={{
+ title: "Bookmark View Mode",
+ headerTitle: "Bookmark View Mode",
+ headerBackTitle: "Back",
+ }}
+ />
</StyledStack>
);
}
diff --git a/apps/mobile/app/dashboard/bookmarks/[slug]/index.tsx b/apps/mobile/app/dashboard/bookmarks/[slug]/index.tsx
index 00954dd8..eafcfc19 100644
--- a/apps/mobile/app/dashboard/bookmarks/[slug]/index.tsx
+++ b/apps/mobile/app/dashboard/bookmarks/[slug]/index.tsx
@@ -1,4 +1,4 @@
-import React, { useState } from "react";
+import { useState } from "react";
import {
Alert,
Keyboard,
@@ -27,6 +27,7 @@ import FullPageSpinner from "@/components/ui/FullPageSpinner";
import { Input } from "@/components/ui/Input";
import { useToast } from "@/components/ui/Toast";
import { useAssetUrl } from "@/lib/hooks";
+import useAppSettings from "@/lib/settings";
import { api } from "@/lib/trpc";
import { MenuView } from "@react-native-menu/menu";
import {
@@ -378,9 +379,11 @@ export default function ListView() {
const { slug } = useLocalSearchParams();
const { colorScheme } = useColorScheme();
const isDark = colorScheme === "dark";
+ const { settings } = useAppSettings();
- const [bookmarkLinkType, setBookmarkLinkType] =
- useState<BookmarkLinkType>("browser");
+ const [bookmarkLinkType, setBookmarkLinkType] = useState<BookmarkLinkType>(
+ settings.defaultBookmarkView,
+ );
if (typeof slug !== "string") {
throw new Error("Unexpected param type");
diff --git a/apps/mobile/app/dashboard/settings/bookmark-default-view.tsx b/apps/mobile/app/dashboard/settings/bookmark-default-view.tsx
new file mode 100644
index 00000000..c8c522cf
--- /dev/null
+++ b/apps/mobile/app/dashboard/settings/bookmark-default-view.tsx
@@ -0,0 +1,68 @@
+import { Pressable, Text, View } from "react-native";
+import { useRouter } from "expo-router";
+import CustomSafeAreaView from "@/components/ui/CustomSafeAreaView";
+import { Divider } from "@/components/ui/Divider";
+import { useToast } from "@/components/ui/Toast";
+import useAppSettings from "@/lib/settings";
+import { Check } from "lucide-react-native";
+
+export default function BookmarkDefaultViewSettings() {
+ const router = useRouter();
+ const { toast } = useToast();
+ const { settings, setSettings } = useAppSettings();
+
+ const handleUpdate = async (mode: "reader" | "browser") => {
+ try {
+ await setSettings({
+ ...settings,
+ defaultBookmarkView: mode,
+ });
+ toast({
+ message: "Default Bookmark View updated!",
+ showProgress: false,
+ });
+ router.back();
+ } catch {
+ toast({
+ message: "Something went wrong",
+ variant: "destructive",
+ showProgress: false,
+ });
+ }
+ };
+
+ const options = (["reader", "browser"] as const)
+ .map((mode) => {
+ const currentMode = settings.defaultBookmarkView;
+ const isChecked = currentMode === mode;
+ return [
+ <Pressable
+ onPress={() => handleUpdate(mode)}
+ className="flex flex-row justify-between"
+ key={mode}
+ >
+ <Text className="text-lg text-accent-foreground">
+ {{ browser: "Browser", reader: "Reader" }[mode]}
+ </Text>
+ {isChecked && <Check color="rgb(0, 122, 255)" />}
+ </Pressable>,
+ <Divider
+ key={mode + "-divider"}
+ orientation="horizontal"
+ className="my-3 h-0.5 w-full"
+ />,
+ ];
+ })
+ .flat();
+ options.pop();
+
+ return (
+ <CustomSafeAreaView>
+ <View className="flex h-full w-full items-center px-4 py-2">
+ <View className="w-full rounded-lg bg-white px-4 py-2 dark:bg-accent">
+ {options}
+ </View>
+ </View>
+ </CustomSafeAreaView>
+ );
+}
diff --git a/apps/mobile/lib/settings.ts b/apps/mobile/lib/settings.ts
index 58b0817f..51fa661f 100644
--- a/apps/mobile/lib/settings.ts
+++ b/apps/mobile/lib/settings.ts
@@ -10,6 +10,10 @@ const zSettingsSchema = z.object({
address: z.string(),
imageQuality: z.number().optional().default(0.2),
theme: z.enum(["light", "dark", "system"]).optional().default("system"),
+ defaultBookmarkView: z
+ .enum(["reader", "browser"])
+ .optional()
+ .default("reader"),
});
export type Settings = z.infer<typeof zSettingsSchema>;
@@ -23,7 +27,12 @@ interface AppSettingsState {
const useSettings = create<AppSettingsState>((set, get) => ({
settings: {
isLoading: true,
- settings: { address: "", imageQuality: 0.2, theme: "system" },
+ settings: {
+ address: "",
+ imageQuality: 0.2,
+ theme: "system",
+ defaultBookmarkView: "reader",
+ },
},
setSettings: async (settings) => {
await SecureStore.setItemAsync(SETTING_NAME, JSON.stringify(settings));
diff --git a/docs/docs/07-Development/01-setup.md b/docs/docs/07-Development/01-setup.md
index 2d668556..6072a377 100644
--- a/docs/docs/07-Development/01-setup.md
+++ b/docs/docs/07-Development/01-setup.md
@@ -1,5 +1,31 @@
# Setup
+## Quick Start
+
+For the fastest way to get started with development, use the one-command setup script:
+
+```bash
+./start-dev.sh
+```
+
+This script will automatically:
+- Start Meilisearch in Docker (on port 7700)
+- Start headless Chrome in Docker (on port 9222)
+- Install dependencies with `pnpm install` if needed
+- Start both the web app and workers in parallel
+- Provide cleanup when you stop with Ctrl+C
+
+**Prerequisites:**
+- Docker installed and running
+- pnpm installed (see manual setup below for installation instructions)
+
+The script will output the running services:
+- Web app: http://localhost:3000
+- Meilisearch: http://localhost:7700
+- Chrome debugger: http://localhost:9222
+
+Press Ctrl+C to stop all services and clean up Docker containers.
+
## Manual Setup
Karakeep uses `node` version 22. To install it, you can use `nvm` [^1]
@@ -88,14 +114,44 @@ The worker app will automatically start headless chrome on startup for crawling
- Run `pnpm workers` in the root of the repo.
-### iOS Mobile App
+### Mobile App (iOS & Android)
+
+#### Prerequisites
+
+To build and run the mobile app locally, you'll need:
+
+- **For iOS development**:
+ - macOS computer
+ - Xcode installed from the App Store
+ - iOS Simulator (comes with Xcode)
+
+- **For Android development**:
+ - Android Studio installed
+ - Android SDK configured
+ - Android Emulator or physical device
+
+For detailed setup instructions, refer to the [Expo documentation](https://docs.expo.dev/guides/local-app-development/).
+
+#### Running the app
- `cd apps/mobile`
- `pnpm exec expo prebuild --no-install` to build the app.
-- Start the ios simulator.
+
+**For iOS:**
- `pnpm exec expo run:ios`
- The app will be installed and started in the simulator.
+**Troubleshooting iOS Setup:**
+If you encounter an error like `xcrun: error: SDK "iphoneos" cannot be located`, you may need to set the correct Xcode developer directory:
+```bash
+sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
+```
+
+**For Android:**
+- Start the Android emulator or connect a physical device.
+- `pnpm exec expo run:android`
+- The app will be installed and started on the emulator/device.
+
Changing the code will hot reload the app. However, installing new packages requires restarting the expo server.
### Browser Extension
diff --git a/docs/docs/07-Development/03-database.md b/docs/docs/07-Development/03-database.md
index a5dee2a9..d7a15b62 100644
--- a/docs/docs/07-Development/03-database.md
+++ b/docs/docs/07-Development/03-database.md
@@ -2,7 +2,7 @@
- The database schema lives in `packages/db/schema.ts`.
- Changing the schema, requires a migration.
-- You can generate the migration by running `pnpm run db:generate` in the root dir.
+- You can generate the migration by running `pnpm run db:generate --name description_of_schema_change` in the root dir.
- You can then apply the migration by running `pnpm run db:migrate`.
## Drizzle Studio
diff --git a/start-dev.sh b/start-dev.sh
index a421be34..0f5f9d2e 100755
--- a/start-dev.sh
+++ b/start-dev.sh
@@ -50,9 +50,33 @@ if [ ! -d "node_modules" ]; then
pnpm install
fi
-# Start the web app and workers in parallel
-echo "Starting web app and workers..."
+# Get DATA_DIR from environment or .env file
+if [ -z "$DATA_DIR" ] && [ -f ".env" ]; then
+ DATA_DIR=$(grep "^DATA_DIR=" .env | cut -d'=' -f2)
+fi
+
+# Create DATA_DIR if it doesn't exist
+if [ -n "$DATA_DIR" ] && [ ! -d "$DATA_DIR" ]; then
+ echo "Creating DATA_DIR at $DATA_DIR..."
+ mkdir -p "$DATA_DIR"
+fi
+
+# Start the web app
+echo "Starting web app..."
pnpm web & WEB_PID=$!
+
+# Wait for web app to be ready
+echo "Waiting for web app to start..."
+until curl -s http://localhost:3000 > /dev/null 2>&1; do
+ sleep 1
+done
+
+# Run database migrations
+echo "Running database migrations..."
+pnpm run db:migrate
+
+# Start workers
+echo "Starting workers..."
pnpm workers & WORKERS_PID=$!
# Function to handle script termination