gitcast 1.0.17__tar.gz → 1.0.18__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {gitcast-1.0.17/gitcast.egg-info → gitcast-1.0.18}/PKG-INFO +1 -1
- {gitcast-1.0.17 → gitcast-1.0.18}/ai/prompts.py +1 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/api/payload.py +35 -29
- {gitcast-1.0.17 → gitcast-1.0.18}/api/routes.py +5 -1
- {gitcast-1.0.17 → gitcast-1.0.18/gitcast.egg-info}/PKG-INFO +1 -1
- {gitcast-1.0.17 → gitcast-1.0.18}/setup.py +1 -1
- gitcast-1.0.18/ui/popup.py +165 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/web/index.html +31 -1
- gitcast-1.0.17/ui/popup.py +0 -12
- {gitcast-1.0.17 → gitcast-1.0.18}/.env.example +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/LICENSE +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/MANIFEST.in +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/README.md +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/ai/__init__.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/ai/formatter.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/ai/generator.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/ai/viral_patterns.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/api/__init__.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/api/analytics.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/api/auth.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/api/auth_middleware.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/api/auth_routes.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/api/monitoring.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/api/ratelimit.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/api/server.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/api/validators.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/assets/__init__.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/assets/favicon-16x16.png +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/assets/favicon-32x32.png +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/assets/favicon-64x64.png +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/assets/favicon.ico +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/assets/icon.png +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/cli/.env.example +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/cli/__init__.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/cli/gitcast.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/config/__init__.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/config/settings.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/core/__init__.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/core/capture.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/core/codebase_reader.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/core/framing.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/core/hotkey.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/core/log_stream.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/core/ocr.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/core/screenshot_session.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/core/security.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/core/tray.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/core/trigger.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/docs/api-keys.md +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/docs/article-generator.md +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/docs/capture-flow.md +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/docs/changelog.md +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/docs/contributing.md +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/docs/dashboard.md +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/docs/insights.md +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/docs/introduction.md +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/docs/post-formats.md +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/docs/providers.md +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/docs/pypi-errors.md +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/docs/quickstart.md +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/docs/sprint-mode.md +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/gitcast.egg-info/SOURCES.txt +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/gitcast.egg-info/dependency_links.txt +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/gitcast.egg-info/entry_points.txt +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/gitcast.egg-info/requires.txt +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/gitcast.egg-info/top_level.txt +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/publisher/__init__.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/publisher/clipboard.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/publisher/twitter.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/pyproject.toml +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/requirements.txt +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/setup.cfg +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/storage/__init__.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/storage/cleanup.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/storage/engagement.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/storage/insights.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/storage/key_manager.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/storage/logger.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/storage/metrics.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/storage/sprint.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/storage/streak.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/storage/supabase_client.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/storage/tone_memory.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/tests/test_keys.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/tests/test_ocr_fix.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/ui/__init__.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/ui/review.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/web/__init__.py +0 -0
- {gitcast-1.0.17 → gitcast-1.0.18}/web/landing.html +0 -0
|
@@ -54,6 +54,7 @@ Rules:
|
|
|
54
54
|
or the developer's raw thought. If the available
|
|
55
55
|
context is thin or unclear, write a short, honest,
|
|
56
56
|
low-key post rather than fabricating details.
|
|
57
|
+
- If a git diff is provided, it is the primary source of truth. Screen text is secondary and may be noisy — ignore any browser UI text.
|
|
57
58
|
- If OCR text looks like browser UI, tab names, or
|
|
58
59
|
garbled text rather than code, IGNORE it completely
|
|
59
60
|
and rely only on the raw thought and git diff.
|
|
@@ -104,13 +104,6 @@ def _build_user_message(
|
|
|
104
104
|
if raw_thought.strip():
|
|
105
105
|
parts.append(f"## Developer's raw thought\n{raw_thought.strip()}")
|
|
106
106
|
|
|
107
|
-
parts.append(
|
|
108
|
-
"## Priority\n"
|
|
109
|
-
"If a git diff is present below, treat it as "
|
|
110
|
-
"the primary source of truth for what changed. "
|
|
111
|
-
"Screen text is secondary context only."
|
|
112
|
-
)
|
|
113
|
-
|
|
114
107
|
# git diff section comes first
|
|
115
108
|
diff_text = git_diff.get("diff", "") if isinstance(git_diff, dict) else ""
|
|
116
109
|
if diff_text:
|
|
@@ -118,13 +111,6 @@ def _build_user_message(
|
|
|
118
111
|
|
|
119
112
|
# check if OCR text is fragmented
|
|
120
113
|
ocr_text = "\n\n".join([s.get("ocr_text", "") for s in screenshots if s.get("ocr_text")]).strip()
|
|
121
|
-
if len(ocr_text.strip()) < 80:
|
|
122
|
-
parts.append(
|
|
123
|
-
"## Note\n"
|
|
124
|
-
"Screen context is fragmented/unreliable — "
|
|
125
|
-
"do not base the post on it. Rely on the "
|
|
126
|
-
"developer's raw thought and git diff only."
|
|
127
|
-
)
|
|
128
114
|
|
|
129
115
|
# OCR section comes second, clearly labeled
|
|
130
116
|
if ocr_text and not use_vision:
|
|
@@ -208,35 +194,55 @@ def _encode_image(image_path: str) -> str:
|
|
|
208
194
|
|
|
209
195
|
# ── Payload validator ─────────────────────────────────────────────────────────
|
|
210
196
|
|
|
197
|
+
GENERIC_THOUGHTS = [
|
|
198
|
+
"captured via hotkey trigger",
|
|
199
|
+
"",
|
|
200
|
+
"test",
|
|
201
|
+
"testing",
|
|
202
|
+
"...",
|
|
203
|
+
"thought",
|
|
204
|
+
]
|
|
205
|
+
|
|
211
206
|
def validate_payload(payload: dict) -> tuple[bool, list[str]]:
|
|
212
207
|
"""
|
|
213
208
|
Checks the payload has the minimum viable content to generate a post.
|
|
214
209
|
Returns (is_valid, list_of_warnings).
|
|
215
210
|
"""
|
|
216
211
|
warnings = []
|
|
212
|
+
|
|
217
213
|
raw_thought = payload.get("raw_thought", "").strip()
|
|
218
214
|
ocr_text = payload.get("ocr_text", "").strip()
|
|
215
|
+
git_diff = payload.get("git_diff", "").strip()
|
|
219
216
|
git_diff_available = payload.get("git_diff_available", False)
|
|
220
217
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
]
|
|
225
|
-
is_generic = raw_thought.lower() in generic_thoughts
|
|
226
|
-
|
|
227
|
-
has_real_content = (
|
|
228
|
-
not is_generic or
|
|
229
|
-
len(ocr_text) > 100 or
|
|
230
|
-
git_diff_available
|
|
218
|
+
is_generic_thought = (
|
|
219
|
+
raw_thought.lower() in GENERIC_THOUGHTS or
|
|
220
|
+
len(raw_thought) < 5
|
|
231
221
|
)
|
|
222
|
+
has_real_ocr = len(ocr_text) >= 80
|
|
223
|
+
has_diff = git_diff_available and len(git_diff) > 20
|
|
224
|
+
|
|
225
|
+
# BLOCK generation if all three are weak
|
|
226
|
+
if is_generic_thought and not has_real_ocr and not has_diff:
|
|
227
|
+
return False, [
|
|
228
|
+
"Not enough context to generate a post. Please:\n"
|
|
229
|
+
"1. Capture from your code editor (not a browser)\n"
|
|
230
|
+
"2. Type a real thought about what you just built or fixed\n"
|
|
231
|
+
"3. Make sure you have uncommitted git changes for best results"
|
|
232
|
+
]
|
|
233
|
+
|
|
234
|
+
# WARN but allow if thought is real but diff missing
|
|
235
|
+
if not has_diff:
|
|
236
|
+
warnings.append(
|
|
237
|
+
"No git diff — post will rely on your thought and screen context only."
|
|
238
|
+
)
|
|
232
239
|
|
|
233
|
-
if not
|
|
240
|
+
if not raw_thought or is_generic_thought:
|
|
234
241
|
warnings.append(
|
|
235
|
-
"
|
|
236
|
-
|
|
242
|
+
"Generic thought detected — post quality will be lower. Try describing what you actually fixed or built."
|
|
243
|
+
)
|
|
237
244
|
|
|
238
|
-
|
|
239
|
-
return is_valid, warnings
|
|
245
|
+
return True, warnings
|
|
240
246
|
|
|
241
247
|
|
|
242
248
|
# ── Test ──────────────────────────────────────────────────────────────────────
|
|
@@ -789,7 +789,11 @@ async def generate(request: Request, body: GenerateRequest, user_id: str = Depen
|
|
|
789
789
|
if not is_valid:
|
|
790
790
|
raise HTTPException(
|
|
791
791
|
status_code=400,
|
|
792
|
-
detail=
|
|
792
|
+
detail={
|
|
793
|
+
"error": "insufficient_context",
|
|
794
|
+
"message": warnings[0] if warnings else "Not enough context.",
|
|
795
|
+
"warnings": warnings,
|
|
796
|
+
}
|
|
793
797
|
)
|
|
794
798
|
|
|
795
799
|
try:
|
|
@@ -51,7 +51,7 @@ if os.path.exists(requirements_path):
|
|
|
51
51
|
|
|
52
52
|
setup(
|
|
53
53
|
name="gitcast",
|
|
54
|
-
version="1.0.
|
|
54
|
+
version="1.0.18",
|
|
55
55
|
description="git diff → published post. under 60 seconds.",
|
|
56
56
|
long_description=long_description,
|
|
57
57
|
long_description_content_type="text/markdown",
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import tkinter as tk
|
|
2
|
+
from tkinter import font as tkfont
|
|
3
|
+
import threading
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# ── Constants ─────────────────────────────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
POPUP_WIDTH = 520
|
|
9
|
+
POPUP_HEIGHT = 120
|
|
10
|
+
BG_COLOR = "#1a1a1a"
|
|
11
|
+
BORDER_COLOR = "#333333"
|
|
12
|
+
TEXT_COLOR = "#f0f0f0"
|
|
13
|
+
PLACEHOLDER_COLOR = "#555555"
|
|
14
|
+
ACCENT_COLOR = "#6366f1" # indigo — matches tray icon
|
|
15
|
+
FONT_FAMILY = "Segoe UI"
|
|
16
|
+
FONT_SIZE = 13
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# ── Popup window ──────────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
class CapturePopup:
|
|
22
|
+
def __init__(self, on_submit, on_dismiss):
|
|
23
|
+
"""
|
|
24
|
+
Args:
|
|
25
|
+
on_submit: Callback called with the user's raw thought string.
|
|
26
|
+
on_dismiss: Callback called when the user hits Esc or closes.
|
|
27
|
+
"""
|
|
28
|
+
self.on_submit = on_submit
|
|
29
|
+
self.on_dismiss = on_dismiss
|
|
30
|
+
self.result = None
|
|
31
|
+
self.root = None
|
|
32
|
+
|
|
33
|
+
def show(self):
|
|
34
|
+
"""Build and display the popup. Blocks until submitted or dismissed."""
|
|
35
|
+
self.root = tk.Tk()
|
|
36
|
+
self.root.withdraw() # hide while building to prevent flicker
|
|
37
|
+
|
|
38
|
+
self._configure_window()
|
|
39
|
+
self._build_ui()
|
|
40
|
+
self._center_on_screen()
|
|
41
|
+
self._bind_keys()
|
|
42
|
+
|
|
43
|
+
self.root.deiconify() # show fully built window
|
|
44
|
+
self.root.focus_force()
|
|
45
|
+
self.entry.focus_set()
|
|
46
|
+
|
|
47
|
+
self.root.mainloop()
|
|
48
|
+
|
|
49
|
+
def _configure_window(self):
|
|
50
|
+
self.root.title("")
|
|
51
|
+
self.root.configure(bg=BG_COLOR)
|
|
52
|
+
self.root.overrideredirect(True) # removes title bar and border
|
|
53
|
+
self.root.attributes("-topmost", True) # float above all windows
|
|
54
|
+
self.root.resizable(False, False)
|
|
55
|
+
|
|
56
|
+
# draw a 1px border using a frame
|
|
57
|
+
self.root.configure(highlightbackground=BORDER_COLOR, highlightthickness=1)
|
|
58
|
+
|
|
59
|
+
def _build_ui(self):
|
|
60
|
+
main_frame = tk.Frame(self.root, bg=BG_COLOR, padx=20, pady=16)
|
|
61
|
+
main_frame.pack(fill=tk.BOTH, expand=True)
|
|
62
|
+
|
|
63
|
+
# label
|
|
64
|
+
label_font = tkfont.Font(family=FONT_FAMILY, size=10)
|
|
65
|
+
label = tk.Label(
|
|
66
|
+
main_frame,
|
|
67
|
+
text="What was the struggle or win?",
|
|
68
|
+
bg=BG_COLOR,
|
|
69
|
+
fg=PLACEHOLDER_COLOR,
|
|
70
|
+
font=label_font,
|
|
71
|
+
anchor="w",
|
|
72
|
+
)
|
|
73
|
+
label.pack(fill=tk.X, pady=(0, 8))
|
|
74
|
+
|
|
75
|
+
# accent line under label
|
|
76
|
+
accent_line = tk.Frame(main_frame, bg=ACCENT_COLOR, height=1)
|
|
77
|
+
accent_line.pack(fill=tk.X, pady=(0, 10))
|
|
78
|
+
|
|
79
|
+
# text input
|
|
80
|
+
entry_font = tkfont.Font(family=FONT_FAMILY, size=FONT_SIZE)
|
|
81
|
+
self.entry = tk.Entry(
|
|
82
|
+
main_frame,
|
|
83
|
+
font=entry_font,
|
|
84
|
+
bg=BG_COLOR,
|
|
85
|
+
fg=TEXT_COLOR,
|
|
86
|
+
insertbackground=ACCENT_COLOR, # cursor color
|
|
87
|
+
relief=tk.FLAT,
|
|
88
|
+
bd=0,
|
|
89
|
+
highlightthickness=0,
|
|
90
|
+
)
|
|
91
|
+
self.entry.pack(fill=tk.X)
|
|
92
|
+
|
|
93
|
+
# hint text at bottom
|
|
94
|
+
hint_font = tkfont.Font(family=FONT_FAMILY, size=9)
|
|
95
|
+
hint = tk.Label(
|
|
96
|
+
main_frame,
|
|
97
|
+
text="Enter to capture · Esc to cancel",
|
|
98
|
+
bg=BG_COLOR,
|
|
99
|
+
fg=PLACEHOLDER_COLOR,
|
|
100
|
+
font=hint_font,
|
|
101
|
+
anchor="e",
|
|
102
|
+
)
|
|
103
|
+
hint.pack(fill=tk.X, pady=(10, 0))
|
|
104
|
+
|
|
105
|
+
def _center_on_screen(self):
|
|
106
|
+
self.root.update_idletasks()
|
|
107
|
+
screen_w = self.root.winfo_screenwidth()
|
|
108
|
+
screen_h = self.root.winfo_screenheight()
|
|
109
|
+
x = (screen_w - POPUP_WIDTH) // 2
|
|
110
|
+
y = int(screen_h * 0.38) # slightly above center — feels more natural
|
|
111
|
+
self.root.geometry(f"{POPUP_WIDTH}x{POPUP_HEIGHT}+{x}+{y}")
|
|
112
|
+
|
|
113
|
+
def _bind_keys(self):
|
|
114
|
+
self.root.bind("<Return>", self._handle_submit)
|
|
115
|
+
self.root.bind("<Escape>", self._handle_dismiss)
|
|
116
|
+
self.root.bind("<FocusOut>", self._handle_focus_out)
|
|
117
|
+
self.root.protocol("WM_DELETE_WINDOW", self._handle_dismiss)
|
|
118
|
+
|
|
119
|
+
def _handle_submit(self, event=None):
|
|
120
|
+
text = self.entry.get().strip()
|
|
121
|
+
if not text:
|
|
122
|
+
# shake the entry to signal empty input
|
|
123
|
+
self._shake()
|
|
124
|
+
return
|
|
125
|
+
self.result = text
|
|
126
|
+
self.root.destroy()
|
|
127
|
+
self.on_submit(text)
|
|
128
|
+
|
|
129
|
+
def _handle_dismiss(self, event=None):
|
|
130
|
+
self.root.destroy()
|
|
131
|
+
self.on_dismiss()
|
|
132
|
+
|
|
133
|
+
def _handle_focus_out(self, event=None):
|
|
134
|
+
"""Dismiss if user clicks away from the popup."""
|
|
135
|
+
self.root.destroy()
|
|
136
|
+
self.on_dismiss()
|
|
137
|
+
|
|
138
|
+
def _shake(self):
|
|
139
|
+
"""Briefly shakes the window to signal empty input."""
|
|
140
|
+
x = self.root.winfo_x()
|
|
141
|
+
y = self.root.winfo_y()
|
|
142
|
+
for delta in [6, -6, 4, -4, 2, -2, 0]:
|
|
143
|
+
self.root.geometry(f"{POPUP_WIDTH}x{POPUP_HEIGHT}+{x + delta}+{y}")
|
|
144
|
+
self.root.update()
|
|
145
|
+
self.root.after(18)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
# ── Public interface ──────────────────────────────────────────────────────────
|
|
149
|
+
|
|
150
|
+
def show_popup(on_submit, on_dismiss):
|
|
151
|
+
"""
|
|
152
|
+
Shows the capture popup in a new thread so it doesn't block
|
|
153
|
+
the hotkey listener or the tray process.
|
|
154
|
+
"""
|
|
155
|
+
def run():
|
|
156
|
+
try:
|
|
157
|
+
popup = CapturePopup(on_submit=on_submit, on_dismiss=on_dismiss)
|
|
158
|
+
popup.show()
|
|
159
|
+
except Exception as e:
|
|
160
|
+
print(f"[Gitcast] Popup display error: {e}")
|
|
161
|
+
on_dismiss()
|
|
162
|
+
|
|
163
|
+
thread = threading.Thread(target=run, daemon=True)
|
|
164
|
+
thread.start()
|
|
165
|
+
return thread
|
|
@@ -2637,6 +2637,17 @@
|
|
|
2637
2637
|
}
|
|
2638
2638
|
if (!res.ok) {
|
|
2639
2639
|
const text = await res.text();
|
|
2640
|
+
let errorData = {};
|
|
2641
|
+
try {
|
|
2642
|
+
errorData = JSON.parse(text);
|
|
2643
|
+
} catch(e) {}
|
|
2644
|
+
const detail = errorData.detail || {};
|
|
2645
|
+
const error = typeof detail === 'object' ? (detail.error || '') : (detail || '');
|
|
2646
|
+
|
|
2647
|
+
if (error.includes('insufficient_context') || res.status === 400) {
|
|
2648
|
+
showContextWarning(detail.message || errorData.message || error || text);
|
|
2649
|
+
return null;
|
|
2650
|
+
}
|
|
2640
2651
|
throw new Error(text || `Request failed with status ${res.status}`);
|
|
2641
2652
|
}
|
|
2642
2653
|
return res.json();
|
|
@@ -4989,7 +5000,15 @@
|
|
|
4989
5000
|
}
|
|
4990
5001
|
}
|
|
4991
5002
|
|
|
4992
|
-
function appendLog(entry) {
|
|
5003
|
+
function appendLog(entry, level = null, message = null) {
|
|
5004
|
+
if (typeof entry === 'string') {
|
|
5005
|
+
entry = {
|
|
5006
|
+
module: entry,
|
|
5007
|
+
level: level,
|
|
5008
|
+
message: message,
|
|
5009
|
+
time: new Date().toLocaleTimeString()
|
|
5010
|
+
};
|
|
5011
|
+
}
|
|
4993
5012
|
state.logs.push(entry);
|
|
4994
5013
|
if (state.logs.length > 50) state.logs = state.logs.slice(-50);
|
|
4995
5014
|
if (state.logsOpen) {
|
|
@@ -4997,6 +5016,17 @@
|
|
|
4997
5016
|
}
|
|
4998
5017
|
}
|
|
4999
5018
|
|
|
5019
|
+
function showContextWarning(message) {
|
|
5020
|
+
const editor = document.getElementById('post-editor') || document.getElementById('main-editor');
|
|
5021
|
+
if (editor) {
|
|
5022
|
+
editor.value = '';
|
|
5023
|
+
editor.placeholder = message ||
|
|
5024
|
+
'Not enough context — capture from ' +
|
|
5025
|
+
'your code editor and type a real thought.';
|
|
5026
|
+
}
|
|
5027
|
+
appendLog('GENERATOR', 'WARN', 'generation blocked — thin context');
|
|
5028
|
+
}
|
|
5029
|
+
|
|
5000
5030
|
function renderLogs() {
|
|
5001
5031
|
const body = document.getElementById('logs-body');
|
|
5002
5032
|
if (!body) return;
|
gitcast-1.0.17/ui/popup.py
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
def show_popup(on_submit, on_dismiss):
|
|
2
|
-
"""
|
|
3
|
-
Tkinter popup fallback/mock.
|
|
4
|
-
Immediately submits a default thought to run the pipeline.
|
|
5
|
-
"""
|
|
6
|
-
import threading
|
|
7
|
-
|
|
8
|
-
def run():
|
|
9
|
-
print("[Gitcast] Mock popup triggered.")
|
|
10
|
-
on_submit("Captured via hotkey trigger")
|
|
11
|
-
|
|
12
|
-
threading.Thread(target=run, daemon=True).start()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|