Die Seite verlangte eine technische Weichenstellung, BEVOR man gesagt hat was man will — mit Begriffen aus dem Maschinenraum (BOX / IDE: SIMPEL / IDE: GRUENDLICH). Deshalb klebten 116 Woerter Beipackzettel unter einem einzigen Eingabefeld. Zwei unabhaengige Entscheidungen, vorher in vier sich ausschliessende Knoepfe gepresst: wer — die Box allein, oder ein Repo fuer Zed nurDenken — erst nur ein Konzept (passt zu beidem, jetzt ein Haken) Die Knoepfe nennen das ERGEBNIS statt des Werkzeugs, und der erklaerende Satz steht an der Option statt in einer Legende darunter. Neu: POST /api/ideen/einordnen — das dauerwarme Hirn liest den getippten Text und waehlt vor (gemessen ~500 ms, 5 von 5 Testfaellen richtig, inkl. "Lohnt sich...?" -> nur denken). Bewusst ein VORSCHLAG mit sichtbarem Etikett: er setzt nur die Auswahl, die ohnehin dasteht. Faellt er aus, bleibt schlicht die Voreinstellung — das Tippen wird nie blockiert, es gibt keinen Ladebalken und keine Fehlermeldung. Tiefe und Ziel erscheinen nur noch beim Repo-Weg, weil sie nur dort wirken. Ein Schalter der nichts tut ist schlimmer als keiner. Leerer Zustand zeigt drei anklickbare Beispiele statt "wirf der Box eine Idee zu". Kopfzeile nennt nur noch den Zustand — "Ideen" stand dreimal uebereinander. Die API bleibt unveraendert; die Abbildung passiert in submit(). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -31,6 +31,10 @@ class ArchivAlleIn(BaseModel):
|
|||||||
projekt: str = ""
|
projekt: str = ""
|
||||||
|
|
||||||
|
|
||||||
|
class EinordnenIn(BaseModel):
|
||||||
|
text: str
|
||||||
|
|
||||||
|
|
||||||
@router.get("/ideen")
|
@router.get("/ideen")
|
||||||
def list_queue() -> dict:
|
def list_queue() -> dict:
|
||||||
return ideen.list_queue()
|
return ideen.list_queue()
|
||||||
@@ -119,6 +123,13 @@ def archive(body: TaskIn) -> dict:
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/ideen/einordnen")
|
||||||
|
def einordnen(body: EinordnenIn) -> dict:
|
||||||
|
"""Einordnungs-VORSCHLAG beim Tippen. Wirft nie — scheitert es, kommt `ok: false`
|
||||||
|
und die Oberfläche behält ihre Voreinstellung."""
|
||||||
|
return ideen.einordnen(body.text)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/ideen/archivieren-alle")
|
@router.post("/ideen/archivieren-alle")
|
||||||
def archive_alle(body: ArchivAlleIn) -> dict:
|
def archive_alle(body: ArchivAlleIn) -> dict:
|
||||||
"""Fertige Karten sammelweise wegräumen. Fasst ausschließlich `done` an."""
|
"""Fertige Karten sammelweise wegräumen. Fasst ausschließlich `done` an."""
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ import subprocess
|
|||||||
import time
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
import httpx
|
||||||
|
from config import GATEWAY_URL, V1_UPSTREAM
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
HERMES = Path(os.environ.get("MC_HERMES_BIN", "~/.local/bin/hermes")).expanduser()
|
HERMES = Path(os.environ.get("MC_HERMES_BIN", "~/.local/bin/hermes")).expanduser()
|
||||||
@@ -509,6 +512,66 @@ def archive_task(task_id: str) -> dict:
|
|||||||
return {"ok": True}
|
return {"ok": True}
|
||||||
|
|
||||||
|
|
||||||
|
_EINORDNEN_SYS = (
|
||||||
|
"Du ordnest eine frisch getippte Projekt-Idee ein. Antworte AUSSCHLIESSLICH mit einem "
|
||||||
|
"JSON-Objekt, ohne Prosa, ohne Codezaun:\n"
|
||||||
|
'{"wer":"box"|"zed","tiefe":"schlank"|"gruendlich","nur_denken":true|false}\n\n'
|
||||||
|
"wer=box: Aufgabe am eigenen Heimlabor des Nutzers — seine Server, seine bestehenden "
|
||||||
|
"Dienste (Mission Control, Lucy, Rippy), Wartung, Doku, Automatisierung. Die Box baut selbst.\n"
|
||||||
|
"wer=zed: ein NEUES eigenstaendiges Programm/Werkzeug/App, das der Nutzer danach selbst "
|
||||||
|
"programmieren will. Er bekommt nur ein vorbereitetes Repo.\n"
|
||||||
|
"tiefe=gruendlich: gross, vage oder mit vielen offenen Entwurfsfragen. "
|
||||||
|
"tiefe=schlank: klein und klar umrissen.\n"
|
||||||
|
"nur_denken=true: der Text stellt eine FRAGE oder waegt ab (\"lohnt sich\", \"waere es sinnvoll\", "
|
||||||
|
"\"brauche ich\") statt einen Auftrag zu geben."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def einordnen(text: str) -> dict:
|
||||||
|
"""Grober Einordnungs-VORSCHLAG für eine frisch getippte Idee.
|
||||||
|
|
||||||
|
Nur eine Vorauswahl in der Oberfläche — der Mensch sieht und überschreibt sie. Deshalb
|
||||||
|
bewusst anspruchslos: ein einziger kurzer Aufruf ans dauerwarme Hirn (69,6 t/s gemessen),
|
||||||
|
Schnellspur-Marker gegen das Nachdenken, hartes Timeout. Scheitert irgendetwas, kommt
|
||||||
|
`{"ok": False}` zurück und die Oberfläche behält schlicht ihre Voreinstellung — eine
|
||||||
|
Einordnung darf das Tippen NIE blockieren oder eine Fehlermeldung erzeugen.
|
||||||
|
"""
|
||||||
|
text = (text or "").strip()
|
||||||
|
if len(text) < 12:
|
||||||
|
return {"ok": False}
|
||||||
|
ziel_url = (V1_UPSTREAM or GATEWAY_URL).rstrip("/")
|
||||||
|
marker = os.environ.get("MC_NO_THINK_MARKER", "[[MC:NO_THINK]]")
|
||||||
|
try:
|
||||||
|
r = httpx.post(f"{ziel_url}/v1/chat/completions", timeout=12.0, json={
|
||||||
|
"model": "hermes", "temperature": 0, "max_tokens": 60, "stream": False,
|
||||||
|
"messages": [
|
||||||
|
{"role": "system", "content": _EINORDNEN_SYS},
|
||||||
|
{"role": "user", "content": f"{marker} {text[:600]}"},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
r.raise_for_status()
|
||||||
|
inhalt = (r.json()["choices"][0]["message"]["content"] or "").strip()
|
||||||
|
except Exception:
|
||||||
|
log.debug("ideen: einordnen fehlgeschlagen", exc_info=True)
|
||||||
|
return {"ok": False}
|
||||||
|
|
||||||
|
# Modelle packen JSON gern in einen Codezaun oder schreiben einen Satz davor.
|
||||||
|
m = re.search(r"\{.*\}", inhalt, re.DOTALL)
|
||||||
|
if not m:
|
||||||
|
return {"ok": False}
|
||||||
|
try:
|
||||||
|
d = json.loads(m.group(0))
|
||||||
|
except Exception:
|
||||||
|
return {"ok": False}
|
||||||
|
|
||||||
|
wer = d.get("wer") if d.get("wer") in ("box", "zed") else None
|
||||||
|
tiefe = d.get("tiefe") if d.get("tiefe") in ("schlank", "gruendlich") else None
|
||||||
|
if wer is None and tiefe is None:
|
||||||
|
return {"ok": False}
|
||||||
|
return {"ok": True, "wer": wer or "zed", "tiefe": tiefe or "schlank",
|
||||||
|
"nur_denken": bool(d.get("nur_denken"))}
|
||||||
|
|
||||||
|
|
||||||
def archive_done(projekt: str = "") -> dict:
|
def archive_done(projekt: str = "") -> dict:
|
||||||
"""Alle FERTIGEN Karten wegräumen — optional nur die eines Projekts.
|
"""Alle FERTIGEN Karten wegräumen — optional nur die eines Projekts.
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import{c as A,Y as P,l as B,b as M,u as D,r as z,j as e,Z as T,e as a,$ as g,a0 as b,a1 as W,x as f,a2 as j,W as G,X as I,C as N,g as y,q as i}from"./index-DCel3VuW.js";/**
|
import{c as A,Y as P,l as B,b as M,u as D,r as z,j as e,Z as T,e as a,$ as g,a0 as b,a1 as W,x as f,a2 as j,W as G,X as I,C as N,g as y,q as i}from"./index-DI19YZ2i.js";/**
|
||||||
* @license lucide-react v0.460.0 - ISC
|
* @license lucide-react v0.460.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import{c as W,ab as H,ac as T,u as F,b as G,r as b,j as e,a3 as O,e as y,L as p,C as z,a9 as P,K as q,U as I,V,ad as K,X as E,T as C,D as M,M as R,g as $,ae as _,q as U}from"./index-DCel3VuW.js";import{S,H as Q}from"./SectionLabel-BpAZbx-8.js";/**
|
import{c as W,ab as H,ac as T,u as F,b as G,r as b,j as e,a3 as O,e as y,L as p,C as z,a9 as P,K as q,U as I,V,ad as K,X as E,T as C,D as M,M as R,g as $,ae as _,q as U}from"./index-DI19YZ2i.js";import{S,H as Q}from"./SectionLabel-DJ0JNfWI.js";/**
|
||||||
* @license lucide-react v0.460.0 - ISC
|
* @license lucide-react v0.460.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import{c as m,ai as S,r as c,j as e,aj as z,L as g,U as M,ak as C,ae as D,al as A,am as B,an as Z,e as E,ao as L,u as R,b as q,g as k,q as T}from"./index-DCel3VuW.js";import{R as j}from"./rotate-ccw-D-_Gb8HQ.js";/**
|
import{c as m,ai as S,r as c,j as e,aj as z,L as g,U as M,ak as C,ae as D,al as A,am as B,an as Z,e as E,ao as L,u as R,b as q,g as k,q as T}from"./index-DI19YZ2i.js";import{R as j}from"./rotate-ccw-CBUivZG3.js";/**
|
||||||
* @license lucide-react v0.460.0 - ISC
|
* @license lucide-react v0.460.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import{c as v,r as c,I,J as A,j as e,K as h,x as B,B as H,D as E,M as z,e as M,L as T,N as F,C as K}from"./index-DCel3VuW.js";import{F as O}from"./folder-open-wGxbAB4V.js";import{C as R}from"./circle-x-BPOmowbC.js";import{C as W}from"./copy-BMmvuJ2J.js";/**
|
import{c as v,r as c,I,J as A,j as e,K as h,x as B,B as H,D as E,M as z,e as M,L as T,N as F,C as K}from"./index-DI19YZ2i.js";import{F as O}from"./folder-open-DKNPWUub.js";import{C as R}from"./circle-x-5Vi9Kek6.js";import{C as W}from"./copy-CsXXFqkj.js";/**
|
||||||
* @license lucide-react v0.460.0 - ISC
|
* @license lucide-react v0.460.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import{c as h,ap as b,r as i,j as e,L as g,aq as f,a2 as j,U as k,e as o,D as N,M as v,N as w,g as y}from"./index-DCel3VuW.js";import{B as S}from"./book-open-Dl0AmNw9.js";import{C as z}from"./circle-x-BPOmowbC.js";/**
|
import{c as h,ap as b,r as i,j as e,L as g,aq as f,a2 as j,U as k,e as o,D as N,M as v,N as w,g as y}from"./index-DI19YZ2i.js";import{B as S}from"./book-open-CqxVgaj6.js";import{C as z}from"./circle-x-5Vi9Kek6.js";/**
|
||||||
* @license lucide-react v0.460.0 - ISC
|
* @license lucide-react v0.460.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import{c as p,j as e,r as M,x as C,a6 as z,a7 as k,a8 as y,a0 as v,a9 as L,W as G,aa as T,a2 as D,e as f,U as B}from"./index-DCel3VuW.js";import{B as w}from"./book-open-Dl0AmNw9.js";import{a as N,L as P,C as E,Z as R}from"./zap-BzgvZa8u.js";import{L as I}from"./layers-BNyHHdLg.js";/**
|
import{c as p,j as e,r as M,x as C,a6 as z,a7 as k,a8 as y,a0 as v,a9 as L,W as G,aa as T,a2 as D,e as f,U as B}from"./index-DI19YZ2i.js";import{B as w}from"./book-open-CqxVgaj6.js";import{a as N,L as P,C as E,Z as R}from"./zap-CVKHAOl9.js";import{L as I}from"./layers-DQdxWFgI.js";/**
|
||||||
* @license lucide-react v0.460.0 - ISC
|
* @license lucide-react v0.460.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
-30
File diff suppressed because one or more lines are too long
+30
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -1 +1 @@
|
|||||||
import{Y as b,u as f,$ as h,r as d,j as e,e as c,Z as g,T as p,a3 as j,a4 as N,g as v,a5 as w,q as x}from"./index-DCel3VuW.js";function y(){const{data:s}=b(),m=f(),r=s!=null&&s.box_console_url?h(s.box_console_url):void 0,a=s==null?void 0:s.box_console_reachable,[l,o]=d.useState(!1),[i,n]=d.useState("");async function u(){o(!0),n("");try{const t=await v("/api/maintenance/restart",{method:"POST",body:JSON.stringify({service:"box-console"})});n(t.ok?"Neu gestartet — einen Moment, dann lädt das Terminal.":`Fehlgeschlagen: ${t.err||"Unbekannter Fehler"}`),w(m,x.agentStatus,x.services)}catch(t){n(`Fehlgeschlagen: ${(t==null?void 0:t.message)||t}`)}finally{o(!1)}}return e.jsxs("div",{className:"flex h-full flex-col gap-4",children:[e.jsxs("div",{className:"flex flex-wrap items-end justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"bg-gradient-to-r from-foreground via-foreground to-primary bg-clip-text font-space text-2xl font-bold tracking-tight text-transparent",children:"Konsole"}),e.jsx("p",{className:"flex items-center gap-2 text-sm text-muted-foreground",children:"Direkte Shell auf der Box — wie ein SSH-Fenster, mitten im Browser."})]}),e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsxs("span",{className:"flex items-center gap-1.5 text-[11px] font-medium text-muted-foreground",children:[e.jsx("span",{className:c("h-2 w-2 rounded-full",a?"bg-emerald-500 animate-pulse":"bg-amber-500")}),a?"online":"offline"]}),r&&e.jsxs("a",{href:r,target:"_blank",rel:"noopener",className:"flex h-8 items-center gap-1.5 rounded-lg border border-border/60 bg-background/20 px-3 text-xs font-semibold text-muted-foreground transition-all hover:border-primary/50 hover:text-foreground",children:[e.jsx(g,{className:"h-3.5 w-3.5"})," In neuem Tab"]})]})]}),r?e.jsxs("div",{className:"relative min-h-[58vh] flex-1 overflow-hidden rounded-2xl border border-border/60 bg-black/50 shadow-lg shadow-black/25",children:[a===!1&&e.jsxs("div",{className:"absolute inset-0 z-10 flex flex-col items-center justify-center gap-3 bg-black/70 text-center",children:[e.jsx(p,{className:"h-8 w-8 text-amber-400"}),e.jsx("div",{className:"text-sm font-semibold text-amber-300",children:"Konsole nicht erreichbar"}),e.jsxs("p",{className:"max-w-sm text-[11px] leading-normal text-muted-foreground",children:["Der Terminal-Dienst (",e.jsx("code",{className:"font-mono text-primary",children:"box-console"}),") läuft gerade nicht."]}),e.jsxs("button",{onClick:u,disabled:l,className:"flex h-9 items-center gap-1.5 rounded-lg border border-amber-500/40 bg-amber-500/10 px-4 text-[11px] font-bold uppercase tracking-wide text-amber-300 transition-all hover:bg-amber-500/20 cursor-pointer disabled:opacity-50",children:[e.jsx(j,{className:c("h-3.5 w-3.5",l&&"animate-spin")})," Dienst neu starten"]}),i&&e.jsx("p",{className:"max-w-sm text-[11px] text-muted-foreground",children:i})]}),e.jsx("iframe",{src:r,title:"Box-Konsole",className:"h-full w-full border-0",style:{minHeight:"58vh"}})]}):e.jsxs("div",{className:"flex min-h-[58vh] flex-1 items-center justify-center rounded-2xl border border-border/60 bg-background/20 text-xs text-muted-foreground",children:[e.jsx(N,{className:"mr-2 h-4 w-4"})," Lade Konsole…"]})]})}export{y as KonsoleView};
|
import{Y as b,u as f,$ as h,r as d,j as e,e as c,Z as g,T as p,a3 as j,a4 as N,g as v,a5 as w,q as x}from"./index-DI19YZ2i.js";function y(){const{data:s}=b(),m=f(),r=s!=null&&s.box_console_url?h(s.box_console_url):void 0,a=s==null?void 0:s.box_console_reachable,[l,o]=d.useState(!1),[i,n]=d.useState("");async function u(){o(!0),n("");try{const t=await v("/api/maintenance/restart",{method:"POST",body:JSON.stringify({service:"box-console"})});n(t.ok?"Neu gestartet — einen Moment, dann lädt das Terminal.":`Fehlgeschlagen: ${t.err||"Unbekannter Fehler"}`),w(m,x.agentStatus,x.services)}catch(t){n(`Fehlgeschlagen: ${(t==null?void 0:t.message)||t}`)}finally{o(!1)}}return e.jsxs("div",{className:"flex h-full flex-col gap-4",children:[e.jsxs("div",{className:"flex flex-wrap items-end justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"bg-gradient-to-r from-foreground via-foreground to-primary bg-clip-text font-space text-2xl font-bold tracking-tight text-transparent",children:"Konsole"}),e.jsx("p",{className:"flex items-center gap-2 text-sm text-muted-foreground",children:"Direkte Shell auf der Box — wie ein SSH-Fenster, mitten im Browser."})]}),e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsxs("span",{className:"flex items-center gap-1.5 text-[11px] font-medium text-muted-foreground",children:[e.jsx("span",{className:c("h-2 w-2 rounded-full",a?"bg-emerald-500 animate-pulse":"bg-amber-500")}),a?"online":"offline"]}),r&&e.jsxs("a",{href:r,target:"_blank",rel:"noopener",className:"flex h-8 items-center gap-1.5 rounded-lg border border-border/60 bg-background/20 px-3 text-xs font-semibold text-muted-foreground transition-all hover:border-primary/50 hover:text-foreground",children:[e.jsx(g,{className:"h-3.5 w-3.5"})," In neuem Tab"]})]})]}),r?e.jsxs("div",{className:"relative min-h-[58vh] flex-1 overflow-hidden rounded-2xl border border-border/60 bg-black/50 shadow-lg shadow-black/25",children:[a===!1&&e.jsxs("div",{className:"absolute inset-0 z-10 flex flex-col items-center justify-center gap-3 bg-black/70 text-center",children:[e.jsx(p,{className:"h-8 w-8 text-amber-400"}),e.jsx("div",{className:"text-sm font-semibold text-amber-300",children:"Konsole nicht erreichbar"}),e.jsxs("p",{className:"max-w-sm text-[11px] leading-normal text-muted-foreground",children:["Der Terminal-Dienst (",e.jsx("code",{className:"font-mono text-primary",children:"box-console"}),") läuft gerade nicht."]}),e.jsxs("button",{onClick:u,disabled:l,className:"flex h-9 items-center gap-1.5 rounded-lg border border-amber-500/40 bg-amber-500/10 px-4 text-[11px] font-bold uppercase tracking-wide text-amber-300 transition-all hover:bg-amber-500/20 cursor-pointer disabled:opacity-50",children:[e.jsx(j,{className:c("h-3.5 w-3.5",l&&"animate-spin")})," Dienst neu starten"]}),i&&e.jsx("p",{className:"max-w-sm text-[11px] text-muted-foreground",children:i})]}),e.jsx("iframe",{src:r,title:"Box-Konsole",className:"h-full w-full border-0",style:{minHeight:"58vh"}})]}):e.jsxs("div",{className:"flex min-h-[58vh] flex-1 items-center justify-center rounded-2xl border border-border/60 bg-background/20 text-xs text-muted-foreground",children:[e.jsx(N,{className:"mr-2 h-4 w-4"})," Lade Konsole…"]})]})}export{y as KonsoleView};
|
||||||
+3
-3
@@ -1,5 +1,5 @@
|
|||||||
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/GraphView-4NidXPBK.js","assets/index-DCel3VuW.js","assets/index-DJ-m2OOM.css"])))=>i.map(i=>d[i]);
|
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/GraphView-JdSE2NmA.js","assets/index-DI19YZ2i.js","assets/index-DLBMFIff.css"])))=>i.map(i=>d[i]);
|
||||||
var je=Object.defineProperty;var ke=(d,i,c)=>i in d?je(d,i,{enumerable:!0,configurable:!0,writable:!0,value:c}):d[i]=c;var J=(d,i,c)=>ke(d,typeof i!="symbol"?i+"":i,c);import{c as h,r as s,j as e,u as ve,b as Ne,O as W,Q as Se,C as X,R as Ce,S as Ee,P as Me,U as O,e as x,V as ze,W as De,X as Z,A as Y,_ as Oe,g as j}from"./index-DCel3VuW.js";import{C as ee}from"./copy-BMmvuJ2J.js";import{S as _e}from"./send-CgygdWYO.js";import{B as Ge}from"./book-open-Dl0AmNw9.js";/**
|
var je=Object.defineProperty;var ke=(d,i,c)=>i in d?je(d,i,{enumerable:!0,configurable:!0,writable:!0,value:c}):d[i]=c;var J=(d,i,c)=>ke(d,typeof i!="symbol"?i+"":i,c);import{c as h,r as s,j as e,u as ve,b as Ne,O as W,Q as Se,C as X,R as Ce,S as Ee,P as Me,U as O,e as x,V as ze,W as De,X as Z,A as Y,_ as Oe,g as j}from"./index-DI19YZ2i.js";import{C as ee}from"./copy-CsXXFqkj.js";import{S as _e}from"./send-CpIJWZ5L.js";import{B as Ge}from"./book-open-CqxVgaj6.js";/**
|
||||||
* @license lucide-react v0.460.0 - ISC
|
* @license lucide-react v0.460.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
@@ -34,7 +34,7 @@ var je=Object.defineProperty;var ke=(d,i,c)=>i in d?je(d,i,{enumerable:!0,config
|
|||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
* See the LICENSE file in the root directory of this source tree.
|
* See the LICENSE file in the root directory of this source tree.
|
||||||
*/const Pe=h("User",[["path",{d:"M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2",key:"975kel"}],["circle",{cx:"12",cy:"7",r:"4",key:"17ys0d"}]]);class te extends s.Component{constructor(){super(...arguments);J(this,"state",{error:null})}static getDerivedStateFromError(c){return{error:c}}render(){return this.state.error?e.jsxs("div",{className:"h-[480px] rounded-2xl border border-red-500/20 bg-red-500/5 p-6 text-xs text-red-400 overflow-auto",children:[e.jsx("div",{className:"font-semibold mb-2",children:"Graph konnte nicht gerendert werden"}),e.jsx("pre",{className:"whitespace-pre-wrap break-words text-[11px] leading-relaxed",children:this.state.error.message})]}):this.props.children}}const se=s.lazy(()=>Oe(()=>import("./GraphView-4NidXPBK.js"),__vite__mapDeps([0,1,2])).then(d=>({default:d.GraphView}))),k=["identity","knowledge","rules","events"],v=new Set(["auto","agent","hermes"]),N={identity:{label:"Identität",icon:Pe,bg:"bg-cyan-500/10",text:"text-cyan-400"},knowledge:{label:"Wissen",icon:De,bg:"bg-indigo-500/10",text:"text-indigo-400"},rules:{label:"Regeln",icon:Le,bg:"bg-violet-500/10",text:"text-violet-400"},events:{label:"Ereignisse",icon:ze,bg:"bg-amber-500/10",text:"text-amber-400"}},ne={label:"Gedächtnis",icon:Ge,text:"text-muted-foreground"},Re={identity:"border-l-cyan-500/80",knowledge:"border-l-indigo-500/80",rules:"border-l-violet-500/80",events:"border-l-amber-500/80"},_={identity:"#00f5ff",knowledge:"#3b82f6",rules:"#d946ef",events:"#fbbf24"},ae=`Hi Hermes! Lass uns ein kurzes Onboarding machen, damit du dich künftig an mich erinnerst. Stell mir nacheinander ein paar kurze Fragen zu:
|
*/const Pe=h("User",[["path",{d:"M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2",key:"975kel"}],["circle",{cx:"12",cy:"7",r:"4",key:"17ys0d"}]]);class te extends s.Component{constructor(){super(...arguments);J(this,"state",{error:null})}static getDerivedStateFromError(c){return{error:c}}render(){return this.state.error?e.jsxs("div",{className:"h-[480px] rounded-2xl border border-red-500/20 bg-red-500/5 p-6 text-xs text-red-400 overflow-auto",children:[e.jsx("div",{className:"font-semibold mb-2",children:"Graph konnte nicht gerendert werden"}),e.jsx("pre",{className:"whitespace-pre-wrap break-words text-[11px] leading-relaxed",children:this.state.error.message})]}):this.props.children}}const se=s.lazy(()=>Oe(()=>import("./GraphView-JdSE2NmA.js"),__vite__mapDeps([0,1,2])).then(d=>({default:d.GraphView}))),k=["identity","knowledge","rules","events"],v=new Set(["auto","agent","hermes"]),N={identity:{label:"Identität",icon:Pe,bg:"bg-cyan-500/10",text:"text-cyan-400"},knowledge:{label:"Wissen",icon:De,bg:"bg-indigo-500/10",text:"text-indigo-400"},rules:{label:"Regeln",icon:Le,bg:"bg-violet-500/10",text:"text-violet-400"},events:{label:"Ereignisse",icon:ze,bg:"bg-amber-500/10",text:"text-amber-400"}},ne={label:"Gedächtnis",icon:Ge,text:"text-muted-foreground"},Re={identity:"border-l-cyan-500/80",knowledge:"border-l-indigo-500/80",rules:"border-l-violet-500/80",events:"border-l-amber-500/80"},_={identity:"#00f5ff",knowledge:"#3b82f6",rules:"#d946ef",events:"#fbbf24"},ae=`Hi Hermes! Lass uns ein kurzes Onboarding machen, damit du dich künftig an mich erinnerst. Stell mir nacheinander ein paar kurze Fragen zu:
|
||||||
1) wer ich bin und woran ich gerade arbeite,
|
1) wer ich bin und woran ich gerade arbeite,
|
||||||
2) wie ich angesprochen werden möchte,
|
2) wie ich angesprochen werden möchte,
|
||||||
3) meine bevorzugten Tools, Sprachen und Arbeitsweise,
|
3) meine bevorzugten Tools, Sprachen und Arbeitsweise,
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import{c as q,u as oe,a as Fe,b as ge,j as e,f as ue,d as Re,e as w,g as K,q as P,h as Ie,r as N,C as Y,L as Oe,i as Pe,B as le,k as Te,E as Be,l as de,m as Ge,n as $e,o as He,p as ee,X as pe,P as Qe,H as fe,T as re,s as Ue,t as Ve,v as V,w as We,S as ye,x as Ze,y as Je,z as Xe,A as Ye,D as De,F as et,G as tt}from"./index-DCel3VuW.js";import{C as rt}from"./code-xml-D8KV20lN.js";import{R as st}from"./rotate-ccw-D-_Gb8HQ.js";import{C as be,Z as ie,L as nt,a as at}from"./zap-BzgvZa8u.js";import{L as ot}from"./layers-BNyHHdLg.js";/**
|
import{c as q,u as oe,a as Fe,b as ge,j as e,f as ue,d as Re,e as w,g as K,q as P,h as Ie,r as N,C as Y,L as Oe,i as Pe,B as le,k as Te,E as Be,l as de,m as Ge,n as $e,o as He,p as ee,X as pe,P as Qe,H as fe,T as re,s as Ue,t as Ve,v as V,w as We,S as ye,x as Ze,y as Je,z as Xe,A as Ye,D as De,F as et,G as tt}from"./index-DI19YZ2i.js";import{C as rt}from"./code-xml-C9b7xGyM.js";import{R as st}from"./rotate-ccw-CBUivZG3.js";import{C as be,Z as ie,L as nt,a as at}from"./zap-CVKHAOl9.js";import{L as ot}from"./layers-DQdxWFgI.js";/**
|
||||||
* @license lucide-react v0.460.0 - ISC
|
* @license lucide-react v0.460.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import{c as s,j as e}from"./index-DCel3VuW.js";/**
|
import{c as s,j as e}from"./index-DI19YZ2i.js";/**
|
||||||
* @license lucide-react v0.460.0 - ISC
|
* @license lucide-react v0.460.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import{ar as y,r as x,g as L,j as e,L as v,S,ag as W,U as C,e as w,as as E}from"./index-DCel3VuW.js";import{F as M}from"./folder-open-wGxbAB4V.js";const D={"":"Allgemein",traeume:"Träume",muster:"Muster","skill-kandidaten":"Skill-Kandidaten","skill-kandidaten/beauftragt":"Skill-Kandidaten · beauftragt","skill-kandidaten/verworfen":"Skill-Kandidaten · verworfen"};function T(){const{data:o,isLoading:h}=y(),[l,f]=x.useState(null),[d,r]=x.useState(null),[u,i]=x.useState(!1),[a,p]=x.useState(""),t=x.useMemo(()=>(o==null?void 0:o.files)??[],[o]),m=x.useMemo(()=>{const s=new Map;for(const n of t)s.set(n.name.toLowerCase(),n.path);return s},[t]),g=x.useMemo(()=>{if(!a.trim())return t;const s=a.toLowerCase();return t.filter(n=>n.title.toLowerCase().includes(s)||n.path.toLowerCase().includes(s))},[t,a]),b=x.useMemo(()=>{const s=[];for(const n of g){const c=s.find(k=>k.dir===n.dir);c?c.files.push(n):s.push({dir:n.dir,files:[n]})}return s.sort((n,c)=>n.dir===""?-1:c.dir===""?1:n.dir.localeCompare(c.dir))},[g]);x.useEffect(()=>{if(!l&&t.length){const s=t.find(n=>n.path.toLowerCase()==="index.md");f((s==null?void 0:s.path)??t[0].path)}},[t,l]),x.useEffect(()=>{l&&(i(!0),L(`/api/wissen/datei?pfad=${encodeURIComponent(l)}`).then(r).catch(()=>r({path:l,content:"*(Notiz nicht ladbar)*",mtime:0})).finally(()=>i(!1)))},[l]);const j=s=>{const n=m.get(s.toLowerCase());n&&f(n)};return e.jsxs("div",{className:"space-y-5",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"bg-gradient-to-r from-foreground via-foreground to-primary bg-clip-text font-space text-2xl font-bold tracking-tight text-transparent",children:"Wissen"}),e.jsx("p",{className:"text-sm text-muted-foreground",children:"Was sich die Box nachts erschließt — Traum-Notizen, Muster und Skill-Ideen aus dem Wissens-Vault, verlinkt wie ein Wiki."})]}),o&&!o.available&&e.jsx("div",{className:"rounded-2xl border border-amber-500/30 bg-amber-500/5 p-4 text-xs text-amber-300",children:"Der Wissens-Vault liegt auf der Box (~/wissens-vault) — im lokalen Dev-Modus nicht verfügbar."}),h&&e.jsxs("div",{className:"flex items-center gap-2 rounded-2xl border border-border/60 bg-card/30 p-6 text-xs text-muted-foreground",children:[e.jsx(v,{className:"h-4 w-4 animate-spin"})," Vault wird geladen …"]}),(o==null?void 0:o.available)&&t.length===0&&e.jsx("div",{className:"rounded-2xl border border-dashed border-border/60 bg-card/20 p-8 text-center text-xs text-muted-foreground",children:"Noch keine Notizen. Der nächtliche Traum (03:15) legt sie an — morgen früh steht hier die erste."}),t.length>0&&e.jsxs("div",{className:"flex flex-col gap-4 lg:flex-row",children:[e.jsxs("aside",{className:"w-full shrink-0 space-y-3 lg:w-72",children:[e.jsxs("div",{className:"relative",children:[e.jsx("input",{value:a,onChange:s=>p(s.target.value),type:"search",placeholder:"Notizen durchsuchen…","aria-label":"Notizen durchsuchen",className:"h-9 w-full rounded-lg border border-border/60 bg-card/45 pl-8 pr-3 text-xs text-foreground outline-none focus:ring-1 focus:ring-primary/50"}),e.jsx(S,{className:"absolute left-2.5 top-2.5 h-3.5 w-3.5 text-muted-foreground"})]}),e.jsx("div",{className:"max-h-[70vh] space-y-3 overflow-y-auto pr-1 scrollbar-thin",children:b.map(({dir:s,files:n})=>e.jsxs("div",{children:[e.jsxs("p",{className:"mb-1 flex items-center gap-1 text-[10px] font-bold uppercase tracking-wider text-muted-foreground/60",children:[e.jsx(M,{className:"h-3 w-3"})," ",D[s]??s]}),e.jsx("div",{className:"space-y-0.5",children:n.map(c=>e.jsxs("button",{onClick:()=>f(c.path),className:w("flex w-full items-center gap-1.5 rounded-lg px-2 py-1.5 text-left text-xs transition-all cursor-pointer",l===c.path?"bg-primary/15 text-primary":"text-muted-foreground hover:bg-accent hover:text-foreground"),children:[e.jsx(W,{className:"h-3.5 w-3.5 shrink-0"}),e.jsx("span",{className:"truncate",title:c.title,children:c.name}),c.neu&&e.jsxs("span",{className:"ml-auto flex items-center gap-0.5 rounded bg-violet-500/15 px-1 py-0.5 text-[8px] font-bold uppercase text-violet-300",children:[e.jsx(C,{className:"h-2.5 w-2.5"}),"neu"]})]},c.path))})]},s||"__root"))})]}),e.jsx("div",{className:"min-w-0 flex-1 mc-card p-5",children:u?e.jsxs("div",{className:"flex items-center gap-2 text-xs text-muted-foreground",children:[e.jsx(v,{className:"h-4 w-4 animate-spin"})," Notiz wird geladen …"]}):d?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"mb-3 flex items-center justify-between border-b border-border/40 pb-2",children:[e.jsxs("span",{className:"flex items-center gap-1.5 font-mono text-[11px] text-muted-foreground",children:[e.jsx(E,{className:"h-3.5 w-3.5"})," ",d.path]}),d.mtime>0&&e.jsxs("span",{className:"text-[10px] text-muted-foreground/60",children:["Stand ",new Date(d.mtime*1e3).toLocaleString("de-DE",{day:"2-digit",month:"2-digit",hour:"2-digit",minute:"2-digit"})]})]}),e.jsx(z,{text:d.content,onWikiLink:j,known:m})]}):null})]})]})}function z({text:o,onWikiLink:h,known:l}){const f=o.split(`
|
import{ar as y,r as x,g as L,j as e,L as v,S,ag as W,U as C,e as w,as as E}from"./index-DI19YZ2i.js";import{F as M}from"./folder-open-DKNPWUub.js";const D={"":"Allgemein",traeume:"Träume",muster:"Muster","skill-kandidaten":"Skill-Kandidaten","skill-kandidaten/beauftragt":"Skill-Kandidaten · beauftragt","skill-kandidaten/verworfen":"Skill-Kandidaten · verworfen"};function T(){const{data:o,isLoading:h}=y(),[l,f]=x.useState(null),[d,r]=x.useState(null),[u,i]=x.useState(!1),[a,p]=x.useState(""),t=x.useMemo(()=>(o==null?void 0:o.files)??[],[o]),m=x.useMemo(()=>{const s=new Map;for(const n of t)s.set(n.name.toLowerCase(),n.path);return s},[t]),g=x.useMemo(()=>{if(!a.trim())return t;const s=a.toLowerCase();return t.filter(n=>n.title.toLowerCase().includes(s)||n.path.toLowerCase().includes(s))},[t,a]),b=x.useMemo(()=>{const s=[];for(const n of g){const c=s.find(k=>k.dir===n.dir);c?c.files.push(n):s.push({dir:n.dir,files:[n]})}return s.sort((n,c)=>n.dir===""?-1:c.dir===""?1:n.dir.localeCompare(c.dir))},[g]);x.useEffect(()=>{if(!l&&t.length){const s=t.find(n=>n.path.toLowerCase()==="index.md");f((s==null?void 0:s.path)??t[0].path)}},[t,l]),x.useEffect(()=>{l&&(i(!0),L(`/api/wissen/datei?pfad=${encodeURIComponent(l)}`).then(r).catch(()=>r({path:l,content:"*(Notiz nicht ladbar)*",mtime:0})).finally(()=>i(!1)))},[l]);const j=s=>{const n=m.get(s.toLowerCase());n&&f(n)};return e.jsxs("div",{className:"space-y-5",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"bg-gradient-to-r from-foreground via-foreground to-primary bg-clip-text font-space text-2xl font-bold tracking-tight text-transparent",children:"Wissen"}),e.jsx("p",{className:"text-sm text-muted-foreground",children:"Was sich die Box nachts erschließt — Traum-Notizen, Muster und Skill-Ideen aus dem Wissens-Vault, verlinkt wie ein Wiki."})]}),o&&!o.available&&e.jsx("div",{className:"rounded-2xl border border-amber-500/30 bg-amber-500/5 p-4 text-xs text-amber-300",children:"Der Wissens-Vault liegt auf der Box (~/wissens-vault) — im lokalen Dev-Modus nicht verfügbar."}),h&&e.jsxs("div",{className:"flex items-center gap-2 rounded-2xl border border-border/60 bg-card/30 p-6 text-xs text-muted-foreground",children:[e.jsx(v,{className:"h-4 w-4 animate-spin"})," Vault wird geladen …"]}),(o==null?void 0:o.available)&&t.length===0&&e.jsx("div",{className:"rounded-2xl border border-dashed border-border/60 bg-card/20 p-8 text-center text-xs text-muted-foreground",children:"Noch keine Notizen. Der nächtliche Traum (03:15) legt sie an — morgen früh steht hier die erste."}),t.length>0&&e.jsxs("div",{className:"flex flex-col gap-4 lg:flex-row",children:[e.jsxs("aside",{className:"w-full shrink-0 space-y-3 lg:w-72",children:[e.jsxs("div",{className:"relative",children:[e.jsx("input",{value:a,onChange:s=>p(s.target.value),type:"search",placeholder:"Notizen durchsuchen…","aria-label":"Notizen durchsuchen",className:"h-9 w-full rounded-lg border border-border/60 bg-card/45 pl-8 pr-3 text-xs text-foreground outline-none focus:ring-1 focus:ring-primary/50"}),e.jsx(S,{className:"absolute left-2.5 top-2.5 h-3.5 w-3.5 text-muted-foreground"})]}),e.jsx("div",{className:"max-h-[70vh] space-y-3 overflow-y-auto pr-1 scrollbar-thin",children:b.map(({dir:s,files:n})=>e.jsxs("div",{children:[e.jsxs("p",{className:"mb-1 flex items-center gap-1 text-[10px] font-bold uppercase tracking-wider text-muted-foreground/60",children:[e.jsx(M,{className:"h-3 w-3"})," ",D[s]??s]}),e.jsx("div",{className:"space-y-0.5",children:n.map(c=>e.jsxs("button",{onClick:()=>f(c.path),className:w("flex w-full items-center gap-1.5 rounded-lg px-2 py-1.5 text-left text-xs transition-all cursor-pointer",l===c.path?"bg-primary/15 text-primary":"text-muted-foreground hover:bg-accent hover:text-foreground"),children:[e.jsx(W,{className:"h-3.5 w-3.5 shrink-0"}),e.jsx("span",{className:"truncate",title:c.title,children:c.name}),c.neu&&e.jsxs("span",{className:"ml-auto flex items-center gap-0.5 rounded bg-violet-500/15 px-1 py-0.5 text-[8px] font-bold uppercase text-violet-300",children:[e.jsx(C,{className:"h-2.5 w-2.5"}),"neu"]})]},c.path))})]},s||"__root"))})]}),e.jsx("div",{className:"min-w-0 flex-1 mc-card p-5",children:u?e.jsxs("div",{className:"flex items-center gap-2 text-xs text-muted-foreground",children:[e.jsx(v,{className:"h-4 w-4 animate-spin"})," Notiz wird geladen …"]}):d?e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"mb-3 flex items-center justify-between border-b border-border/40 pb-2",children:[e.jsxs("span",{className:"flex items-center gap-1.5 font-mono text-[11px] text-muted-foreground",children:[e.jsx(E,{className:"h-3.5 w-3.5"})," ",d.path]}),d.mtime>0&&e.jsxs("span",{className:"text-[10px] text-muted-foreground/60",children:["Stand ",new Date(d.mtime*1e3).toLocaleString("de-DE",{day:"2-digit",month:"2-digit",hour:"2-digit",minute:"2-digit"})]})]}),e.jsx(z,{text:d.content,onWikiLink:j,known:m})]}):null})]})]})}function z({text:o,onWikiLink:h,known:l}){const f=o.split(`
|
||||||
`),d=[];let r=[],u=null;const i=a=>{r.length&&(d.push(e.jsx("ul",{className:"mb-3 ml-4 list-disc space-y-1",children:r},a)),r=[])};return f.forEach((a,p)=>{const t=`l${p}`;if(u!==null){a.trimEnd()==="```"?(d.push(e.jsx("pre",{className:"mb-3 overflow-x-auto rounded-xl border border-border/50 bg-background/50 p-3 font-mono text-[11px] leading-relaxed text-foreground/85",children:u.join(`
|
`),d=[];let r=[],u=null;const i=a=>{r.length&&(d.push(e.jsx("ul",{className:"mb-3 ml-4 list-disc space-y-1",children:r},a)),r=[])};return f.forEach((a,p)=>{const t=`l${p}`;if(u!==null){a.trimEnd()==="```"?(d.push(e.jsx("pre",{className:"mb-3 overflow-x-auto rounded-xl border border-border/50 bg-background/50 p-3 font-mono text-[11px] leading-relaxed text-foreground/85",children:u.join(`
|
||||||
`)},t)),u=null):u.push(a);return}if(a.trimStart().startsWith("```")){i(t),u=[];return}const m=a.trimEnd();if(!m.trim()){i(t);return}const g=m.match(/^(#{1,4})\s+(.*)$/);if(g){i(t);const j=g[1].length,s=j===1?"text-lg font-bold mt-1 mb-3":j===2?"text-base font-bold mt-4 mb-2":"text-sm font-bold mt-3 mb-1.5";d.push(e.jsx("p",{className:w(s,"font-space text-foreground"),children:N(g[2],h,l,t)},t));return}const b=m.match(/^\s*[-*•]\s+(.*)$/);if(b){r.push(e.jsx("li",{className:"text-xs leading-relaxed text-foreground/85",children:N(b[1],h,l,t)},t));return}i(t),d.push(e.jsx("p",{className:"mb-2 text-xs leading-relaxed text-foreground/85",children:N(m,h,l,t)},t))}),i("end"),u!==null&&d.push(e.jsx("pre",{className:"mb-3 overflow-x-auto rounded-xl border border-border/50 bg-background/50 p-3 font-mono text-[11px]",children:u.join(`
|
`)},t)),u=null):u.push(a);return}if(a.trimStart().startsWith("```")){i(t),u=[];return}const m=a.trimEnd();if(!m.trim()){i(t);return}const g=m.match(/^(#{1,4})\s+(.*)$/);if(g){i(t);const j=g[1].length,s=j===1?"text-lg font-bold mt-1 mb-3":j===2?"text-base font-bold mt-4 mb-2":"text-sm font-bold mt-3 mb-1.5";d.push(e.jsx("p",{className:w(s,"font-space text-foreground"),children:N(g[2],h,l,t)},t));return}const b=m.match(/^\s*[-*•]\s+(.*)$/);if(b){r.push(e.jsx("li",{className:"text-xs leading-relaxed text-foreground/85",children:N(b[1],h,l,t)},t));return}i(t),d.push(e.jsx("p",{className:"mb-2 text-xs leading-relaxed text-foreground/85",children:N(m,h,l,t)},t))}),i("end"),u!==null&&d.push(e.jsx("pre",{className:"mb-3 overflow-x-auto rounded-xl border border-border/50 bg-background/50 p-3 font-mono text-[11px]",children:u.join(`
|
||||||
`)},"code-end")),e.jsx("div",{className:"max-w-3xl",children:d})}function N(o,h,l,f){return o.split(/(\[\[[^\]]+\]\]|\*\*[^*]+\*\*|\*[^*]+\*|`[^`]+`)/g).map((r,u)=>{const i=`${f}-${u}`,a=r.match(/^\[\[([^\]|]+)(?:\|([^\]]+))?\]\]$/);if(a){const p=a[1].trim(),t=(a[2]??a[1]).trim();return l.has(p.toLowerCase())?e.jsx("button",{onClick:()=>h(p),className:"rounded bg-primary/10 px-1 text-primary underline decoration-primary/40 underline-offset-2 hover:bg-primary/20 cursor-pointer",children:t},i):e.jsx("span",{className:"rounded bg-background/40 px-1 text-muted-foreground",title:"Notiz existiert (noch) nicht",children:t},i)}return r.startsWith("**")&&r.endsWith("**")?e.jsx("b",{className:"font-semibold text-foreground",children:r.slice(2,-2)},i):r.startsWith("*")&&r.endsWith("*")&&r.length>2?e.jsx("i",{children:r.slice(1,-1)},i):r.startsWith("`")&&r.endsWith("`")?e.jsx("code",{className:"rounded bg-background/50 px-1 font-mono text-[11px] text-teal-300",children:r.slice(1,-1)},i):e.jsx("span",{children:r},i)})}export{T as WissenView};
|
`)},"code-end")),e.jsx("div",{className:"max-w-3xl",children:d})}function N(o,h,l,f){return o.split(/(\[\[[^\]]+\]\]|\*\*[^*]+\*\*|\*[^*]+\*|`[^`]+`)/g).map((r,u)=>{const i=`${f}-${u}`,a=r.match(/^\[\[([^\]|]+)(?:\|([^\]]+))?\]\]$/);if(a){const p=a[1].trim(),t=(a[2]??a[1]).trim();return l.has(p.toLowerCase())?e.jsx("button",{onClick:()=>h(p),className:"rounded bg-primary/10 px-1 text-primary underline decoration-primary/40 underline-offset-2 hover:bg-primary/20 cursor-pointer",children:t},i):e.jsx("span",{className:"rounded bg-background/40 px-1 text-muted-foreground",title:"Notiz existiert (noch) nicht",children:t},i)}return r.startsWith("**")&&r.endsWith("**")?e.jsx("b",{className:"font-semibold text-foreground",children:r.slice(2,-2)},i):r.startsWith("*")&&r.endsWith("*")&&r.length>2?e.jsx("i",{children:r.slice(1,-1)},i):r.startsWith("`")&&r.endsWith("`")?e.jsx("code",{className:"rounded bg-background/50 px-1 font-mono text-[11px] text-teal-300",children:r.slice(1,-1)},i):e.jsx("span",{children:r},i)})}export{T as WissenView};
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import{c as a}from"./index-DCel3VuW.js";/**
|
import{c as a}from"./index-DI19YZ2i.js";/**
|
||||||
* @license lucide-react v0.460.0 - ISC
|
* @license lucide-react v0.460.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import{c}from"./index-DCel3VuW.js";/**
|
import{c}from"./index-DI19YZ2i.js";/**
|
||||||
* @license lucide-react v0.460.0 - ISC
|
* @license lucide-react v0.460.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import{c as e}from"./index-DCel3VuW.js";/**
|
import{c as e}from"./index-DI19YZ2i.js";/**
|
||||||
* @license lucide-react v0.460.0 - ISC
|
* @license lucide-react v0.460.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import{c}from"./index-DCel3VuW.js";/**
|
import{c}from"./index-DI19YZ2i.js";/**
|
||||||
* @license lucide-react v0.460.0 - ISC
|
* @license lucide-react v0.460.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import{c as a}from"./index-DCel3VuW.js";/**
|
import{c as a}from"./index-DI19YZ2i.js";/**
|
||||||
* @license lucide-react v0.460.0 - ISC
|
* @license lucide-react v0.460.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
+3
-3
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import{c as a}from"./index-DCel3VuW.js";/**
|
import{c as a}from"./index-DI19YZ2i.js";/**
|
||||||
* @license lucide-react v0.460.0 - ISC
|
* @license lucide-react v0.460.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import{c as t}from"./index-DCel3VuW.js";/**
|
import{c as t}from"./index-DI19YZ2i.js";/**
|
||||||
* @license lucide-react v0.460.0 - ISC
|
* @license lucide-react v0.460.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import{c as a}from"./index-DCel3VuW.js";/**
|
import{c as a}from"./index-DI19YZ2i.js";/**
|
||||||
* @license lucide-react v0.460.0 - ISC
|
* @license lucide-react v0.460.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import{c as a}from"./index-DCel3VuW.js";/**
|
import{c as a}from"./index-DI19YZ2i.js";/**
|
||||||
* @license lucide-react v0.460.0 - ISC
|
* @license lucide-react v0.460.0 - ISC
|
||||||
*
|
*
|
||||||
* This source code is licensed under the ISC license.
|
* This source code is licensed under the ISC license.
|
||||||
Vendored
+2
-2
@@ -7,8 +7,8 @@
|
|||||||
<link rel="manifest" href="/manifest.webmanifest" />
|
<link rel="manifest" href="/manifest.webmanifest" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
<title>Mission Control 2.0</title>
|
<title>Mission Control 2.0</title>
|
||||||
<script type="module" crossorigin src="/assets/index-DCel3VuW.js"></script>
|
<script type="module" crossorigin src="/assets/index-DI19YZ2i.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-DJ-m2OOM.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-DLBMFIff.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
+3
-1
@@ -31,7 +31,9 @@ export const NAV: NavItem[] = [
|
|||||||
{ id: "dashboard", label: "Cockpit", hint: "Deine Box auf einen Blick", icon: LayoutDashboard, group: "Operativ" },
|
{ id: "dashboard", label: "Cockpit", hint: "Deine Box auf einen Blick", icon: LayoutDashboard, group: "Operativ" },
|
||||||
// Der Haupteingang: Idee → Konzept → Repo → Zed. Steht bewusst direkt unter dem
|
// Der Haupteingang: Idee → Konzept → Repo → Zed. Steht bewusst direkt unter dem
|
||||||
// Cockpit — hier fängt jede Arbeit an (eigene Seite seit 25.07.2026).
|
// Cockpit — hier fängt jede Arbeit an (eigene Seite seit 25.07.2026).
|
||||||
{ id: "ideen", label: "Ideen", hint: "Idee eingeben — die Box legt Konzept und Repo an", icon: Lightbulb, group: "Operativ" },
|
// Hinweis bewusst kurz: die App zeigt ihn oben in Großbuchstaben, direkt über der
|
||||||
|
// Seitenüberschrift „Ideen" — ein langer Satz stand dort dreifach gedoppelt.
|
||||||
|
{ id: "ideen", label: "Ideen", hint: "Schreib hin, was entstehen soll", icon: Lightbulb, group: "Operativ" },
|
||||||
{ id: "auftraege", label: "Auftragsbuch", hint: "Vorschläge der Box — annehmen oder ablehnen", icon: Inbox, group: "Operativ" },
|
{ id: "auftraege", label: "Auftragsbuch", hint: "Vorschläge der Box — annehmen oder ablehnen", icon: Inbox, group: "Operativ" },
|
||||||
{ id: "eigenleben", label: "Skills", hint: "Fähigkeiten der Box — aktiv/inaktiv, Herkunft & Vorschlags-Bilanz", icon: Sparkles, group: "Operativ" },
|
{ id: "eigenleben", label: "Skills", hint: "Fähigkeiten der Box — aktiv/inaktiv, Herkunft & Vorschlags-Bilanz", icon: Sparkles, group: "Operativ" },
|
||||||
{ id: "chronik", label: "Chronik", hint: "Was die Box von allein getan hat + Zeitmaschine", icon: History, group: "Operativ" },
|
{ id: "chronik", label: "Chronik", hint: "Was die Box von allein getan hat + Zeitmaschine", icon: History, group: "Operativ" },
|
||||||
|
|||||||
+153
-105
@@ -67,11 +67,20 @@ function IdeenSection() {
|
|||||||
const qc = useQueryClient()
|
const qc = useQueryClient()
|
||||||
const { showAlert, showConfirm, dialogElement } = useDialog()
|
const { showAlert, showConfirm, dialogElement } = useDialog()
|
||||||
const [text, setText] = useState("")
|
const [text, setText] = useState("")
|
||||||
// Intake-Modus: Box baut · Idee (nur durchdenken) · IDE simpel/gründlich (Projekt vorbereiten).
|
// Zwei UNABHÄNGIGE Entscheidungen statt vier sich ausschliessender Modi (25.07.2026):
|
||||||
const [modus, setModus] = useState<"box" | "idee" | "simpel" | "gruendlich">("box")
|
// wer — baut die Box selbst, oder bekommst du ein Repo für Zed?
|
||||||
const isIdee = modus === "idee" // Produkt-Idee prüfen
|
// nurDenken — soll erst nur ein Konzept entstehen? (passt zu beidem)
|
||||||
const isProjekt = modus === "simpel" || modus === "gruendlich" // Projekt für Zed
|
// Vorher hiessen die Knöpfe BOX/IDEE/IDE:SIMPEL/IDE:GRÜNDLICH und pressten beides in eine
|
||||||
const isIde = isIdee || isProjekt // beides → projektstart
|
// Reihe — das brauchte 116 Wörter Beipackzettel darunter. Die Abbildung auf die
|
||||||
|
// unveränderte API passiert in submit().
|
||||||
|
const [wer, setWer] = useState<"box" | "zed">("box")
|
||||||
|
const [tiefe, setTiefe] = useState<"schlank" | "gruendlich">("schlank")
|
||||||
|
const [nurDenken, setNurDenken] = useState(false)
|
||||||
|
// Kam die aktuelle Auswahl von der Box? Nur fürs Etikett „Vorschlag" — sobald du selbst
|
||||||
|
// klickst, verschwindet es, damit nie unklar ist, wessen Entscheidung da steht.
|
||||||
|
const [vorgeschlagen, setVorgeschlagen] = useState(false)
|
||||||
|
const isIde = wer === "zed" || nurDenken // beides läuft über das projektstart-Profil
|
||||||
|
const isProjekt = wer === "zed" && !nurDenken // nur DANN sind Tiefe und Ziel wirksam
|
||||||
// Ziel-Plattform des Projekts (nur IDE-Welt): läuft es als LXC-Dienst oder in Docker?
|
// Ziel-Plattform des Projekts (nur IDE-Welt): läuft es als LXC-Dienst oder in Docker?
|
||||||
const [ziel, setZiel] = useState<"lxc" | "docker">("lxc")
|
const [ziel, setZiel] = useState<"lxc" | "docker">("lxc")
|
||||||
const [sending, setSending] = useState(false)
|
const [sending, setSending] = useState(false)
|
||||||
@@ -83,6 +92,28 @@ function IdeenSection() {
|
|||||||
const items = data?.items ?? []
|
const items = data?.items ?? []
|
||||||
const reload = () => qc.invalidateQueries({ queryKey: qk.ideen })
|
const reload = () => qc.invalidateQueries({ queryKey: qk.ideen })
|
||||||
|
|
||||||
|
// Die Box liest mit und wählt vor. Gemessen ~500 ms auf dem dauerwarmen Hirn, deshalb
|
||||||
|
// reicht eine kurze Tipp-Pause. Bewusst ein VORSCHLAG: er setzt nur die Auswahl, die du
|
||||||
|
// ohnehin siehst und mit einem Klick überschreibst. Scheitert er, passiert schlicht nichts
|
||||||
|
// — kein Ladebalken, keine Fehlermeldung, das Tippen wird nie blockiert.
|
||||||
|
useEffect(() => {
|
||||||
|
const t = text.trim()
|
||||||
|
if (t.length < 12) { setVorgeschlagen(false); return }
|
||||||
|
let verworfen = false
|
||||||
|
const timer = setTimeout(async () => {
|
||||||
|
try {
|
||||||
|
const r = await api<{ ok: boolean; wer?: "box" | "zed"; tiefe?: "schlank" | "gruendlich"; nur_denken?: boolean }>(
|
||||||
|
"/api/ideen/einordnen", { method: "POST", body: JSON.stringify({ text: t }) })
|
||||||
|
if (verworfen || !r?.ok) return
|
||||||
|
if (r.wer) setWer(r.wer)
|
||||||
|
if (r.tiefe) setTiefe(r.tiefe)
|
||||||
|
setNurDenken(!!r.nur_denken)
|
||||||
|
setVorgeschlagen(true)
|
||||||
|
} catch { /* stumm — die Voreinstellung bleibt einfach stehen */ }
|
||||||
|
}, 700)
|
||||||
|
return () => { verworfen = true; clearTimeout(timer) }
|
||||||
|
}, [text])
|
||||||
|
|
||||||
// Polling-Log nur aktivieren, wenn Idee triage/running und geöffnet
|
// Polling-Log nur aktivieren, wenn Idee triage/running und geöffnet
|
||||||
const logEnabled = openLog !== null && (data?.items.find((i) => i.id === openLog)?.status === "triage" || data?.items.find((i) => i.id === openLog)?.status === "running")
|
const logEnabled = openLog !== null && (data?.items.find((i) => i.id === openLog)?.status === "triage" || data?.items.find((i) => i.id === openLog)?.status === "running")
|
||||||
const logData = useIdeenLog(openLog ?? "", logEnabled)
|
const logData = useIdeenLog(openLog ?? "", logEnabled)
|
||||||
@@ -97,12 +128,15 @@ function IdeenSection() {
|
|||||||
const titel = t.length > 160 ? t.slice(0, 157) + "…" : t
|
const titel = t.length > 160 ? t.slice(0, 157) + "…" : t
|
||||||
await api("/api/ideen", { method: "POST", body: JSON.stringify({
|
await api("/api/ideen", { method: "POST", body: JSON.stringify({
|
||||||
titel, notiz: t.length > 160 ? t : "",
|
titel, notiz: t.length > 160 ? t : "",
|
||||||
|
// Abbildung auf die unveränderte API: „nur denken" läuft immer über die IDE-Welt,
|
||||||
|
// weil dort das Profil sitzt, das denkt statt baut.
|
||||||
welt: isIde ? "ide" : "box",
|
welt: isIde ? "ide" : "box",
|
||||||
tiefe: isProjekt ? modus : "",
|
tiefe: isProjekt ? (tiefe === "gruendlich" ? "gruendlich" : "simpel") : "",
|
||||||
ziel: isProjekt ? ziel : "", // eine Idee braucht noch keine Plattform
|
ziel: isProjekt ? ziel : "", // eine Idee braucht noch keine Plattform
|
||||||
art: isIdee ? "idee" : "projekt",
|
art: nurDenken ? "idee" : "projekt",
|
||||||
}) })
|
}) })
|
||||||
setText("")
|
setText("")
|
||||||
|
setVorgeschlagen(false)
|
||||||
reload()
|
reload()
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
showAlert("Das hat nicht geklappt", String(e?.message || e))
|
showAlert("Das hat nicht geklappt", String(e?.message || e))
|
||||||
@@ -392,7 +426,9 @@ function IdeenSection() {
|
|||||||
n(["todo", "scheduled", "ready"]) > 0 ? `${n(["todo", "scheduled", "ready"])} wartet` : "",
|
n(["todo", "scheduled", "ready"]) > 0 ? `${n(["todo", "scheduled", "ready"])} wartet` : "",
|
||||||
n(["done"]) > 0 ? `${n(["done"])} fertig` : "",
|
n(["done"]) > 0 ? `${n(["done"])} fertig` : "",
|
||||||
].filter(Boolean)
|
].filter(Boolean)
|
||||||
return `Ideen-Queue${teile.length ? " — " + teile.join(" · ") : ""}`
|
// Nur der Zustand, nicht nochmal der Seitenname: „Ideen" steht bereits als
|
||||||
|
// Überschrift darüber und ein drittes Mal in der Kopfzeile der App.
|
||||||
|
return teile.length ? teile.join(" · ") : "noch nichts in der Queue"
|
||||||
})()}
|
})()}
|
||||||
</SectionLabel>
|
</SectionLabel>
|
||||||
{(() => {
|
{(() => {
|
||||||
@@ -410,120 +446,116 @@ function IdeenSection() {
|
|||||||
)
|
)
|
||||||
})()}
|
})()}
|
||||||
</div>
|
</div>
|
||||||
<div className="mc-card p-4 space-y-3">
|
<div className="mc-card p-4 space-y-4">
|
||||||
|
{/* Schreiben zuerst. Alles darunter ist eine Entscheidung ZU dem, was hier steht —
|
||||||
|
nicht davor. Der Knopf heisst immer gleich; was passiert, steht bei den Karten. */}
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<input
|
<input
|
||||||
value={text}
|
value={text}
|
||||||
onChange={(e) => setText(e.target.value)}
|
onChange={(e) => { setText(e.target.value); setVorgeschlagen(false) }}
|
||||||
onKeyDown={(e) => { if (e.key === "Enter") submit() }}
|
onKeyDown={(e) => { if (e.key === "Enter") submit() }}
|
||||||
placeholder="Neue Idee — einfach hinschreiben, die Box macht den Rest"
|
placeholder="Was soll entstehen?"
|
||||||
className="h-9 min-w-0 flex-1 rounded-lg border border-border/60 bg-background/50 px-3 text-sm text-foreground placeholder:text-muted-foreground/50 focus:border-primary/50 focus:outline-none"
|
className="h-10 min-w-0 flex-1 rounded-lg border border-border/60 bg-background/50 px-3 text-sm text-foreground placeholder:text-muted-foreground/50 focus:border-primary/50 focus:outline-none"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
onClick={submit}
|
onClick={submit}
|
||||||
disabled={sending || text.trim().length < 3}
|
disabled={sending || text.trim().length < 3}
|
||||||
className={cn("flex h-9 shrink-0 items-center gap-1.5 rounded-lg border px-3 text-[11px] font-bold uppercase tracking-wide transition-all cursor-pointer disabled:opacity-50",
|
className="flex h-10 shrink-0 items-center gap-1.5 rounded-lg border border-primary/40 bg-primary/10 px-4 text-xs font-semibold text-primary transition-all hover:bg-primary/20 disabled:opacity-40 cursor-pointer">
|
||||||
modus === "gruendlich" ? "border-amber-500/40 bg-amber-500/10 text-amber-300 hover:bg-amber-500/20"
|
{sending ? <Loader2 className="h-4 w-4 animate-spin" /> : <Send className="h-4 w-4" />}
|
||||||
: modus === "simpel" ? "border-violet-500/40 bg-violet-500/10 text-violet-300 hover:bg-violet-500/20"
|
Los
|
||||||
: isIdee ? "border-sky-500/40 bg-sky-500/10 text-sky-300 hover:bg-sky-500/20"
|
|
||||||
: "border-primary/40 bg-primary/10 text-primary hover:bg-primary/20")}>
|
|
||||||
{sending ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : isIdee ? <Lightbulb className="h-3.5 w-3.5" /> : isProjekt ? <Code2 className="h-3.5 w-3.5" /> : <Send className="h-3.5 w-3.5" />}
|
|
||||||
{isIdee ? "Durchdenken" : isProjekt ? "Vorbereiten" : "In die Queue"}
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1.5">
|
|
||||||
<span className="text-[11px] text-muted-foreground/70">Modus</span>
|
{/* Wer baut das? Zwei Karten statt vier Kürzel — jede sagt, was DU danach bekommst. */}
|
||||||
<div className="flex rounded-lg border border-border/60 p-0.5">
|
<div>
|
||||||
<button
|
<p className="mb-1.5 text-[11px] text-muted-foreground/70">Wer baut das?</p>
|
||||||
onClick={() => setModus("box")}
|
<div className="grid gap-2 sm:grid-cols-2">
|
||||||
className={cn("flex items-center gap-1 rounded px-2 py-1 text-[10px] font-bold uppercase tracking-wide transition-all cursor-pointer",
|
{([
|
||||||
modus === "box" ? "bg-primary/15 text-primary" : "text-muted-foreground/60 hover:text-foreground")}>
|
{ id: "box" as const, icon: Hammer, titel: "Die Box, allein",
|
||||||
<Hammer className="h-3 w-3" /> Box
|
text: "Sie baut es und legt dir das Ergebnis als Vorschlag hin — nichts geht ohne deinen Klick live." },
|
||||||
</button>
|
{ id: "zed" as const, icon: Code2, titel: "Ich, in Zed",
|
||||||
<button
|
text: "Du bekommst ein fertiges Repo mit Konzept und Doku. Den Code schreibst du selbst." },
|
||||||
onClick={() => setModus("idee")}
|
]).map((k) => {
|
||||||
title="Produkt-Idee erst einmal durchdenken lassen: Was ist es, für wen, was gibt es schon, was spricht dagegen? Ergebnis ist ein Konzept — kein Projekt, kein Bau-Druck."
|
const aktiv = wer === k.id
|
||||||
className={cn("flex items-center gap-1 rounded px-2 py-1 text-[10px] font-bold uppercase tracking-wide transition-all cursor-pointer",
|
return (
|
||||||
modus === "idee" ? "bg-sky-500/15 text-sky-300" : "text-muted-foreground/60 hover:text-foreground")}>
|
<button key={k.id}
|
||||||
<Lightbulb className="h-3 w-3" /> Idee
|
onClick={() => { setWer(k.id); setVorgeschlagen(false) }}
|
||||||
</button>
|
disabled={nurDenken}
|
||||||
<button
|
className={cn("rounded-xl border p-3 text-left transition-all cursor-pointer disabled:cursor-default",
|
||||||
onClick={() => setModus("simpel")}
|
nurDenken ? "border-border/30 opacity-40"
|
||||||
title="IDE-Projekt, schnell vorbereitet: knappes Konzept, wenige Denk-Runden. Für kleine, klar umrissene Sachen."
|
: aktiv ? "border-primary/60 bg-primary/5" : "border-border/50 hover:border-border")}>
|
||||||
className={cn("flex items-center gap-1 rounded px-2 py-1 text-[10px] font-bold uppercase tracking-wide transition-all cursor-pointer",
|
<div className="flex items-center gap-2">
|
||||||
modus === "simpel" ? "bg-violet-500/15 text-violet-300" : "text-muted-foreground/60 hover:text-foreground")}>
|
<k.icon className={cn("h-4 w-4 shrink-0", aktiv && !nurDenken ? "text-primary" : "text-muted-foreground")} />
|
||||||
<Code2 className="h-3 w-3" /> IDE: simpel
|
<span className="text-xs font-semibold text-foreground">{k.titel}</span>
|
||||||
</button>
|
{aktiv && vorgeschlagen && !nurDenken && (
|
||||||
<button
|
<span className="ml-auto shrink-0 rounded border border-primary/40 bg-primary/10 px-1.5 py-0.5 text-[10px] font-bold text-primary">
|
||||||
onClick={() => setModus("gruendlich")}
|
Vorschlag
|
||||||
title="IDE-Projekt, gründlich durchdacht: volle Kaskade mit mehreren Rollen und Härtetests. Für große/ambitionierte Projekte."
|
</span>
|
||||||
className={cn("flex items-center gap-1 rounded px-2 py-1 text-[10px] font-bold uppercase tracking-wide transition-all cursor-pointer",
|
)}
|
||||||
modus === "gruendlich" ? "bg-amber-500/15 text-amber-300" : "text-muted-foreground/60 hover:text-foreground")}>
|
</div>
|
||||||
<Code2 className="h-3 w-3" /> IDE: gründlich
|
<p className="mt-1 text-[11px] leading-relaxed text-muted-foreground">{k.text}</p>
|
||||||
</button>
|
</button>
|
||||||
|
)
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
{nurDenken && (
|
||||||
|
<p className="mt-1.5 text-[10px] text-muted-foreground/60">
|
||||||
|
Wer baut, entscheidest du, wenn das Konzept da ist.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{/* Immer sichtbare Orientierung — der Text darunter erklärt nur den GEWÄHLTEN
|
|
||||||
Modus, die Wahl selbst muss man aber vorher treffen können (User 21.07.). */}
|
{/* Tiefe und Ziel wirken NUR beim Repo-Weg — deshalb erscheinen sie auch nur dort.
|
||||||
<p className="text-[10px] leading-relaxed text-muted-foreground/60">
|
Ein Schalter, der nichts tut, ist schlimmer als keiner. */}
|
||||||
<b className="text-primary/80">Box</b>, wenn die Box es selbst bauen soll (dein Heimlabor,
|
|
||||||
MC2, Lucy — Ergebnis kommt als Patch-Vorschlag zurück). ·{" "}
|
|
||||||
<b className="text-sky-300/80">Idee</b>, wenn du noch nicht weißt, ob es das überhaupt wert
|
|
||||||
ist — erst durchdenken, entscheiden kannst du danach. ·{" "}
|
|
||||||
<b className="text-violet-300/80">IDE</b>, wenn du es selbst in Zed bauen willst und dafür
|
|
||||||
ein startklares Repo brauchst.
|
|
||||||
</p>
|
|
||||||
{isProjekt && (
|
{isProjekt && (
|
||||||
<div className="flex items-center gap-1.5">
|
<div className="flex flex-wrap items-center gap-x-3 gap-y-2">
|
||||||
<span className="text-[11px] text-muted-foreground/70">Ziel</span>
|
<span className="text-[11px] text-muted-foreground/70">Wie gründlich?</span>
|
||||||
<div className="flex rounded-lg border border-border/40 p-0.5">
|
<div className="flex rounded-lg border border-border/60 p-0.5">
|
||||||
<button
|
{([["schlank", "schlank"], ["gruendlich", "gründlich"]] as const).map(([id, label]) => (
|
||||||
onClick={() => setZiel("lxc")}
|
<button key={id} onClick={() => { setTiefe(id); setVorgeschlagen(false) }}
|
||||||
title="Läuft als Dienst in einem eigenen Proxmox-LXC (systemd, kein Docker). Wird vom Backup-Server automatisch mitgesichert."
|
className={cn("rounded px-2.5 py-1 text-[10px] font-bold uppercase tracking-wide transition-all cursor-pointer",
|
||||||
className={cn("rounded px-2 py-0.5 text-[10px] font-semibold transition-all cursor-pointer",
|
tiefe === id ? "bg-primary/15 text-primary" : "text-muted-foreground/60 hover:text-foreground")}>
|
||||||
ziel === "lxc" ? "bg-foreground/10 text-foreground" : "text-muted-foreground/50 hover:text-foreground/80")}>
|
{label}
|
||||||
LXC
|
</button>
|
||||||
</button>
|
))}
|
||||||
<button
|
|
||||||
onClick={() => setZiel("docker")}
|
|
||||||
title="Natives Docker nach Standard-Praxis: Dockerfile + compose, portabel, über Gitea-/GitLab-Pipelines baubar. Für Projekte, die bewusst außerhalb der LXC-Welt leben."
|
|
||||||
className={cn("rounded px-2 py-0.5 text-[10px] font-semibold transition-all cursor-pointer",
|
|
||||||
ziel === "docker" ? "bg-foreground/10 text-foreground" : "text-muted-foreground/50 hover:text-foreground/80")}>
|
|
||||||
Docker
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<span className="text-[10px] text-muted-foreground/50">
|
<span className="text-[10px] text-muted-foreground/60">
|
||||||
{ziel === "lxc" ? "Dienst im Proxmox-Container (deine Heimat)" : "natives Docker, portabel & pipeline-tauglich"}
|
{tiefe === "gruendlich"
|
||||||
|
? "mehrere Fach-Rollen, bis zu 3 Denk-Runden, mehrfacher Härtetest — dauert entsprechend"
|
||||||
|
: "2 Rollen, eine Denk-Runde — ein paar Minuten"}
|
||||||
|
</span>
|
||||||
|
<span className="ml-auto flex items-center gap-1.5">
|
||||||
|
<span className="text-[11px] text-muted-foreground/70">Läuft als</span>
|
||||||
|
<div className="flex rounded-lg border border-border/40 p-0.5">
|
||||||
|
{([["lxc", "LXC"], ["docker", "Docker"]] as const).map(([id, label]) => (
|
||||||
|
<button key={id} onClick={() => setZiel(id)}
|
||||||
|
title={id === "lxc"
|
||||||
|
? "Dienst in einem eigenen Proxmox-LXC (systemd, kein Docker). Wird vom Backup-Server automatisch mitgesichert."
|
||||||
|
: "Natives Docker: Dockerfile + compose, portabel und pipeline-tauglich."}
|
||||||
|
className={cn("rounded px-2 py-0.5 text-[10px] font-semibold transition-all cursor-pointer",
|
||||||
|
ziel === id ? "bg-foreground/10 text-foreground" : "text-muted-foreground/50 hover:text-foreground/80")}>
|
||||||
|
{label}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<p className="text-[11px] text-muted-foreground">
|
|
||||||
{isIdee ? (
|
|
||||||
<>
|
|
||||||
<b className="text-sky-300/90">Idee durchdenken:</b> Die Box arbeitet deine Produkt-Idee
|
|
||||||
mit Produkt-Blick aus — was ist es genau, für wen, was gibt es dafür schon, was wäre der
|
|
||||||
kleinste sinnvolle Wurf und was spricht dagegen. Volle Denk-Kaskade mit Kritiker, Ergebnis
|
|
||||||
ist <b>nur ein Konzept</b> (kein Gerüst, keine Roadmap, kein Bau-Druck) — du liest es hier
|
|
||||||
und entscheidest danach, ob ein Projekt draus wird.
|
|
||||||
</>
|
|
||||||
) : isProjekt ? (
|
|
||||||
<>
|
|
||||||
<b className={modus === "gruendlich" ? "text-amber-300/90" : "text-violet-300/90"}>IDE-Welt{modus === "gruendlich" ? " · gründlich" : " · simpel"}:</b>{" "}
|
|
||||||
Die Box denkt das Konzept durch{modus === "gruendlich"
|
|
||||||
? " — volle Kaskade: mehrere Fach-Rollen, bis zu 3 Denk-Runden, mehrfacher Kritiker-Härtetest (für große/ambitionierte Projekte, dauert entsprechend)"
|
|
||||||
: " — schlank: 2 Rollen, 1 Denk-Runde (für kleine, klar umrissene Projekte, ein paar Minuten)"}, legt
|
|
||||||
ein leeres Gitea-Repo an und füllt es nur mit den wichtigen Dokumenten (Konzept, Roadmap, AGENTS.md,
|
|
||||||
.aiexclude) — <b>kein Code</b>. Danach öffnest du das Repo in Zed und baust selbst.
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
Eine Queue, alle Türen: hier, per Telegram oder im Gespräch mit Lucy. Die Box arbeitet Ideen selbst
|
|
||||||
aus und baut — Code-Ergebnisse erscheinen als Karte unter „Patch-Vorschläge“, nichts davon geht ohne
|
|
||||||
deinen Klick live. Nur reine Doku-Bagatellen zieht die Box nachts selbst ein (mit Fangnetz und Meldung).
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
|
{/* Orthogonal zu allem darüber: erst denken, bauen später entscheiden. */}
|
||||||
|
<label className="flex cursor-pointer items-start gap-2 border-t border-border/30 pt-3">
|
||||||
|
<input type="checkbox" checked={nurDenken}
|
||||||
|
onChange={(e) => { setNurDenken(e.target.checked); setVorgeschlagen(false) }}
|
||||||
|
className="mt-0.5 h-3.5 w-3.5 shrink-0 accent-sky-400 cursor-pointer" />
|
||||||
|
<span className="min-w-0">
|
||||||
|
<span className="text-xs font-semibold text-foreground">Erst nur durchdenken, noch nichts anlegen</span>
|
||||||
|
<span className="mt-0.5 block text-[11px] leading-relaxed text-muted-foreground">
|
||||||
|
Die Box arbeitet die Idee mit Produkt-Blick aus — was ist es, für wen, was gibt es
|
||||||
|
schon, was wäre der kleinste sinnvolle Wurf, was spricht dagegen. Ergebnis ist ein
|
||||||
|
Konzept: kein Repo, kein Code, kein Bau-Druck.
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
<p className="flex items-center gap-2 text-xs text-muted-foreground"><Loader2 className="h-3.5 w-3.5 animate-spin" /> Queue wird geladen …</p>
|
<p className="flex items-center gap-2 text-xs text-muted-foreground"><Loader2 className="h-3.5 w-3.5 animate-spin" /> Queue wird geladen …</p>
|
||||||
)}
|
)}
|
||||||
@@ -577,7 +609,23 @@ function IdeenSection() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{data?.available && !isLoading && items.length === 0 && (
|
{data?.available && !isLoading && items.length === 0 && (
|
||||||
<p className="text-xs text-muted-foreground/70">Queue ist leer — wirf der Box eine Idee zu.</p>
|
// Leerer Zustand als Einladung statt als Feststellung: genau jetzt möchte man
|
||||||
|
// wissen, was man überhaupt hineinschreiben kann. Ein Klick füllt das Feld —
|
||||||
|
// die Box ordnet den Beispieltext dann selbst ein.
|
||||||
|
<div className="space-y-2">
|
||||||
|
<p className="text-[11px] text-muted-foreground/70">Noch nichts hier. Zum Beispiel:</p>
|
||||||
|
{[
|
||||||
|
"Ein kleines Werkzeug, das meine Fotos nach Aufnahmedatum in Ordner sortiert",
|
||||||
|
"Rippy soll die Untertitel-Spur automatisch auswählen statt zu fragen",
|
||||||
|
"Lohnt sich ein Dashboard für die Stromzähler, oder reicht Home Assistant?",
|
||||||
|
].map((b) => (
|
||||||
|
<button key={b} onClick={() => setText(b)}
|
||||||
|
className="flex w-full items-center gap-2 rounded-lg border border-border/40 bg-background/20 px-3 py-2 text-left text-xs text-muted-foreground transition-all hover:border-primary/40 hover:text-foreground cursor-pointer">
|
||||||
|
<Lightbulb className="h-3.5 w-3.5 shrink-0 text-muted-foreground/50" />
|
||||||
|
{b}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{dialogElement}
|
{dialogElement}
|
||||||
|
|||||||
Reference in New Issue
Block a user