Initial commit.
This commit is contained in:
27
app/__init__.py
Normal file
27
app/__init__.py
Normal file
@@ -0,0 +1,27 @@
|
||||
from flask import Flask
|
||||
from .config import load_config
|
||||
|
||||
|
||||
def create_app(config_override=None) -> Flask:
|
||||
app = Flask(
|
||||
__name__,
|
||||
static_folder="static",
|
||||
template_folder="templates",
|
||||
)
|
||||
|
||||
cfg = config_override or load_config()
|
||||
# Map to Flask app.config for easy access in templates/routes
|
||||
app.config["KB_VAULT_PATH"] = cfg.VAULT_PATH
|
||||
app.config["SECRET_KEY"] = cfg.SECRET_KEY
|
||||
app.config["DEBUG"] = cfg.DEBUG
|
||||
|
||||
# Register blueprints
|
||||
from .routes.home import bp as home_bp
|
||||
app.register_blueprint(home_bp)
|
||||
|
||||
# Simple health endpoint
|
||||
@app.get("/healthz")
|
||||
def healthz():
|
||||
return {"status": "ok", "vault": app.config.get("KB_VAULT_PATH")}, 200
|
||||
|
||||
return app
|
22
app/config.py
Normal file
22
app/config.py
Normal file
@@ -0,0 +1,22 @@
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
from dotenv import load_dotenv
|
||||
|
||||
|
||||
@dataclass
|
||||
class AppConfig:
|
||||
DEBUG: bool
|
||||
SECRET_KEY: str
|
||||
VAULT_PATH: str | None
|
||||
|
||||
|
||||
def load_config(vault_override: str | None = None) -> AppConfig:
|
||||
# Load .env if present
|
||||
load_dotenv()
|
||||
|
||||
debug = os.getenv("FLASK_DEBUG", "0") in ("1", "true", "True")
|
||||
secret = os.getenv("SECRET_KEY", "dev-secret-change-me")
|
||||
vault_env = os.getenv("KB_VAULT_PATH")
|
||||
|
||||
vault = vault_override or vault_env
|
||||
return AppConfig(DEBUG=debug, SECRET_KEY=secret, VAULT_PATH=vault)
|
1
app/routes/__init__.py
Normal file
1
app/routes/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# Package marker for routes
|
10
app/routes/home.py
Normal file
10
app/routes/home.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from flask import Blueprint, current_app, render_template
|
||||
|
||||
bp = Blueprint("home", __name__)
|
||||
|
||||
|
||||
@bp.get("/")
|
||||
def home():
|
||||
vault_path = current_app.config.get("KB_VAULT_PATH")
|
||||
has_vault = bool(vault_path)
|
||||
return render_template("home.html", vault_path=vault_path, has_vault=has_vault)
|
5
app/static/css/input.css
Normal file
5
app/static/css/input.css
Normal file
@@ -0,0 +1,5 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
/* You can add custom CSS here if needed */
|
18
app/static/js/app.js
Normal file
18
app/static/js/app.js
Normal file
@@ -0,0 +1,18 @@
|
||||
(function themeInit() {
|
||||
try {
|
||||
const html = document.documentElement;
|
||||
const select = document.getElementById("theme-select");
|
||||
const saved = localStorage.getItem("theme") || "light";
|
||||
html.setAttribute("data-theme", saved);
|
||||
if (select) {
|
||||
select.value = saved;
|
||||
select.addEventListener("change", () => {
|
||||
const value = select.value;
|
||||
html.setAttribute("data-theme", value);
|
||||
localStorage.setItem("theme", value);
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
// no-op
|
||||
}
|
||||
})();
|
35
app/templates/base.html
Normal file
35
app/templates/base.html
Normal file
@@ -0,0 +1,35 @@
|
||||
<!doctype html>
|
||||
<html lang="en" data-theme="light">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>{% block title %}PKM{% endblock %}</title>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/app.css') }}" />
|
||||
<script defer src="{{ url_for('static', filename='js/app.js') }}"></script>
|
||||
</head>
|
||||
<body class="min-h-screen bg-base-200">
|
||||
<div class="navbar bg-base-100 shadow">
|
||||
<div class="flex-1">
|
||||
<a href="{{ url_for('home.home') }}" class="btn btn-ghost text-xl">PKM</a>
|
||||
</div>
|
||||
<div class="flex-none gap-2">
|
||||
<label class="label mr-2"><span class="label-text">Theme</span></label>
|
||||
<select id="theme-select" class="select select-sm select-bordered">
|
||||
<option>light</option>
|
||||
<option>dark</option>
|
||||
<option>cupcake</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<main class="container mx-auto p-4">
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
|
||||
<footer class="footer items-center p-4 bg-base-100 text-base-content border-t">
|
||||
<aside class="grid-flow-col items-center">
|
||||
<p>PKM — Flask + Tailwind + DaisyUI</p>
|
||||
</aside>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
26
app/templates/home.html
Normal file
26
app/templates/home.html
Normal file
@@ -0,0 +1,26 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}PKM — Welcome{% endblock %}
|
||||
{% block content %}
|
||||
<div class="card bg-base-100 shadow">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">Welcome</h2>
|
||||
{% if has_vault %}
|
||||
<p>Your vault path is configured:</p>
|
||||
<code class="kbd kbd-sm">{{ vault_path }}</code>
|
||||
<p class="mt-2">You're ready to proceed with indexing and note features in the next milestones.</p>
|
||||
{% else %}
|
||||
<div class="alert alert-warning">
|
||||
<span>No vault path configured.</span>
|
||||
</div>
|
||||
<p class="mt-2">
|
||||
Set KB_VAULT_PATH in your environment or run via the CLI with
|
||||
<span class="kbd kbd-sm">pkm run --vault /path/to/vault</span>
|
||||
</p>
|
||||
{% endif %}
|
||||
<div class="divider"></div>
|
||||
<p class="text-sm opacity-70">
|
||||
Tailwind + DaisyUI is active. Try switching theme in the navbar.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
Reference in New Issue
Block a user