feat(notify-bridge): phase 1 - project scaffolding

Set up the Notify Bridge project structure:
- packages/core (notify_bridge_core) with provider, model, notification, template packages
- packages/server (notify_bridge_server) with FastAPI skeleton and health endpoint
- frontend with SvelteKit 2, Svelte 5, Tailwind CSS v4, static adapter
- Root configs: .gitignore, README.md, CLAUDE.md

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-19 22:30:06 +03:00
commit b724447f4d
29 changed files with 4525 additions and 0 deletions
+4003
View File
File diff suppressed because it is too large Load Diff
+40
View File
@@ -0,0 +1,40 @@
{
"name": "notify-bridge-frontend",
"private": true,
"version": "0.1.0",
"type": "module",
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"prepare": "svelte-kit sync || echo ''",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
},
"devDependencies": {
"@sveltejs/adapter-auto": "^7.0.0",
"@sveltejs/adapter-static": "^3.0.10",
"@sveltejs/kit": "^2.50.2",
"@sveltejs/vite-plugin-svelte": "^6.2.4",
"@tailwindcss/vite": "^4.2.2",
"bits-ui": "^2.16.3",
"clsx": "^2.1.1",
"lucide-svelte": "^0.577.0",
"svelte": "^5.51.0",
"svelte-check": "^4.4.2",
"tailwind-merge": "^3.5.0",
"tailwind-variants": "^3.2.2",
"tailwindcss": "^4.2.2",
"typescript": "^5.9.3",
"vite": "^7.3.1"
},
"dependencies": {
"@codemirror/lang-html": "^6.4.11",
"@codemirror/language": "^6.12.2",
"@codemirror/state": "^6.6.0",
"@codemirror/theme-one-dark": "^6.1.3",
"@codemirror/view": "^6.40.0",
"@mdi/js": "^7.4.47",
"codemirror": "^6.0.2"
}
}
+177
View File
@@ -0,0 +1,177 @@
@import 'tailwindcss';
@theme {
--color-background: #f8f9fb;
--color-foreground: #1a1a2e;
--color-muted: #eef0f4;
--color-muted-foreground: #6b7280;
--color-border: #e2e4ea;
--color-primary: #0d9488;
--color-primary-foreground: #ffffff;
--color-accent: #eef0f4;
--color-accent-foreground: #1a1a2e;
--color-destructive: #ef4444;
--color-card: #ffffff;
--color-card-foreground: #1a1a2e;
--color-success-bg: #ecfdf5;
--color-success-fg: #059669;
--color-warning-bg: #fffbeb;
--color-warning-fg: #d97706;
--color-error-bg: #fef2f2;
--color-error-fg: #dc2626;
--color-glow: rgba(13, 148, 136, 0.15);
--color-glow-strong: rgba(13, 148, 136, 0.3);
--color-sidebar: #ffffff;
--color-sidebar-active: rgba(13, 148, 136, 0.08);
--font-sans: 'DM Sans', ui-sans-serif, system-ui, sans-serif;
--font-mono: 'JetBrains Mono', ui-monospace, monospace;
--radius: 0.625rem;
}
/* Dark theme overrides */
[data-theme="dark"] {
--color-background: #0c0e14;
--color-foreground: #e4e6ed;
--color-muted: #1a1d28;
--color-muted-foreground: #8b8fa4;
--color-border: #252836;
--color-primary: #14b8a6;
--color-primary-foreground: #0c0e14;
--color-accent: #1a1d28;
--color-accent-foreground: #e4e6ed;
--color-destructive: #f87171;
--color-card: #13151e;
--color-card-foreground: #e4e6ed;
--color-success-bg: #052e16;
--color-success-fg: #34d399;
--color-warning-bg: #422006;
--color-warning-fg: #fbbf24;
--color-error-bg: #450a0a;
--color-error-fg: #f87171;
--color-glow: rgba(20, 184, 166, 0.12);
--color-glow-strong: rgba(20, 184, 166, 0.25);
--color-sidebar: #10121a;
--color-sidebar-active: rgba(20, 184, 166, 0.1);
}
body {
font-family: var(--font-sans);
background-color: var(--color-background);
color: var(--color-foreground);
transition: background-color 0.3s ease, color 0.3s ease;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* Subtle background pattern */
body::before {
content: '';
position: fixed;
inset: 0;
z-index: -1;
opacity: 0.4;
background-image: radial-gradient(circle at 1px 1px, var(--color-border) 0.5px, transparent 0);
background-size: 32px 32px;
pointer-events: none;
}
/* Form controls */
input, select, textarea {
color: var(--color-foreground);
background-color: var(--color-background);
border-color: var(--color-border);
font-family: var(--font-sans);
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
input:focus-visible, select:focus-visible, textarea:focus-visible {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 3px var(--color-glow), 0 0 12px var(--color-glow);
}
button:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
border-radius: 0.375rem;
}
a:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
border-radius: 0.375rem;
}
/* Override browser autofill styles in dark mode */
[data-theme="dark"] input:-webkit-autofill,
[data-theme="dark"] input:-webkit-autofill:hover,
[data-theme="dark"] input:-webkit-autofill:focus,
[data-theme="dark"] select:-webkit-autofill {
-webkit-box-shadow: 0 0 0 1000px #13151e inset !important;
-webkit-text-fill-color: #e4e6ed !important;
caret-color: #e4e6ed;
}
/* Color scheme for native controls */
[data-theme="dark"] { color-scheme: dark; }
[data-theme="light"] { color-scheme: light; }
/* Scrollbar styling */
::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--color-border); border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: var(--color-muted-foreground); }
/* Animations */
@keyframes fadeSlideIn {
from { opacity: 0; transform: translateY(12px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}
@keyframes pulseGlow {
0%, 100% { box-shadow: 0 0 4px var(--color-glow); }
50% { box-shadow: 0 0 16px var(--color-glow-strong); }
}
@keyframes countUp {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
.animate-fade-slide-in {
animation: fadeSlideIn 0.4s ease-out both;
}
.animate-shimmer {
background: linear-gradient(90deg, var(--color-muted) 25%, var(--color-border) 50%, var(--color-muted) 75%);
background-size: 200% 100%;
animation: shimmer 1.5s ease-in-out infinite;
}
.animate-pulse-glow {
animation: pulseGlow 2s ease-in-out infinite;
}
.animate-count-up {
animation: countUp 0.5s ease-out both;
}
/* Stagger children utility */
.stagger-children > * {
animation: fadeSlideIn 0.4s ease-out both;
}
.stagger-children > *:nth-child(1) { animation-delay: 0ms; }
.stagger-children > *:nth-child(2) { animation-delay: 60ms; }
.stagger-children > *:nth-child(3) { animation-delay: 120ms; }
.stagger-children > *:nth-child(4) { animation-delay: 180ms; }
.stagger-children > *:nth-child(5) { animation-delay: 240ms; }
.stagger-children > *:nth-child(6) { animation-delay: 300ms; }
.font-mono {
font-family: var(--font-mono);
}
+16
View File
@@ -0,0 +1,16 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,300..700;1,9..40,300..700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet" />
<title>Notify Bridge</title>
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>
+7
View File
@@ -0,0 +1,7 @@
<script>
import '../app.css';
let { children } = $props();
</script>
{@render children()}
+9
View File
@@ -0,0 +1,9 @@
<script>
</script>
<div class="min-h-screen flex items-center justify-center">
<div class="text-center">
<h1 class="text-4xl font-bold text-foreground mb-2">Notify Bridge</h1>
<p class="text-muted-foreground">Service-to-notification bridge</p>
</div>
</div>
+18
View File
@@ -0,0 +1,18 @@
import adapter from '@sveltejs/adapter-static';
/** @type {import('@sveltejs/kit').Config} */
const config = {
kit: {
adapter: adapter({
pages: 'build',
assets: 'build',
fallback: 'index.html'
})
},
vitePlugin: {
dynamicCompileOptions: ({ filename }) =>
filename.includes('node_modules') ? undefined : { runes: true }
}
};
export default config;
+15
View File
@@ -0,0 +1,15 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"rewriteRelativeImportExtensions": true,
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"moduleResolution": "bundler"
}
}
+12
View File
@@ -0,0 +1,12 @@
import { sveltekit } from '@sveltejs/kit/vite';
import tailwindcss from '@tailwindcss/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [tailwindcss(), sveltekit()],
server: {
proxy: {
'/api': 'http://localhost:8420'
}
}
});