Christian Marian - Work Dell преди 2 седмици
родител
ревизия
a85cff2884

+ 113 - 0
cmds/webservice/bin/templates/design.tmpl

@@ -0,0 +1,113 @@
+{{showFile "templates/design/sections/head.tmpl"}}
+<body class="bg-zinc-950 text-zinc-200 h-screen overflow-hidden flex">
+{{showFile "templates/design/sections/nav_small.tmpl"}}
+{{showFile "templates/design/sections/nav_big.tmpl"}}
+    
+
+    <main class="flex-1 flex flex-col relative bg-zinc-900/20 overflow-auto">
+        {{showFile "templates/design/sections/nav_header.tmpl"}}
+
+        <div class="flex-1 flex">
+            <section class="flex-1 overflow-y-auto custom-scrollbar p-12">
+    <div class="w-full grid grid-cols-2 gap-0 border border-zinc-700">
+       
+
+        <div class="h-[85vh] bg-[#446454]">
+            <textarea 
+                id="editor" 
+                placeholder="Start typing..."
+                class="p-6 w-full h-full bg-transparent text-zinc-100 text-[14px] focus:outline-none resize-none custom-scrollbar"
+            ></textarea>
+        </div>
+        <div id="preview" class="border-r border-zinc-700 h-[85vh] bg-[#454644] p-2 text-zinc-100 overflow-y-auto font-sans prose prose-invert prose-zinc max-w-none
+    /* --- Code Blocks (Pre) --- */
+    [&_pre]:bg-zinc-900/80 [&_pre]:p-4 [&_pre]:rounded-lg [&_pre]:border [&_pre]:border-zinc-700 
+    [&_pre]:my-6 [&_pre]:overflow-x-auto [&_pre]:shadow-inner
+    
+    /* --- Inline Code (<code>) --- */
+    [&_code]:font-mono [&_code]:text-indigo-300 [&_code]:bg-zinc-800/50 
+    [&_code]:px-1.5 [&_code]:py-0.5 [&_code]:rounded [&_code]:text-[0.9em]
+    /* Evităm background-ul dublu când codul este în interiorul unui <pre> */
+    [&_pre_code]:bg-transparent [&_pre_code]:p-0 [&_pre_code]:text-zinc-200 [&_pre_code]:text-[0.85em]
+
+    /* --- Stiluri Blockquote --- */
+    [&_blockquote]:border-l-4 [&_blockquote]:border-indigo-500 [&_blockquote]:bg-zinc-800/30 
+    [&_blockquote]:py-2 [&_blockquote]:px-5 [&_blockquote]:my-4 [&_blockquote]:italic [&_blockquote]:text-zinc-300
+    
+    /* --- Stiluri Bold & Italic --- */
+    [&_strong]:text-white [&_strong]:font-bold
+    [&_em]:text-indigo-200 [&_em]:italic
+
+    /* --- Stiluri pentru Tabele --- */
+    [&_table]:w-full [&_table]:my-6 [&_table]:border-collapse [&_table]:border [&_table]:border-zinc-600
+    [&_thead]:bg-zinc-800/50
+    [&_th]:border [&_th]:border-zinc-600 [&_th]:p-3 [&_th]:text-left [&_th]:text-indigo-300 [&_th]:uppercase [&_th]:text-[11px]
+    [&_td]:border [&_td]:border-zinc-600 [&_td]:p-3 [&_td]:text-sm
+    [&_tr:nth-child(even)]:bg-zinc-700/30
+
+    /* --- Stiluri pentru Liste & Headings --- */
+    [&_ul]:list-disc [&_ul]:ml-6 [&_ol]:list-decimal [&_ol]:ml-6
+    [&_h1]:text-3xl [&_h1]:font-bold [&_h1]:border-b [&_h1]:border-zinc-600 [&_h1]:pb-2 [&_h1]:mb-4
+    [&_h2]:text-xl [&_h2]:font-semibold [&_h2]:text-indigo-400 [&_h2]:mt-6 custom-scrollbar">
+            Your preview will appear here...
+        </div>
+    </div>
+</section>
+            
+            
+        </div>
+    </main>
+
+<script>
+    const editor = document.getElementById('editor');
+const preview = document.getElementById('preview');
+
+// Debounce helper
+function debounce(func, delay) {
+    let timeout;
+    return function(...args) {
+        clearTimeout(timeout);
+        timeout = setTimeout(() => func.apply(this, args), delay);
+    };
+}
+
+// The API Call
+const updatePreview = async () => {
+    const content = editor.value;
+    
+    try {
+        const response = await fetch('/live_preview/', {
+            method: 'POST',
+            headers: { 'Content-Type': 'application/json' },
+            body: JSON.stringify({ markdown: content })
+        });
+
+        if (response.ok) {
+            const htmlResult = await response.text();
+            preview.innerHTML = htmlResult; 
+
+            // 1. Aplică etichetele de limbaj (codul de mai devreme)
+            preview.querySelectorAll('pre code').forEach((codeBlock) => {
+                const pre = codeBlock.parentElement;
+                const langClass = Array.from(codeBlock.classList).find(c => c.startsWith('language-'));
+                if (langClass) {
+                    pre.setAttribute('data-language', langClass.replace('language-', ''));
+                }
+
+                // 2. Declanșează evidențierea sintaxei
+                hljs.highlightElement(codeBlock);
+            });
+
+            console.log("Remote Preview & Highlighting Updated!");
+        }
+    } catch (error) {
+        console.error("Error:", error);
+    }
+};
+
+// Listen for input, trigger every 500ms of "pause"
+editor.addEventListener('input', debounce(updatePreview, 500));
+</script>
+
+</body>
+</html>

