Skip to content

Commit ae72d27

Browse files
committed
Add default observables and config connected button
1 parent 05e2775 commit ae72d27

22 files changed

Lines changed: 555 additions & 239 deletions

package-lock.json

Lines changed: 30 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@
5252
"tailwind-merge": "3.3.1",
5353
"tailwindcss": "4.1.11",
5454
"vaul": "^1.1.2",
55-
"zod": "^4.0.15"
55+
"zod": "^4.0.15",
56+
"zustand": "^5.0.7"
5657
},
5758
"devDependencies": {
5859
"@tanstack/eslint-plugin-query": "^5.83.1",

src-lua/feather/init.lua

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,26 @@ local customErrorHandler = errorhandler
1919
function Feather:init(config)
2020
local conf = config or {}
2121
self.pages = {}
22+
self.debug = conf.debug or false
2223
self.host = conf.host or "*"
2324
self.baseDir = conf.baseDir or ""
2425
self.port = conf.port or 4004
25-
self.wrapPrint = conf.wrapPrint or true
26-
self.timestamp = conf.timestamp or true
26+
self.wrapPrint = conf.wrapPrint or false
2727
self.whitelist = conf.whitelist or { "127.0.0.1" }
2828
self.maxTempLogs = conf.maxTempLogs or 200
2929
self.updateInterval = conf.updateInterval or 0.1
30+
self.defaultObservers = conf.defaultObservers or true
3031
---TODO: find a better way to ensure that the error handler is called, maybe a thread?
31-
self.errorWait = conf.errorWait or 10
32+
self.errorWait = conf.errorWait or 3
3233
self.autoRegisterErrorHandler = conf.autoRegisterErrorHandler and true or false
3334
self.plugins = conf.plugins or {}
3435
self.lastDelivery = 0
36+
self.observers = {}
37+
38+
if not self.debug then
39+
return
40+
end
41+
3542
customErrorHandler = conf.errorHandler or errorhandler
3643

3744
local server = assert(socket.bind(self.host, self.port))
@@ -55,7 +62,7 @@ function Feather:init(config)
5562

5663
local start = love.timer.getTime()
5764
while not delivered and (love.timer.getTime() - start) < selfRef.errorWait do
58-
selfRef:update()
65+
selfRef:update(0)
5966
delivered = isDelivered()
6067
love.timer.sleep(self.updateInterval)
6168
end
@@ -135,6 +142,33 @@ function Feather:__isInWhitelist(addr)
135142
return false
136143
end
137144

145+
function Feather:__defaultObservers()
146+
self:observe("Global", _G)
147+
self:observe("Lua Version", _VERSION)
148+
end
149+
150+
--- Tracks the value of a key in the observers table
151+
---@param key string
152+
---@param value any
153+
function Feather:observe(key, value)
154+
if not self.debug then
155+
return
156+
end
157+
158+
self.observers = self.observers or {}
159+
160+
local curr = self:__format(value)
161+
162+
for i, observer in ipairs(self.observers) do
163+
if observer.key == key then
164+
observer.value = curr
165+
return
166+
end
167+
end
168+
169+
table.insert(self.observers, { key = key, value = curr })
170+
end
171+
138172
function Feather:__errorTraceback(msg)
139173
local trace = debug.traceback()
140174

@@ -179,6 +213,10 @@ function Feather:finish()
179213
end
180214

181215
function Feather:onerror(msg, finish)
216+
if not self.debug then
217+
return
218+
end
219+
182220
local err = self:__errorTraceback(msg)
183221
self:log({ type = "error", str = self:__errorTraceback(msg) })
184222
if self.wrapPrint then
@@ -193,6 +231,10 @@ end
193231

194232
---@param dt number
195233
function Feather:update(dt)
234+
if not self.debug then
235+
return
236+
end
237+
196238
local client = self.server:accept()
197239
if client then
198240
if #logs == 0 then
@@ -233,6 +275,14 @@ function Feather:update(dt)
233275
response = self:__buildResponse(body)
234276
end
235277

278+
if request.path == "/observers" then
279+
if self.defaultObservers then
280+
self:__defaultObservers()
281+
end
282+
local body = json.encode(self.observers)
283+
response = self:__buildResponse(body)
284+
end
285+
236286
client:send(response)
237287
end
238288

@@ -241,6 +291,10 @@ function Feather:update(dt)
241291
end
242292

243293
function Feather:trace(...)
294+
if not self.debug then
295+
return
296+
end
297+
244298
local str = "[Feather] " .. self:__format(...)
245299
self.logger(str)
246300
if not self.wrapPrint then
@@ -249,6 +303,10 @@ function Feather:trace(...)
249303
end
250304

251305
function Feather:log(line)
306+
if not self.debug then
307+
return
308+
end
309+
252310
line.id = tostring(os.time()) .. "-" .. tostring(#logs + 1)
253311
line.time = os.time()
254312
line.count = 1
@@ -263,6 +321,10 @@ function Feather:log(line)
263321
end
264322

265323
function Feather:print(...)
324+
if not self.debug then
325+
return
326+
end
327+
266328
local str = self:__format(...)
267329
local last = logs[#logs]
268330
if last and str == last.str then

src-lua/main.lua

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ local debugger = FeatherDebugger({
144144
errorHandler = customerrorhandler,
145145
autoRegisterErrorHandler = true,
146146
baseDir = "src-lua",
147+
debug = true,
147148
})
148149

149150
a = 0
@@ -159,6 +160,7 @@ function love.update(dt)
159160
if a > 1 then
160161
print(a)
161162
a = 0
163+
debugger:observe("a", a)
162164
print(math.random(1, 2))
163165
print({
164166
type = "feather:finish",
@@ -196,6 +198,43 @@ function love.update(dt)
196198
},
197199
},
198200
})
201+
202+
debugger:observe("object", {
203+
type = "feather:finish",
204+
body = {
205+
lol = "hey",
206+
another = {
207+
one = 1,
208+
copy = {
209+
type = "feather:finish",
210+
body = {
211+
lol = "hey",
212+
another = {
213+
one = 1,
214+
},
215+
},
216+
of_a = {
217+
type = "feather:finish",
218+
body = {
219+
lol = "hey",
220+
another = {
221+
one = 1,
222+
},
223+
},
224+
copy = {
225+
type = "feather:finish",
226+
body = {
227+
lol = "hey",
228+
another = {
229+
one = 1,
230+
},
231+
},
232+
},
233+
},
234+
},
235+
},
236+
},
237+
})
199238
end
200239
end
201240

src/components/app-sidebar.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import * as React from "react";
22

3-
import { NavPlugins } from "@/components/nav-plugins";
3+
// import { NavPlugins } from "@/components/nav-plugins";
44
import { NavMain } from "@/components/nav-main";
5-
import { NavSecondary } from "@/components/nav-secondary";
5+
// import { NavSecondary } from "@/components/nav-secondary";
66
import {
77
Sidebar,
88
SidebarContent,
@@ -99,8 +99,8 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
9999
</SidebarHeader>
100100
<SidebarContent>
101101
<NavMain items={data.navMain} />
102-
<NavPlugins items={data.documents} />
103-
<NavSecondary items={data.navSecondary} className="mt-auto" />
102+
{/* <NavPlugins items={data.documents} />
103+
<NavSecondary items={data.navSecondary} className="mt-auto" /> */}
104104
</SidebarContent>
105105
</Sidebar>
106106
);

src/components/code.tsx

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import SyntaxHighlighter from "react-syntax-highlighter";
2+
import { ScrollArea } from "@/components/ui/scroll-area";
3+
import oneLight from "@/assets/theme/light";
4+
import { cn } from "@/lib/utils";
5+
6+
export function LuaBlock({
7+
code,
8+
className,
9+
}: {
10+
code: string;
11+
className?: string;
12+
}) {
13+
return (
14+
<ScrollArea className="mt-2 w-full rounded border bg-muted p-2 font-mono text-xs">
15+
<div className={cn("max-h-64", className)}>
16+
<SyntaxHighlighter
17+
wrapLines
18+
language="lua"
19+
// @ts-ignore
20+
style={oneLight}
21+
showLineNumbers
22+
>
23+
{code}
24+
</SyntaxHighlighter>
25+
</div>
26+
</ScrollArea>
27+
);
28+
}
29+
30+
export function TraceViewer({
31+
onFileClick,
32+
trace,
33+
basePath,
34+
}: {
35+
onFileClick?: (file: string, line?: number) => void;
36+
trace: string;
37+
basePath: string;
38+
}) {
39+
const highlightLine = (line: string, index: number) => {
40+
// Clickable file:line pattern
41+
const filePattern = /([\w./\\-]+\.lua):(\d+)/g;
42+
// "in function"
43+
const inFunctionPattern = /\bin function\b/g;
44+
// 'functionName'
45+
const quotedPattern = /'([^']+)'/g;
46+
47+
let html = line
48+
.replace(
49+
filePattern,
50+
(_, file, lineNum) =>
51+
`<a href="vscode://file/${basePath}/${file}:${lineNum}" class="text-blue-500 underline">${file}:${lineNum}</a>`
52+
)
53+
.replace(
54+
inFunctionPattern,
55+
`<span class="text-purple-500 font-medium">in function</span>`
56+
)
57+
.replace(quotedPattern, `<span class="text-green-500">'$1'</span>`);
58+
59+
return (
60+
<div
61+
key={index}
62+
className="whitespace-pre-wrap break-words"
63+
dangerouslySetInnerHTML={{ __html: html }}
64+
onClick={(e) => {
65+
const target = e.target as HTMLElement;
66+
if (target.tagName === "A" && target.dataset.file) {
67+
e.preventDefault();
68+
onFileClick?.(
69+
target.dataset.file,
70+
target.dataset.line ? parseInt(target.dataset.line) : undefined
71+
);
72+
}
73+
}}
74+
/>
75+
);
76+
};
77+
78+
return (
79+
<div className="font-mono text-xs space-y-1">
80+
{trace.split("\n").map(highlightLine)}
81+
</div>
82+
);
83+
}

0 commit comments

Comments
 (0)