Skip to content

Commit 9ff8d0f

Browse files
Merge pull request #20 from fastapi-startkit/home-page-fix
feat: homepage section fixed
2 parents 246a500 + a4b572c commit 9ff8d0f

12 files changed

Lines changed: 163 additions & 21 deletions

.vitepress/theme/components/HeroSection.vue

Lines changed: 97 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
<script setup>
2+
import { Copy, FileText, Terminal } from "lucide-vue-next"
23
import { computed, onMounted, onUnmounted, ref } from "vue"
3-
import { Terminal, FileText, Copy } from "lucide-vue-next"
44
55
// ── Raw Python snippet imports ──────────────────────────────────────────────
66
import applicationRaw from "../snippets/application.py?raw"
77
import artisanRaw from "../snippets/artisan?raw"
8+
import databaseAppRaw from "../snippets/database_app.py?raw"
89
import dbMigrationsRaw from "../snippets/database_migrations.py?raw"
910
import dbModelsRaw from "../snippets/database_models.py?raw"
11+
import fastapiAppRaw from "../snippets/fastapi-app.py?raw"
1012
import fastapiRaw from "../snippets/fastapi.py?raw"
11-
import fastapiControllerRaw from "../snippets/fastapi_users_controller.py?raw"
1213
import fastapiRoutesRaw from "../snippets/fastapi_routes.py?raw"
14+
import fastapiControllerRaw from "../snippets/fastapi_users_controller.py?raw"
1315
import logLoggerRaw from "../snippets/logging_logger.py?raw"
1416
1517
// ── Word rotator ────────────────────────────────────────────────────────────
@@ -32,16 +34,15 @@
3234
onUnmounted(() => { if (interval) clearInterval(interval) })
3335
3436
// ── Tab / file structure ────────────────────────────────────────────────────
35-
const categories = ["Application", "FastAPI", "Database", "Migrations", "Logging"]
37+
const categories = ["Application", "FastAPI", "Database", "Logging"]
3638
const activeCategory = ref("Application")
3739
const activeFileIndex = ref(0)
3840
const isTransitioning = ref(false)
3941
4042
const tabData = {
4143
Application: { files: ["application.py", "artisan"], raw: [applicationRaw, artisanRaw] },
42-
FastAPI: { files: ["fastapi.py", "users_controllers.py", "api.py"], raw: [fastapiRaw, fastapiControllerRaw, fastapiRoutesRaw] },
43-
Database: { files: ["models.py"], raw: [dbModelsRaw] },
44-
Migrations: { files: ["2026_04_26_110113_create_users.py"], raw: [dbMigrationsRaw] },
44+
FastAPI: { files: ["api.py", "application", "api.py", "users_controllers.py"], raw: [fastapiRaw, fastapiAppRaw, fastapiRoutesRaw, fastapiControllerRaw] },
45+
Database: { files: ["application.py", "2026_04_26_110113_create_users.py", "models.py"], raw: [databaseAppRaw, dbMigrationsRaw, dbModelsRaw] },
4546
Logging: { files: ["logging.py"], raw: [logLoggerRaw] },
4647
}
4748
@@ -50,6 +51,25 @@
5051
const highlighted = ref({})
5152
const highlighterReady = ref(false)
5253
54+
// Strip diff markers from source and return { code, lineTypes }
55+
// Supported markers: # [+] # [-] # [!hl]
56+
function extractHighlights(raw) {
57+
const lines = raw.split("\n")
58+
const lineTypes = {} // index -> 'add' | 'remove' | 'highlight'
59+
const cleaned = lines.map((line, i) => {
60+
if (line.includes("# [+]")) {
61+
lineTypes[i] = "add"
62+
return line.replace(/\s*#\s*\[\+\]/, "")
63+
}
64+
if (line.includes("# [-]")) {
65+
lineTypes[i] = "remove"
66+
return line.replace(/\s*#\s*\[-\]/, "")
67+
}
68+
return line
69+
})
70+
return { code: cleaned.join("\n"), lineTypes }
71+
}
72+
5373
onMounted(async () => {
5474
try {
5575
const { createHighlighter } = await import("shiki")
@@ -60,12 +80,20 @@
6080
6181
const result = {}
6282
for (const [cat, data] of Object.entries(tabData)) {
63-
result[cat] = data.raw.map(code => {
83+
result[cat] = data.raw.map(raw => {
84+
const { code, lineTypes } = extractHighlights(raw)
6485
const html = hl.codeToHtml(code, { lang: "python", theme: "github-dark" })
6586
// Strip outer <pre ...><code> wrapper — we render inside our own pre/code
66-
return html
87+
const inner = html
6788
.replace(/^<pre[^>]*><code[^>]*>/, "")
6889
.replace(/<\/code><\/pre>$/, "")
90+
// Wrap each line in a span and apply diff/highlight classes
91+
return inner.split("\n").map((line, i) => {
92+
const type = lineTypes[i]
93+
const cls = type ? ` ${type}` : ""
94+
const prefix = type === "add" ? "+" : type === "remove" ? "-" : " "
95+
return `<span class="line${cls}"><span class="diff-sign">${prefix}</span>${line}</span>`
96+
}).join("\n")
6997
})
7098
}
7199
highlighted.value = result
@@ -111,6 +139,18 @@
111139
}, 150)
112140
}
113141
142+
// ── Copy to clipboard ───────────────────────────────────────────────────────
143+
const copied = ref(false)
144+
145+
function copyCode() {
146+
const raw = currentTabData.value.raw[activeFileIndex.value]
147+
const { code } = extractHighlights(raw)
148+
navigator.clipboard.writeText(code).then(() => {
149+
copied.value = true
150+
setTimeout(() => { copied.value = false }, 2000)
151+
})
152+
}
153+
114154
// ── Synchronized horizontal scroll (file tabs ↔ code body) ─────────────────
115155
const fileTabsRef = ref(null)
116156
const codeBodyRef = ref(null)
@@ -164,7 +204,7 @@
164204
<div class="flex flex-col sm:flex-row gap-4">
165205
<a href="/docs/getting-started" class="bg-brand-teal text-white dark:text-black px-8 py-4 rounded font-label-md font-bold flex items-center justify-center gap-2 transition-all hover:brightness-110 shadow-lg shadow-brand-teal/20 active:scale-[0.98]">
166206
Initialize Project
167-
<Terminal :size="18" />
207+
<Terminal :size="18"/>
168208
</a>
169209
</div>
170210
@@ -223,12 +263,15 @@
223263
:class="activeFileIndex === idx ? 'bg-brand-teal/10 border-b-2 border-brand-teal' : 'opacity-50 hover:opacity-75'"
224264
@click="switchFile(idx)"
225265
>
226-
<FileText :size="12" :class="activeFileIndex === idx ? 'text-brand-teal' : 'text-outline-variant'" />
266+
<FileText :size="12" :class="activeFileIndex === idx ? 'text-brand-teal' : 'text-outline-variant'"/>
227267
<span class="text-xs font-mono tracking-tight" :class="activeFileIndex === idx ? 'text-white' : 'text-outline-variant'">{{ file }}</span>
228268
</button>
229269
</div>
230270
</div>
231-
<Copy :size="14" class="text-outline-variant hover:text-brand-teal cursor-pointer transition-colors" />
271+
<button @click="copyCode" class="shrink-0 transition-colors" :title="copied ? 'Copied!' : 'Copy code'">
272+
<Copy v-if="!copied" :size="14" class="text-outline-variant hover:text-brand-teal"/>
273+
<span v-else class="text-[11px] text-brand-teal font-mono">copied!</span>
274+
</button>
232275
</div>
233276
234277
<!-- Code body -->
@@ -251,4 +294,47 @@
251294
pre, code {
252295
background: transparent !important;
253296
}
297+
298+
/* Diff / highlight line decorations */
299+
:deep(.line) {
300+
display: inline-block;
301+
width: 100%;
302+
}
303+
304+
:deep(.diff-sign) {
305+
user-select: none;
306+
margin-right: 6px;
307+
opacity: 0.6;
308+
}
309+
310+
:deep(.line.highlight) {
311+
background-color: rgba(5, 150, 105, 0.15);
312+
border-left: 2px solid #059669;
313+
padding-left: 6px;
314+
margin-left: -8px;
315+
}
316+
317+
:deep(.line.add) {
318+
background-color: rgba(5, 150, 105, 0.15);
319+
border-left: 2px solid #059669;
320+
padding-left: 6px;
321+
margin-left: -8px;
322+
}
323+
324+
:deep(.line.add .diff-sign) {
325+
color: #059669;
326+
opacity: 1;
327+
}
328+
329+
:deep(.line.remove) {
330+
background-color: rgba(239, 68, 68, 0.1);
331+
border-left: 2px solid #ef4444;
332+
padding-left: 6px;
333+
margin-left: -8px;
334+
}
335+
336+
:deep(.line.remove .diff-sign) {
337+
color: #ef4444;
338+
opacity: 1;
339+
}
254340
</style>
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
# bootstrap/application.py
22
from pathlib import Path
33
from fastapi_startkit import Application
4-
from fastapi_startkit.fastapi import FastAPIProvider
54
from fastapi_startkit.logging import LoggerProvider
65

7-
# Initialize Application with logging and fastapi
6+
# A lightweight background worker with zero web overhead,
7+
# No FastAPI, no database ORMs, no frontend assets—just pure, lean Python.
8+
# Automatically loads .env files and exposes a structured CLI engine.
89
app: Application = Application(
910
base_path=Path(__file__).parent.parent,
1011
providers=[
12+
# Configures uniform logs across terminal, files, syslog, or Slack
1113
LoggerProvider,
12-
FastAPIProvider
1314
]
1415
)

.vitepress/theme/snippets/artisan

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,10 @@ import sys
44
from bootstrap.application import app
55

66
if __name__ == "__main__":
7+
# Your console scripts now can access a centralized, single source of truth
8+
# for environment variables and structured logging.
79
status = app.handle_command()
810
sys.exit(status if isinstance(status, int) else 0)
11+
12+
# Save as `artisan` -> Run via `uv run python artisan`
13+
# Build background workers, cron jobs, or database utilities uniformly.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# bootstrap/application.py
2+
from fastapi_startkit import Application
3+
from fastapi_startkit.fastapi import FastAPIProvider
4+
from fastapi_startkit.logging import LoggerProvider
5+
from fastapi_startkit.masoniteorm import DatabaseProvider # [+]
6+
from pathlib import Path
7+
# FastAPI Startkit ships with a fully async MasoniteORM provider.
8+
# Use it for an elegant, Active Record workflow—or completely swap it
9+
# for SQLAlchemy, SQLModel, or any async database stack you prefer.
10+
app: Application = Application(
11+
base_path=Path(__file__).parent.parent,
12+
providers=[
13+
LoggerProvider,
14+
FastAPIProvider,
15+
DatabaseProvider # [+]
16+
]
17+
)

.vitepress/theme/snippets/database_migrations.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from fastapi_startkit.masoniteorm import Migration
22

3-
3+
# Explicit schema design: MasoniteORM requires you to write your
4+
# migrations manually rather than auto-generating them from
5+
# code models (like Django or Alembic do).
46
class CreateUsersTable(Migration):
57
async def up(self):
68
async with await self.schema.create("users") as table:
@@ -12,3 +14,5 @@ async def up(self):
1214

1315
async def down(self):
1416
await self.schema.drop("users")
17+
18+
# Run `uv run python artisan db:migrate` to apply this migration.

.vitepress/theme/snippets/database_models.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,18 @@ class User(Model):
44
id: int
55
name: str
66
email: str
7+
password: str
78

9+
# Note: Showing a HasMany relationship example (Post schema not pictured).
810
posts: list["Post"] = HasMany("Post")
911

1012
class Post(Model):
1113
id: int
1214
user_id: int
1315
title: str
1416
content: str
17+
18+
# Then you can use the models in your application:
19+
# from app.models import User, Post
20+
# user = await User.find(1)
21+
# user_with_posts = await User.with_('posts').find(1)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# bootstrap/application.py
2+
from fastapi_startkit import Application
3+
from fastapi_startkit.fastapi import FastAPIProvider # [+]
4+
from fastapi_startkit.logging import LoggerProvider
5+
from pathlib import Path
6+
7+
app: Application = Application(
8+
base_path=Path(__file__).parent.parent,
9+
providers=[
10+
LoggerProvider,
11+
# Register FastAPIProvider to bind the fastapi to the application,
12+
FastAPIProvider # [+]
13+
]
14+
)
15+
16+
# Bind your routes to fastapi.
17+
from routes.api import router
18+
app.include_router(router)
19+
# `uv run python artisan serve` to serve the fastapi application
Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
# routes/api.py
2+
3+
# Define your FastAPI routes as it is using fastapi's APIRouter
24
from fastapi import APIRouter
35

46
router = APIRouter()
57

68
@router.get("/")
79
async def index():
810
return {"message": "Hello, FastAPI!"}
9-
10-
@router.post("/items")
11-
async def create_item(item: ItemSchema):
12-
return item

.vitepress/theme/snippets/fastapi_routes.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# routes/web.py
22
from fastapi_startkit.fastapi import Router
33

4+
# Or, switch to an opinionated routing architecture for better organization
45
router = Router()
56

67
from app.http.controllers import users_controller

.vitepress/theme/snippets/fastapi_users_controller.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# app/http/controllers/users_controller.py
2+
# NOTE: Database provider needs to register to run this code
23
async def index(request: Request):
34
return await User.all()
45

0 commit comments

Comments
 (0)