diff options
| -rw-r--r-- | .claude/settings.json | 9 | ||||
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | GEMINI.md | 1 | ||||
| -rw-r--r-- | apps/mobile/app/dashboard/(tabs)/settings.tsx | 27 | ||||
| -rw-r--r-- | apps/mobile/app/dashboard/_layout.tsx | 8 | ||||
| -rw-r--r-- | apps/mobile/app/dashboard/bookmarks/[slug]/index.tsx | 9 | ||||
| -rw-r--r-- | apps/mobile/app/dashboard/settings/bookmark-default-view.tsx | 68 | ||||
| -rw-r--r-- | apps/mobile/lib/settings.ts | 11 | ||||
| -rw-r--r-- | docs/docs/07-Development/01-setup.md | 60 | ||||
| -rw-r--r-- | docs/docs/07-Development/03-database.md | 2 | ||||
| -rwxr-xr-x | start-dev.sh | 28 |
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": [] + } +} @@ -61,3 +61,4 @@ data # VS-Code .vscode auth_failures.log +.claude/settings.local.json @@ -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 |