+ 36 - 0
cmds/webservice/bin/templates/design/sections/head.tmpl

@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html lang="en" class="dark">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Obsidian Admin Interface</title>
+    <script src="https://cdn.tailwindcss.com"></script>
+    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css">
+    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet">
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
+    <style>
+    body { font-family: 'Inter', sans-serif; }
+
+    /* 1. Fundalul scrollbar-ului rămâne mereu transparent */
+    .custom-scrollbar::-webkit-scrollbar {
+        width: 4px; /* Am mărit puțin la 4px pentru a fi utilizabil, dar discret */
+    }
+
+    /* 2. Thumb-ul este invizibil implicit (sau foarte transparent) */
+    .custom-scrollbar::-webkit-scrollbar-thumb {
+        background: transparent;
+        border-radius: 10px;
+        transition: background 0.3s ease;
+    }
+
+    /* 3. Thumb-ul devine vizibil doar când facem HOVER pe containerul părinte */
+    .custom-scrollbar:hover::-webkit-scrollbar-thumb {
+        background: #52525b; /* O nuanță de zinc/gri medie */
+    }
+
+    /* 4. Opțional: Culoare mai intensă când se dă click pe scrollbar */
+    .custom-scrollbar::-webkit-scrollbar-thumb:hover {
+        background: #71717a; 
+    }
+</style>
+</head>

+ 19 - 0
cmds/webservice/bin/templates/design/sections/nav_big.tmpl

@@ -0,0 +1,19 @@
+<aside class="w-64 border-r border-zinc-800 bg-zinc-900/50 flex flex-col">
+        <div class="p-4 flex justify-between items-center border-bottom border-zinc-800">
+            <span class="text-xs font-bold uppercase tracking-widest text-zinc-500">Main Vault</span>
+            <span class="text-[10px] px-1.5 py-0.5 rounded bg-zinc-800 text-zinc-400 border border-zinc-700">v2.4</span>
+        </div>
+        <div class="flex-1 overflow-y-auto custom-scrollbar p-2 space-y-1">
+            <details open class="group">
+                <summary class="flex items-center p-2 text-sm hover:bg-zinc-800 rounded cursor-pointer list-none text-zinc-400 group-open:text-zinc-100">
+                    <span class="mr-2 transform group-open:rotate-90 transition-transform">▸</span> Projects
+                </summary>
+                <div class="pl-6 space-y-1 mt-1 border-l border-zinc-800 ml-3">
+                    <div class="p-2 text-xs bg-indigo-500/10 text-indigo-400 rounded border border-indigo-500/20">System_Architecture.md</div>
+                    <div class="p-2 text-xs hover:bg-zinc-800 rounded text-zinc-500">Database_Schema.sql</div>
+                </div>
+            </details>
+            <div class="p-2 text-sm hover:bg-zinc-800 rounded cursor-pointer text-zinc-400">Daily_Notes</div>
+            <div class="p-2 text-sm hover:bg-zinc-800 rounded cursor-pointer text-zinc-400">Templates</div>
+        </div>
+    </aside>

+ 13 - 0
cmds/webservice/bin/templates/design/sections/nav_header.tmpl

@@ -0,0 +1,13 @@
+<header class="h-12 border-b border-zinc-800 flex items-center justify-between px-6 bg-zinc-900/30">
+            <div class="flex items-center text-xs text-zinc-500 gap-2">
+                <span>Vault</span> <span class="text-zinc-700">/</span> 
+                <span>Projects</span> <span class="text-zinc-700">/</span> 
+                <span class="text-zinc-200 font-medium">System_Architecture.md</span>
+            </div>
+            <div class="flex items-center gap-4">
+                <span class="flex items-center gap-1.5 text-[10px] text-emerald-500 bg-emerald-500/10 px-2 py-0.5 rounded-full border border-emerald-500/20">
+                    <span class="w-1.5 h-1.5 bg-emerald-500 rounded-full animate-pulse"></span> Fully Synced
+                </span>
+                <button class="text-zinc-400 hover:text-white text-sm">Edit</button>
+            </div>
+        </header>

+ 16 - 0
cmds/webservice/bin/templates/design/sections/nav_right.tmpl

