diff --git a/content/docs/guides/oidc.mdx b/content/docs/guides/oidc.mdx
index fe9c204..0f0614b 100644
--- a/content/docs/guides/oidc.mdx
+++ b/content/docs/guides/oidc.mdx
@@ -23,7 +23,9 @@ To set up OIDC authentication with Pocket Id, follow these steps:
Go to your Pocket ID dashboard and create a new application. Give it a name and leave the redirect URI blank for now.
-TODO: Pocket Id application creation image here
+
+
+You can find the OrcaCD logo [here](https://github.com/OrcaCD/orca-cd/tree/main/frontend/public/assets).
### 2. Configure OrcaCD
@@ -35,7 +37,7 @@ TODO: Pocket Id application creation image here
- **Client Secret**: The client secret from your Pocket Id application.
3. Save the configuration and ensure it is enabled.
-TODO: Orca CD OIDC configuration image here
+
### 3. Test the Configuration
@@ -44,4 +46,4 @@ TODO: Orca CD OIDC configuration image here
3. You will be redirected to the Pocket Id login page. Enter your credentials and log in.
4. After successful authentication, you will be redirected back to OrcaCD and logged in with your Pocket Id account.
-TODO: Login page image here
+

diff --git a/content/docs/guides/reverse-proxy.mdx b/content/docs/guides/reverse-proxy.mdx
index 6be3249..1eb304e 100644
--- a/content/docs/guides/reverse-proxy.mdx
+++ b/content/docs/guides/reverse-proxy.mdx
@@ -11,7 +11,7 @@ TODO
To use Traefik as a reverse proxy for OrcaCD, you can add the following labels to your Docker Compose configuration for the OrcaCD Hub service:
-```yml
+```yml lineNumbers
services:
hub:
image: ghcr.io/orcacd/hub:latest
diff --git a/content/docs/helping-out/documentation.mdx b/content/docs/helping-out/documentation.mdx
index 4462e3f..e9ad91a 100644
--- a/content/docs/helping-out/documentation.mdx
+++ b/content/docs/helping-out/documentation.mdx
@@ -17,7 +17,7 @@ All markdown pages are under `/content/docs`. The file `meta.json` controls the
3. Add frontmatter:
-```yaml
+```yaml lineNumbers
---
title: Installation
description: Get OrcaCD running quickly with Docker installation
diff --git a/content/docs/troubleshooting/account-recovery.mdx b/content/docs/troubleshooting/account-recovery.mdx
new file mode 100644
index 0000000..95070f2
--- /dev/null
+++ b/content/docs/troubleshooting/account-recovery.mdx
@@ -0,0 +1,17 @@
+---
+title: Account Recovery
+description: Steps to recover your account if you have lost access
+---
+
+There are two ways to reset a password for a user:
+
+1. UI: An admin can reset the password for a user in the admin panel under the "Users" tab by clicking on the three dots next to the user's name and selecting "Edit".
+2. Terminal: You can reset a password for a user by running `hub reset-password `. To execute this script with Docker you have to run the following command:
+
+```bash
+docker compose exec hub /app/hub reset-password
+```
+
+The user with the reset password has to change the password on the next login.
+
+
diff --git a/public/assets/docs/orca-sso-registration.png b/public/assets/docs/orca-sso-registration.png
new file mode 100644
index 0000000..9087c39
Binary files /dev/null and b/public/assets/docs/orca-sso-registration.png differ
diff --git a/public/assets/docs/pocketid-orca-registration.png b/public/assets/docs/pocketid-orca-registration.png
new file mode 100644
index 0000000..529abc9
Binary files /dev/null and b/public/assets/docs/pocketid-orca-registration.png differ
diff --git a/public/assets/docs/sso-login-screen.png b/public/assets/docs/sso-login-screen.png
new file mode 100644
index 0000000..4aba7c4
Binary files /dev/null and b/public/assets/docs/sso-login-screen.png differ
diff --git a/src/components/account-recovery-terminal.tsx b/src/components/account-recovery-terminal.tsx
new file mode 100644
index 0000000..698bc0e
--- /dev/null
+++ b/src/components/account-recovery-terminal.tsx
@@ -0,0 +1,26 @@
+import { AnimatedSpan, Terminal, TypingAnimation } from "./terminal";
+
+export function AccountRecoveryTerminal() {
+ return (
+
+
+ $ docker compose exec hub /app/hub reset-password 019dc111-5220-77bc-9729-2335f88fa658
+
+
+ {`
+╭────────────────────────────────────────────────────────────────────╮
+│ │
+│ Password Reset Successful │
+│ │
+│ User ID: 019dc111-5220-77bc-9729-2335f88fa658 │
+│ Email: test@orcacd.dev │
+│ New temporary password: w&BU6G,WM#!MX9M4eq │
+│ │
+│ Important: The user must change this password on the next login. │
+│ │
+╰────────────────────────────────────────────────────────────────────╯
+`}
+
+
+ );
+}
diff --git a/src/components/terminal.tsx b/src/components/terminal.tsx
new file mode 100644
index 0000000..29e8477
--- /dev/null
+++ b/src/components/terminal.tsx
@@ -0,0 +1,111 @@
+"use client";
+
+import { cn } from "@/lib/utils";
+import { type MotionProps, motion } from "motion/react";
+import { useEffect, useRef, useState } from "react";
+
+interface AnimatedSpanProps extends MotionProps {
+ children: React.ReactNode;
+ delay?: number;
+ className?: string;
+}
+
+export const AnimatedSpan = ({ children, delay = 0, className, ...props }: AnimatedSpanProps) => (
+
+ {children}
+
+);
+
+interface TypingAnimationProps extends MotionProps {
+ children: string;
+ className?: string;
+ duration?: number;
+ delay?: number;
+ as?: React.ElementType;
+}
+
+export const TypingAnimation = ({
+ children,
+ className,
+ duration = 60,
+ delay = 0,
+ as: Component = "span",
+ ...props
+}: TypingAnimationProps) => {
+ if (typeof children !== "string") {
+ throw new Error("TypingAnimation: children must be a string. Received:");
+ }
+
+ const MotionComponent = motion.create(Component, {
+ forwardMotionProps: true,
+ });
+
+ const [displayedText, setDisplayedText] = useState("");
+ const [started, setStarted] = useState(false);
+ const elementRef = useRef(null);
+
+ useEffect(() => {
+ const startTimeout = setTimeout(() => {
+ setStarted(true);
+ }, delay);
+ return () => clearTimeout(startTimeout);
+ }, [delay]);
+
+ useEffect(() => {
+ if (!started) {
+ return;
+ }
+
+ let i = 0;
+ const typingEffect = setInterval(() => {
+ if (i < children.length) {
+ setDisplayedText(children.substring(0, i + 1));
+ i++;
+ } else {
+ clearInterval(typingEffect);
+ }
+ }, duration);
+
+ return () => {
+ clearInterval(typingEffect);
+ };
+ }, [children, duration, started]);
+
+ return (
+
+ {displayedText}
+
+ );
+};
+
+interface TerminalProps {
+ children: React.ReactNode;
+ className?: string;
+}
+
+export const Terminal = ({ children, className }: TerminalProps) => {
+ return (
+
+ );
+};
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
new file mode 100644
index 0000000..ac680b3
--- /dev/null
+++ b/src/lib/utils.ts
@@ -0,0 +1,6 @@
+import { type ClassValue, clsx } from "clsx";
+import { twMerge } from "tailwind-merge";
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs));
+}
diff --git a/src/routes/docs/$.tsx b/src/routes/docs/$.tsx
index 9d24fac..b50b419 100644
--- a/src/routes/docs/$.tsx
+++ b/src/routes/docs/$.tsx
@@ -17,6 +17,8 @@ import defaultMdxComponents from "fumadocs-ui/mdx";
import { baseOptions } from "@/lib/layout.shared";
import { getPageMarkdownUrl, source } from "@/lib/source";
import { Suspense } from "react";
+import { AccountRecoveryTerminal } from "@/components/account-recovery-terminal";
+import { ImageZoom } from "fumadocs-ui/components/image-zoom";
export const Route = createFileRoute("/docs/$")({
component: Page,
@@ -77,6 +79,8 @@ const clientLoader = browserCollections.docs.createClientLoader({
,
}}
/>