diff --git a/_content/cases/stepsy.wiki.md b/_content/cases/stepsy.wiki.md
index 0349747..4312134 100644
--- a/_content/cases/stepsy.wiki.md
+++ b/_content/cases/stepsy.wiki.md
@@ -6,8 +6,17 @@ date: Jul 29, 2023 - Nov 5, 2023
z: 5
---
-
-Solution
+This project aims to be a Google Drive frontend. It uses the Google APIs to fetch document data and display that data in a wiki-style web page.
+
+
+![thumbnail](/thumbnail.png)
+
+
+It supports Google Docs, Google Sheets, Google Slides, PDFs and regular files.
+
+---
+
+### Technical overview
I chose NextJS as the backbone for this project as it offers the greatest amount of flexibility while still being very powerful both on the client as well as on the server with an active community and thriving ecosystem.
@@ -31,12 +40,100 @@ The UI/UX uses TailwindCSS and DaisyUI to make everything a fast, modern, optimi
React was used with TypeScript to provide a nice modern client-side experience between transitions and interactions.
This setup supports maximum optimization as you can see in the screenshots below allowing the app to reach a lighthouse score of 100 on all but one (it has 99) pages.
Both mobile and desktop is supported.
-
-
-This project aims to be a Google Drive frontend. It uses the Google APIs to fetch document data and display that data in a wiki-style web page.
-![thumbnail](/thumbnail.png)
+---
-It supports Google Docs, Google Sheets, Google Slides, PDFs and regular files.
+### Google API details
+
+Configure NextAuth for Google:
+
+```ts
+export default NextAuth({
+...
+ providers: [
+ GoogleProvider({
+ clientId: process.env.GOOGLE_CLIENT_ID!,
+ clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
+ authorization: {
+ params: {
+ access_type: "offline",
+ prompt: "consent",
+ scope: "openid profile email https://www.googleapis.com/auth/drive",
+ },
+ },
+ }),
+ ],
+...
+```
+
+Create an auth client for logged in users
+
+```ts
+let authClient = new google.auth.OAuth2(
+ process.env.GOOGLE_CLIENT_ID,
+ process.env.GOOGLE_CLIENT_SECRET,
+);
+authClient.setCredentials({
+ access_token: accessToken, // this comes from the logged in user info
+ refresh_token: refreshToken, // same for this
+});
+```
+
+or one for anonymous users using a Google service account
+
+```ts
+authClient = new google.auth.JWT({
+ email: serviceAccount.client_email,
+ key: serviceAccount.private_key,
+ scopes: ["https://www.googleapis.com/auth/drive"],
+});
+```
+
+Create the drive client
+
+```ts
+const drive = google.drive({
+ version: "v3",
+ auth: authClient,
+});
+```
+
+You can now use this client to query the API
+
+```ts
+const file = (await drive.files.get({fileId})).data;
+```
+
+```ts
+const folderContents = (await drive.files.list({ q: `'${folderId}' in parents` }))
+ .data.files;
+```
+
+```ts
+const googleDocHtml = (await drive.files.export({
+ fileId: googleDocId,
+ mimeType: "text/html",
+})).data;
+```
+
+```ts
+const shortcutTarget = await drive.files.get({
+ fileId,
+ fields: "shortcutDetails/targetId",
+});
+const targetId = shortcutTarget.data.shortcutDetails?.targetId
+```
+
+Google doesn't export everything to HTML. They provide document renderers as iFrames.
+
+```tsx
+
+```
+
+```tsx
+// This is used for PDFs or regular text files
+
+```
+
diff --git a/bun.lockb b/bun.lockb
index d9a9cae..cfff84b 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/package.json b/package.json
index 97800ad..c80645c 100644
--- a/package.json
+++ b/package.json
@@ -16,6 +16,7 @@
"@fortawesome/free-solid-svg-icons": "^6.4.2",
"@fortawesome/react-fontawesome": "latest",
"gray-matter": "^4.0.3",
+ "highlight.js": "^11.9.0",
"next": "latest",
"react": "latest",
"react-dom": "latest",
@@ -23,8 +24,7 @@
"rehype-highlight": "^7.0.0",
"rehype-raw": "^7.0.0",
"remark-frontmatter": "^5.0.0",
- "remark-gfm": "^4.0.0",
- "tailwind-highlightjs": "^2.0.1"
+ "remark-gfm": "^4.0.0"
},
"devDependencies": {
"typescript": "latest",
diff --git a/src/app/c/[...slug]/content.module.css b/src/app/c/[...slug]/content.module.css
index 2b55e18..54cbbbc 100644
--- a/src/app/c/[...slug]/content.module.css
+++ b/src/app/c/[...slug]/content.module.css
@@ -1,3 +1,7 @@
+.md * {
+ @apply py-2
+}
+
.md ol {
@apply list-decimal list-inside
}
@@ -5,6 +9,10 @@
@apply list-disc list-inside
}
+.md hr {
+ @apply m-4
+}
+
.md blockquote {
@apply border-l-neutral-500 border-l-4 p-2
}
@@ -17,8 +25,8 @@
@apply p-20
}
-.md details * {
- @apply p-2
+.md code {
+ @apply rounded-lg
}
.md h1 {
diff --git a/src/app/c/[...slug]/page.tsx b/src/app/c/[...slug]/page.tsx
index 11f0ad6..6843292 100644
--- a/src/app/c/[...slug]/page.tsx
+++ b/src/app/c/[...slug]/page.tsx
@@ -1,12 +1,15 @@
+import "highlight.js/styles/github-dark.css"
+import styles from "./content.module.css"
import { getAllPaths, getContent } from "$lib/content";
import Markdown from "react-markdown";
import remarkGfm from "remark-gfm";
import remarkFrontmatter from "remark-frontmatter";
-import styles from "./content.module.css"
import Image from "next/image";
import rehypeRaw from "rehype-raw";
import rehypeHighlight from "rehype-highlight";
import { notFound } from "next/navigation";
+import Link from "next/link";
+import CodeBlock from "$components/code-block";
type Params = {
slug: string[]
@@ -58,12 +61,22 @@ export default function Content({ params }: Props) {
remarkPlugins={[remarkGfm, remarkFrontmatter]}
rehypePlugins={[rehypeRaw, rehypeHighlight]}
components={{
- img({ height, width, src, alt }) {
+ img({ height, width, src, alt, className }) {
return (
-
+
)
+ },
+ a({ href, children, className }) {
+ return (
+ {children}
+ )
+ },
+ pre({ children, className }) {
+ return (
+
+ )
}
}}
>
@@ -72,7 +85,7 @@ export default function Content({ params }: Props) {
return (