@@ -0,0 +1,16 @@
+<aside class="w-72 border-l border-zinc-800 bg-zinc-950/50 hidden xl:flex flex-col">
+                <div class="p-4 border-b border-zinc-800">
+                    <h3 class="text-xs font-bold text-zinc-500 uppercase">Backlinks</h3>
+                </div>
+                <div class="p-4 space-y-4">
+                    <div class="p-3 bg-zinc-900 border border-zinc-800 rounded text-xs">
+                        <div class="text-zinc-300 font-medium mb-1">Project_Overview.md</div>
+                        <div class="text-zinc-500 italic">"...refer to the system architecture design for more..."</div>
+                    </div>
+                </div>
+                <div class="mt-auto p-4 border-t border-zinc-800">
+                    <div class="h-32 w-full bg-zinc-900 rounded border border-zinc-800 flex items-center justify-center text-[10px] text-zinc-600">
+                        Graph View Placeholder
+                    </div>
+                </div>
+            </aside>

+ 12 - 0
cmds/webservice/bin/templates/design/sections/nav_small.tmpl

@@ -0,0 +1,12 @@
+ <aside class="w-14 border-r border-zinc-800 flex flex-col items-center py-4 gap-6 bg-zinc-950">
+        <div class="p-2 bg-indigo-600 rounded-lg shadow-lg shadow-indigo-500/20">
+            <svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/></aside>
+        </div>
+        <nav class="flex flex-col gap-4 text-zinc-500">
+            <button class="hover:text-white transition-colors">📂</button>
+            <button class="hover:text-white transition-colors">🔍</button>
+            <button class="hover:text-white transition-colors">🕸️</button>
+            <div class="h-[1px] w-8 bg-zinc-800 my-2"></div>
+            <button class="hover:text-white transition-colors">⚙️</button>
+        </nav>
+    </aside>

+ 4 - 0
lib/inout/json.go

@@ -33,6 +33,10 @@ func ObjToFile(filename string, obj any, beauty bool) error {
 	return SaveObjToFile(filename, data)
 }
 
+func ByteToObj(obj any, data []byte) error {
+	return json.Unmarshal(data, obj)
+}
+
 func FileToObj(filename string, obj any) error {
 	data, err := ReadFile(filename)
 	if err != nil {

+ 32 - 2
lib/server/handlers.go

@@ -2,7 +2,9 @@ package server
 
 import (
 	"bytes"
+	"encoding/json"
 	"fmt"
+	"io"
 	"net/http"
 	"os"
 	"path/filepath"
@@ -13,6 +15,7 @@ import (
 	"git.linuxit.ro/turos.robert/mynotes/cmds/webservice/types"
 	"git.linuxit.ro/turos.robert/mynotes/lib/inout"
 	"github.com/yuin/goldmark"
+	"github.com/yuin/goldmark/extension"
 )
 
 func Wildcard(w http.ResponseWriter, r *http.Request) {
@@ -29,8 +32,12 @@ func Wildcard(w http.ResponseWriter, r *http.Request) {
 		path := filepath.Join(base_dir, endpoint)
 		_, err := os.Stat(path)
 		if err != nil {
-			w.WriteHeader(http.StatusInternalServerError)
-			w.Write(inout.FileToBytes("templates/error.tmpl"))
+			w.Header().Add("Content-Type", "text/html")
+
+			resp, code := inout.ParseTemplate("templates/design.tmpl", err)
+			w.WriteHeader(code)
+			w.Write(resp)
+			return
 		}
 		w.Write(inout.FileToBytes(path))
 		return
@@ -184,3 +191,26 @@ func EditNotes(w http.ResponseWriter, r *http.Request, filename string) {
 	w.WriteHeader(code)
 	w.Write(resp)
 }
+
+func MDToHTML(w http.ResponseWriter, r *http.Request) {
+	body, err := io.ReadAll(r.Body)
+	if err != nil {
+		fmt.Fprintf(w, "%+v", err)
+	}
+	mp := make(map[string]any)
+	err = json.Unmarshal(body, &mp)
+	if err != nil {
+		fmt.Fprintf(w, "%+v", err)
+	}
+	var buff bytes.Buffer
+	md := goldmark.New(
+		goldmark.WithExtensions(extension.Table,
+			extension.DefinitionList,
+			extension.Footnote,
+			extension.Typographer))
+	err = md.Convert([]byte(mp["markdown"].(string)), &buff)
+	if err != nil {
+		fmt.Fprintf(w, "%+v", err)
+	}
+	w.Write(buff.Bytes())
+}

+ 4 - 0
lib/server/srv.go

@@ -38,6 +38,10 @@ func (s *Server) Run() {
 		EditNotes(w, r, note)
 	})
 
+	http.HandleFunc("/live_preview/", func(w http.ResponseWriter, r *http.Request) {
+		MDToHTML(w, r)
+	})
+
 	//running server
 	fmt.Printf("Server running at http://%s:%s/\n", s.Host, s.Port)
 	err := http.ListenAndServe(fmt.Sprintf("%s:%s", s.Host, s.Port), nil)