Javier Montalvo Claude Sonnet 4.6 (1M context) commited on
Commit
015fde7
·
1 Parent(s): 114ea19

Add demo video link to README; frontend and server updates

Browse files

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

README.md CHANGED
@@ -23,8 +23,12 @@ The LLM path is intentionally constrained: Replicate, OpenAI, or Claude compile
23
  natural language into JSON/YAML automation rules. The app validates those rules
24
  before evaluating them, and the LLM never emits executable code.
25
 
 
 
26
  For a quick operator checklist, see [`DEMO.md`](DEMO.md).
27
 
 
 
28
  ## Run Locally
29
 
30
  ```bash
 
23
  natural language into JSON/YAML automation rules. The app validates those rules
24
  before evaluating them, and the LLM never emits executable code.
25
 
26
+ DEMO video: https://youtu.be/BfXir6fonR0
27
+
28
  For a quick operator checklist, see [`DEMO.md`](DEMO.md).
29
 
30
+
31
+
32
  ## Run Locally
33
 
34
  ```bash
frontend/dist/assets/index-CiwitQvv.js DELETED
The diff for this file is too large to render. See raw diff
 
frontend/dist/index.html CHANGED
@@ -16,7 +16,7 @@
16
  href="https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@400;500;600&family=JetBrains+Mono:wght@400;500;600&display=swap"
17
  rel="stylesheet"
18
  />
19
- <script type="module" crossorigin src="/assets/index-CiwitQvv.js"></script>
20
  <link rel="stylesheet" crossorigin href="/assets/index-DbWTYGKW.css">
21
  </head>
22
  <body>
 
16
  href="https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@400;500;600&family=JetBrains+Mono:wght@400;500;600&display=swap"
17
  rel="stylesheet"
18
  />
19
+ <script type="module" crossorigin src="/assets/index-xgyljnse.js"></script>
20
  <link rel="stylesheet" crossorigin href="/assets/index-DbWTYGKW.css">
21
  </head>
22
  <body>
frontend/src/lib/dashboard.tsx CHANGED
@@ -115,6 +115,7 @@ export function DashboardProvider({ children }: { children: ReactNode }) {
115
  const previewRef = useRef<string | null>(null)
116
  const compileInFlightRef = useRef(false)
117
 
 
118
  const applyRulesText = useCallback(async (nextRulesText: string) => {
119
  setRulesText(nextRulesText)
120
  const nextValidation = await api.validateRules(nextRulesText)
 
115
  const previewRef = useRef<string | null>(null)
116
  const compileInFlightRef = useRef(false)
117
 
118
+
119
  const applyRulesText = useCallback(async (nextRulesText: string) => {
120
  setRulesText(nextRulesText)
121
  const nextValidation = await api.validateRules(nextRulesText)
frontend/src/modules/detector/DetectorPanel.tsx CHANGED
@@ -1,5 +1,5 @@
1
  import { useRef, useState } from "react"
2
- import { ChevronDown, Play, RotateCcw, Upload, Loader2 } from "lucide-react"
3
  import { useDashboard } from "@/lib/dashboard"
4
  import { Button } from "@/components/ui/button"
5
  import { Label } from "@/components/ui/label"
@@ -58,6 +58,20 @@ export function DetectorPanel() {
58
  const fileRef = useRef<HTMLInputElement>(null)
59
  const [dragging, setDragging] = useState(false)
60
  const [advanced, setAdvanced] = useState(false)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
62
  return (
63
  <div className="space-y-5">
@@ -107,6 +121,17 @@ export function DetectorPanel() {
107
  )}
108
  </div>
109
 
 
 
 
 
 
 
 
 
 
 
 
110
  <div className="space-y-2">
111
  <Label htmlFor="classes">Open-vocabulary classes</Label>
112
  <Textarea
 
1
  import { useRef, useState } from "react"
2
+ import { ChevronDown, Play, RotateCcw, Upload, Loader2, Clapperboard } from "lucide-react"
3
  import { useDashboard } from "@/lib/dashboard"
4
  import { Button } from "@/components/ui/button"
5
  import { Label } from "@/components/ui/label"
 
58
  const fileRef = useRef<HTMLInputElement>(null)
59
  const [dragging, setDragging] = useState(false)
60
  const [advanced, setAdvanced] = useState(false)
61
+ const [loadingDemo, setLoadingDemo] = useState(false)
62
+
63
+ async function loadDemo() {
64
+ setLoadingDemo(true)
65
+ try {
66
+ const r = await fetch("/demo-video")
67
+ const blob = await r.blob()
68
+ setVideo(new File([blob], "tiny-trigger-demo.mp4", { type: "video/mp4" }))
69
+ } catch {
70
+ // silently ignore if demo not available
71
+ } finally {
72
+ setLoadingDemo(false)
73
+ }
74
+ }
75
 
76
  return (
77
  <div className="space-y-5">
 
121
  )}
122
  </div>
123
 
124
+ <Button
125
+ variant="outline"
126
+ size="sm"
127
+ className="w-full gap-2 text-muted-foreground"
128
+ onClick={loadDemo}
129
+ disabled={loadingDemo}
130
+ >
131
+ {loadingDemo ? <Loader2 className="size-3.5 animate-spin" /> : <Clapperboard className="size-3.5" />}
132
+ Use demo video
133
+ </Button>
134
+
135
  <div className="space-y-2">
136
  <Label htmlFor="classes">Open-vocabulary classes</Label>
137
  <Textarea
server.py CHANGED
@@ -419,6 +419,14 @@ def media(file_path: str) -> Any:
419
  return FileResponse(target, media_type="video/mp4")
420
 
421
 
 
 
 
 
 
 
 
 
422
  if __name__ == "__main__":
423
  app.launch(
424
  server_name=os.getenv("GRADIO_SERVER_NAME", "0.0.0.0"),
 
419
  return FileResponse(target, media_type="video/mp4")
420
 
421
 
422
+ @app.get("/demo-video")
423
+ def demo_video() -> Any:
424
+ path = ROOT / "tiny-trigger-demo.mp4"
425
+ if not path.is_file():
426
+ return JSONResponse({"error": "demo video not found"}, status_code=404)
427
+ return FileResponse(path, media_type="video/mp4")
428
+
429
+
430
  if __name__ == "__main__":
431
  app.launch(
432
  server_name=os.getenv("GRADIO_SERVER_NAME", "0.0.0.0"),