gitcast 1.0.7__tar.gz → 1.0.9__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.
Files changed (89) hide show
  1. {gitcast-1.0.7/gitcast.egg-info → gitcast-1.0.9}/PKG-INFO +1 -1
  2. {gitcast-1.0.7 → gitcast-1.0.9}/cli/gitcast.py +0 -1
  3. gitcast-1.0.9/core/trigger.py +115 -0
  4. {gitcast-1.0.7 → gitcast-1.0.9}/docs/pypi-errors.md +14 -52
  5. {gitcast-1.0.7 → gitcast-1.0.9/gitcast.egg-info}/PKG-INFO +1 -1
  6. {gitcast-1.0.7 → gitcast-1.0.9}/setup.py +1 -1
  7. {gitcast-1.0.7 → gitcast-1.0.9}/web/index.html +15 -8
  8. gitcast-1.0.7/core/trigger.py +0 -96
  9. {gitcast-1.0.7 → gitcast-1.0.9}/.env.example +0 -0
  10. {gitcast-1.0.7 → gitcast-1.0.9}/LICENSE +0 -0
  11. {gitcast-1.0.7 → gitcast-1.0.9}/MANIFEST.in +0 -0
  12. {gitcast-1.0.7 → gitcast-1.0.9}/README.md +0 -0
  13. {gitcast-1.0.7 → gitcast-1.0.9}/ai/__init__.py +0 -0
  14. {gitcast-1.0.7 → gitcast-1.0.9}/ai/formatter.py +0 -0
  15. {gitcast-1.0.7 → gitcast-1.0.9}/ai/generator.py +0 -0
  16. {gitcast-1.0.7 → gitcast-1.0.9}/ai/prompts.py +0 -0
  17. {gitcast-1.0.7 → gitcast-1.0.9}/ai/viral_patterns.py +0 -0
  18. {gitcast-1.0.7 → gitcast-1.0.9}/api/__init__.py +0 -0
  19. {gitcast-1.0.7 → gitcast-1.0.9}/api/analytics.py +0 -0
  20. {gitcast-1.0.7 → gitcast-1.0.9}/api/auth.py +0 -0
  21. {gitcast-1.0.7 → gitcast-1.0.9}/api/auth_middleware.py +0 -0
  22. {gitcast-1.0.7 → gitcast-1.0.9}/api/auth_routes.py +0 -0
  23. {gitcast-1.0.7 → gitcast-1.0.9}/api/monitoring.py +0 -0
  24. {gitcast-1.0.7 → gitcast-1.0.9}/api/payload.py +0 -0
  25. {gitcast-1.0.7 → gitcast-1.0.9}/api/ratelimit.py +0 -0
  26. {gitcast-1.0.7 → gitcast-1.0.9}/api/routes.py +0 -0
  27. {gitcast-1.0.7 → gitcast-1.0.9}/api/server.py +0 -0
  28. {gitcast-1.0.7 → gitcast-1.0.9}/api/validators.py +0 -0
  29. {gitcast-1.0.7 → gitcast-1.0.9}/assets/__init__.py +0 -0
  30. {gitcast-1.0.7 → gitcast-1.0.9}/assets/favicon-16x16.png +0 -0
  31. {gitcast-1.0.7 → gitcast-1.0.9}/assets/favicon-32x32.png +0 -0
  32. {gitcast-1.0.7 → gitcast-1.0.9}/assets/favicon-64x64.png +0 -0
  33. {gitcast-1.0.7 → gitcast-1.0.9}/assets/favicon.ico +0 -0
  34. {gitcast-1.0.7 → gitcast-1.0.9}/assets/icon.png +0 -0
  35. {gitcast-1.0.7 → gitcast-1.0.9}/cli/.env.example +0 -0
  36. {gitcast-1.0.7 → gitcast-1.0.9}/cli/__init__.py +0 -0
  37. {gitcast-1.0.7 → gitcast-1.0.9}/config/__init__.py +0 -0
  38. {gitcast-1.0.7 → gitcast-1.0.9}/config/settings.py +0 -0
  39. {gitcast-1.0.7 → gitcast-1.0.9}/core/__init__.py +0 -0
  40. {gitcast-1.0.7 → gitcast-1.0.9}/core/capture.py +0 -0
  41. {gitcast-1.0.7 → gitcast-1.0.9}/core/codebase_reader.py +0 -0
  42. {gitcast-1.0.7 → gitcast-1.0.9}/core/framing.py +0 -0
  43. {gitcast-1.0.7 → gitcast-1.0.9}/core/hotkey.py +0 -0
  44. {gitcast-1.0.7 → gitcast-1.0.9}/core/log_stream.py +0 -0
  45. {gitcast-1.0.7 → gitcast-1.0.9}/core/ocr.py +0 -0
  46. {gitcast-1.0.7 → gitcast-1.0.9}/core/screenshot_session.py +0 -0
  47. {gitcast-1.0.7 → gitcast-1.0.9}/core/security.py +0 -0
  48. {gitcast-1.0.7 → gitcast-1.0.9}/core/tray.py +0 -0
  49. {gitcast-1.0.7 → gitcast-1.0.9}/docs/api-keys.md +0 -0
  50. {gitcast-1.0.7 → gitcast-1.0.9}/docs/article-generator.md +0 -0
  51. {gitcast-1.0.7 → gitcast-1.0.9}/docs/capture-flow.md +0 -0
  52. {gitcast-1.0.7 → gitcast-1.0.9}/docs/changelog.md +0 -0
  53. {gitcast-1.0.7 → gitcast-1.0.9}/docs/contributing.md +0 -0
  54. {gitcast-1.0.7 → gitcast-1.0.9}/docs/dashboard.md +0 -0
  55. {gitcast-1.0.7 → gitcast-1.0.9}/docs/insights.md +0 -0
  56. {gitcast-1.0.7 → gitcast-1.0.9}/docs/introduction.md +0 -0
  57. {gitcast-1.0.7 → gitcast-1.0.9}/docs/post-formats.md +0 -0
  58. {gitcast-1.0.7 → gitcast-1.0.9}/docs/providers.md +0 -0
  59. {gitcast-1.0.7 → gitcast-1.0.9}/docs/quickstart.md +0 -0
  60. {gitcast-1.0.7 → gitcast-1.0.9}/docs/sprint-mode.md +0 -0
  61. {gitcast-1.0.7 → gitcast-1.0.9}/gitcast.egg-info/SOURCES.txt +0 -0
  62. {gitcast-1.0.7 → gitcast-1.0.9}/gitcast.egg-info/dependency_links.txt +0 -0
  63. {gitcast-1.0.7 → gitcast-1.0.9}/gitcast.egg-info/entry_points.txt +0 -0
  64. {gitcast-1.0.7 → gitcast-1.0.9}/gitcast.egg-info/requires.txt +0 -0
  65. {gitcast-1.0.7 → gitcast-1.0.9}/gitcast.egg-info/top_level.txt +0 -0
  66. {gitcast-1.0.7 → gitcast-1.0.9}/publisher/__init__.py +0 -0
  67. {gitcast-1.0.7 → gitcast-1.0.9}/publisher/clipboard.py +0 -0
  68. {gitcast-1.0.7 → gitcast-1.0.9}/publisher/twitter.py +0 -0
  69. {gitcast-1.0.7 → gitcast-1.0.9}/pyproject.toml +0 -0
  70. {gitcast-1.0.7 → gitcast-1.0.9}/requirements.txt +0 -0
  71. {gitcast-1.0.7 → gitcast-1.0.9}/setup.cfg +0 -0
  72. {gitcast-1.0.7 → gitcast-1.0.9}/storage/__init__.py +0 -0
  73. {gitcast-1.0.7 → gitcast-1.0.9}/storage/cleanup.py +0 -0
  74. {gitcast-1.0.7 → gitcast-1.0.9}/storage/engagement.py +0 -0
  75. {gitcast-1.0.7 → gitcast-1.0.9}/storage/insights.py +0 -0
  76. {gitcast-1.0.7 → gitcast-1.0.9}/storage/key_manager.py +0 -0
  77. {gitcast-1.0.7 → gitcast-1.0.9}/storage/logger.py +0 -0
  78. {gitcast-1.0.7 → gitcast-1.0.9}/storage/metrics.py +0 -0
  79. {gitcast-1.0.7 → gitcast-1.0.9}/storage/sprint.py +0 -0
  80. {gitcast-1.0.7 → gitcast-1.0.9}/storage/streak.py +0 -0
  81. {gitcast-1.0.7 → gitcast-1.0.9}/storage/supabase_client.py +0 -0
  82. {gitcast-1.0.7 → gitcast-1.0.9}/storage/tone_memory.py +0 -0
  83. {gitcast-1.0.7 → gitcast-1.0.9}/tests/test_keys.py +0 -0
  84. {gitcast-1.0.7 → gitcast-1.0.9}/tests/test_ocr_fix.py +0 -0
  85. {gitcast-1.0.7 → gitcast-1.0.9}/ui/__init__.py +0 -0
  86. {gitcast-1.0.7 → gitcast-1.0.9}/ui/popup.py +0 -0
  87. {gitcast-1.0.7 → gitcast-1.0.9}/ui/review.py +0 -0
  88. {gitcast-1.0.7 → gitcast-1.0.9}/web/__init__.py +0 -0
  89. {gitcast-1.0.7 → gitcast-1.0.9}/web/landing.html +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitcast
3
- Version: 1.0.7
3
+ Version: 1.0.9
4
4
  Summary: git diff → published post. under 60 seconds.
5
5
  Home-page: https://github.com/drizzy765/gitcast
6
6
  Author: Timilehin Agoro
@@ -69,7 +69,6 @@ def main():
69
69
 
70
70
  print("[OK] server running at http://localhost:8000")
71
71
  print("[OK] browser opened")
72
- print("[OK] hotkey: Ctrl+Shift+P")
73
72
 
74
73
  from core.tray import run_tray
75
74
  from core.trigger import on_trigger
@@ -0,0 +1,115 @@
1
+ import threading
2
+ from core.capture import run_capture
3
+ from core.ocr import run_ocr
4
+ from api.payload import build_payload
5
+ from ui.popup import show_popup
6
+ from config.settings import is_sprint_mode
7
+ from storage.sprint import log_sprint_capture
8
+ from ai.generator import generate_posts
9
+ from ui.review import show_review
10
+ from publisher.twitter import publish_post
11
+ from storage.logger import log_post
12
+ import asyncio
13
+ import webbrowser
14
+
15
+
16
+ def on_trigger():
17
+ """
18
+ Called every time the hotkey fires.
19
+ Full capture → generate → review flow.
20
+ """
21
+ print("[Trigger] on_trigger fired successfully")
22
+ print("[Gitcast] Hotkey fired — starting capture...")
23
+
24
+ capture = run_capture()
25
+ print("[Trigger] capture complete")
26
+ ocr = run_ocr(capture["screenshot"]["path"])
27
+ print("[Trigger] OCR complete")
28
+
29
+ if is_sprint_mode():
30
+ log_sprint_capture(
31
+ git_diff=capture["git_diff"].get("diff", ""),
32
+ ocr_text=ocr.get("text", ""),
33
+ raw_thought="",
34
+ timestamp=capture["screenshot"]["timestamp"],
35
+ )
36
+ print("[Gitcast] Sprint Mode — capture logged silently.")
37
+ return
38
+
39
+ def on_submit(raw_thought):
40
+ print("[Trigger] raw thought received")
41
+ print(f"[Gitcast] Raw thought: '{raw_thought}'")
42
+ payload = build_payload(
43
+ raw_thought=raw_thought,
44
+ ocr_result=ocr,
45
+ capture_result=capture,
46
+ )
47
+ payload["use_vision_fallback"] = False
48
+ payload["screenshot_b64"] = None
49
+
50
+ def generate_and_show():
51
+ try:
52
+ print('[Trigger] calling generate_posts...')
53
+ print("[Trigger] generating posts...")
54
+ variations = asyncio.run(generate_posts(payload))
55
+ print(f'[Trigger] got {len(variations)} variations')
56
+ print(f'[Trigger] keys: {list(variations.keys())}')
57
+ for k, v in variations.items():
58
+ print(f'[Trigger] {k}: {v[:50]}...')
59
+
60
+ print("[Trigger] opening review window")
61
+ webbrowser.open("http://localhost:8000")
62
+
63
+ def on_publish(post_text, format_key,
64
+ screenshot_path):
65
+ print(f"[Gitcast] Publishing: {format_key}")
66
+ result = publish_post(
67
+ post_text, screenshot_path)
68
+ if result.get("success"):
69
+ if result.get("fallback"):
70
+ print("[Gitcast] Copied to clipboard")
71
+ else:
72
+ print(f"[Gitcast] Published: "
73
+ f"{result.get('tweet_url')}")
74
+ log_post(
75
+ post_text=post_text,
76
+ format_key=format_key,
77
+ screenshot_path=screenshot_path,
78
+ tweet_url=result.get(
79
+ "tweet_url", ""),
80
+ tweet_id=result.get(
81
+ "tweet_id", ""),
82
+ fallback=result.get(
83
+ "fallback", False),
84
+ )
85
+ else:
86
+ print(f"[Gitcast] Error: "
87
+ f"{result.get('error')}")
88
+
89
+ def on_close():
90
+ print("[Gitcast] Review closed.")
91
+
92
+ show_review(
93
+ payload=payload,
94
+ variations=variations,
95
+ on_publish=on_publish,
96
+ on_close=on_close,
97
+ )
98
+ except Exception as e:
99
+ print(f'[Trigger] generation error: {e}')
100
+ import traceback
101
+ traceback.print_exc()
102
+
103
+ threading.Thread(
104
+ target=generate_and_show,
105
+ daemon=True
106
+ ).start()
107
+
108
+ def on_dismiss():
109
+ print("[Gitcast] Capture dismissed.")
110
+
111
+ print("[Trigger] popup showing")
112
+ show_popup(
113
+ on_submit=on_submit,
114
+ on_dismiss=on_dismiss
115
+ )
@@ -6,6 +6,20 @@ This document records the issues faced during the testing, packaging, and execut
6
6
 
7
7
  ## 1. Resolved Issues
8
8
 
9
+ ### Fixed in `gitcast==1.0.8`
10
+
11
+ #### Issue 2.3: Unauthorized (401) API Errors on localhost
12
+ * **Symptom**: Browser console logs API call failures: `api/keys/status:1 Failed to load resource: the server responded with a status of 401 (Unauthorized)`.
13
+ * **Root Cause**: On page load, the frontend checked if a token existed in `localStorage` and immediately verified it by querying `/api/keys/status`. When the server has restarted or the session has expired, the old token was no longer valid, causing the FastAPI backend to reject it with a `401 Unauthorized` response before the frontend could retrieve a fresh token.
14
+ * **Resolution**: Optimized `authGate` in `web/index.html` to check if the app is accessed on `localhost` (using `window.location.hostname`). If so, it auto-fetches the fresh session token directly from the server's `/api/token` endpoint *first* before verifying or falling back to `localStorage`. This avoids sending any request to `/api/keys/status` with an invalid/expired session token on startup, eliminating the console error.
15
+
16
+ ### Fixed in `gitcast==1.0.7`
17
+
18
+ #### Issue 2.2: ImportError and Pygame conflicts when running gitcast CLI from project directories containing `main.py`
19
+ * **Symptom**: Running `gitcast` from any project directory containing its own `main.py` (e.g., `snakegame/main.py`) causes the application to crash on startup after initializing the user's local modules (e.g., importing `pygame` and running game startup logic).
20
+ * **Root Cause**: In `cli/gitcast.py`, the script checked if `main.py` existed in the current working directory (`os.getcwd()`), and if so, prepended `os.getcwd()` to `sys.path` and attempted to run `from main import on_trigger`. This executed the user's local `main.py` and crashed when `on_trigger` was not defined.
21
+ * **Resolution**: Moved `on_trigger` to `core/trigger.py` and removed all local directory `sys.path` injection.
22
+
9
23
  ### Fixed in `gitcast==1.0.6`
10
24
 
11
25
  #### Issue 1.4: FastAPI Server Rate Limiting on Localhost
@@ -66,56 +80,4 @@ This document records the issues faced during the testing, packaging, and execut
66
80
  ```
67
81
  * **Status**: Logged and waiting for future triage. The application starts, but key bindings or endpoints triggers are failing to generate the actual post output.
68
82
 
69
- ### Issue 2.2: ImportError and Pygame conflicts when running gitcast CLI from project directories containing `main.py`
70
- * **Symptom**: Running `gitcast` from any project directory containing its own `main.py` (e.g., `snakegame/main.py`) causes the application to crash on startup after initializing the user's local modules (e.g., importing `pygame` and running game startup logic).
71
- * **Console Logs & Traceback**:
72
- ```text
73
- (venv) PS C:\Users\USER\Documents\snakegame> gitcast
74
- [Monitoring] Sentry not configured - skipping
75
-
76
- [Auth] Session Token: fe45cf73-a93b-4db3-a90a-f03dda41001c
77
- [Server] Starting Gitcast API on http://127.0.0.1:8000
78
-
79
- [Auth] Session Token: fe45cf73-a93b-4db3-a90a-f03dda41001c
80
- [Server] Starting Gitcast API on http://127.0.0.1:8000
81
- pygame 2.6.1 (SDL 2.28.4, Python 3.10.0)
82
- Hello from the pygame community. https://www.pygame.org/contribute.html
83
- Traceback (most recent call last):
84
- File "C:\Users\USER\AppData\Local\Programs\Python\Python310\lib\runpy.py", line 196, in _run_module_as_main
85
- return _run_code(code, main_globals, None,
86
- File "C:\Users\USER\AppData\Local\Programs\Python\Python310\lib\runpy.py", line 86, in _run_code
87
- exec(code, run_globals)
88
- File "C:\Users\USER\Documents\context-engine\venv\Scripts\gitcast.exe\__main__.py", line 7, in <module>
89
- sys.exit(main())
90
- File "C:\Users\USER\Documents\context-engine\venv\lib\site-packages\cli\gitcast.py", line 79, in main
91
- from main import on_trigger
92
- ImportError: cannot import name 'on_trigger' from 'main' (C:\Users\USER\Documents\snakegame\main.py)
93
- ```
94
- * **Root Cause**:
95
- In `cli/gitcast.py`, the script checks if `main.py` exists in the current working directory (`os.getcwd()`), and if so, prepends `os.getcwd()` to `sys.path` and attempts to run `from main import on_trigger`.
96
- This causes the following critical failures:
97
- 1. **Unwanted Side-Effects**: It imports and executes the user's local `main.py` (e.g., in a snake game project), causing dependencies like `pygame` to initialize and boot, printing their community welcome messages and running game startup side-effects.
98
- 2. **Namespace Collision & ImportError**: Since the user's `main.py` does not define `on_trigger`, the application crashes with an `ImportError`.
99
- 3. **Missing Package Module**: The packaged/installed version of `gitcast` from PyPI does not distribute its own root-level `main.py` file because `find_packages()` in `setup.py` only bundles directories containing `__init__.py`. Thus, `main` is completely missing from site-packages.
100
- * **Status**: Logged and pending future resolution.
101
-
102
- #### Proposed Solutions & Analysis
103
-
104
- ##### Option A: Move trigger callback logic to a dedicated package module (e.g., `core/trigger.py`)
105
- * **Rating**: ⭐⭐⭐⭐⭐ (Best Possible Fix)
106
- * **Ups (Pros)**:
107
- 1. **Clean Namespace isolation**: Completely eliminates the custom `sys.path.insert(0, os.getcwd())` lookup logic inside `cli/gitcast.py`.
108
- 2. **No collisions**: Importing from `core.trigger` is guaranteed to load files packaged inside your library's namespace, so it won't conflict with any `main.py` inside the user's workspace.
109
- 3. **No side-effects**: Users' codebases (like a Pygame-based game) will never be imported or executed by Gitcast's boot process.
110
- 4. **Packaging Standard**: Adheres to Python's package guidelines by not polluting the global site-packages namespace with a top-level module named `main`.
111
- * **Downs (Cons)**:
112
- - Requires moving the `on_trigger` function definition from `main.py` to `core/trigger.py` (minimal effort).
113
-
114
- ##### Option B: Package the root-level `main.py` explicitly in `setup.py`
115
- * **Rating**: ⭐⭐ (Poor Packaging Practice)
116
- * **Ups (Pros)**:
117
- - Quick fix: Simply adding `py_modules=["main"]` in `setup.py` distributes `main.py` alongside the package directories.
118
- * **Downs (Cons)**:
119
- - **Global namespace pollution**: Distributing a generic package module named `main` into Python's `site-packages` namespace is highly discouraged. It will clash with any local `main` module import in users' custom python environments.
120
- - **Does not solve the collision**: Since `sys.path` in `cli/gitcast.py` checks `os.getcwd()` first, the local `main.py` of the user's project (e.g. `snakegame/main.py`) will still override and take precedence over the packaged `main.py`, preserving the pygame execution side-effects and crash.
121
83
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitcast
3
- Version: 1.0.7
3
+ Version: 1.0.9
4
4
  Summary: git diff → published post. under 60 seconds.
5
5
  Home-page: https://github.com/drizzy765/gitcast
6
6
  Author: Timilehin Agoro
@@ -51,7 +51,7 @@ if os.path.exists(requirements_path):
51
51
 
52
52
  setup(
53
53
  name="gitcast",
54
- version="1.0.7",
54
+ version="1.0.9",
55
55
  description="git diff → published post. under 60 seconds.",
56
56
  long_description=long_description,
57
57
  long_description_content_type="text/markdown",
@@ -2361,16 +2361,14 @@
2361
2361
  return;
2362
2362
  }
2363
2363
 
2364
- let token = localStorage.getItem('sl_token') || '';
2364
+ let token = '';
2365
2365
  let valid = false;
2366
2366
 
2367
- if (token) {
2368
- setAuthToken(token, localStorage.getItem('sl_user') || 'local_user');
2369
- valid = await verifyStoredToken(token);
2370
- }
2371
-
2372
- if (!valid) {
2373
- // Auto-fetch localhost token if stored token is missing or invalid
2367
+ // 1. If on localhost, try to get the active session token directly from the server first
2368
+ const isLocalhost = window.location.hostname === 'localhost' ||
2369
+ window.location.hostname === '127.0.0.1' ||
2370
+ window.location.hostname === '[::1]';
2371
+ if (isLocalhost) {
2374
2372
  try {
2375
2373
  const tokenRes = await fetch('/api/token');
2376
2374
  if (tokenRes.status === 200) {
@@ -2386,6 +2384,15 @@
2386
2384
  }
2387
2385
  }
2388
2386
 
2387
+ // 2. Fallback to localStorage token if we couldn't get a valid token from localhost auto-fetch
2388
+ if (!valid) {
2389
+ token = localStorage.getItem('sl_token') || '';
2390
+ if (token) {
2391
+ setAuthToken(token, localStorage.getItem('sl_user') || 'local_user');
2392
+ valid = await verifyStoredToken(token);
2393
+ }
2394
+ }
2395
+
2389
2396
  if (valid) {
2390
2397
  await showDashboard(false);
2391
2398
  } else {
@@ -1,96 +0,0 @@
1
- import threading
2
- from core.capture import run_capture
3
- from core.ocr import run_ocr
4
- from api.payload import build_payload
5
- from ui.popup import show_popup
6
- from config.settings import is_sprint_mode
7
- from storage.sprint import log_sprint_capture
8
- from ai.generator import generate_posts
9
- from ui.review import show_review
10
- from publisher.twitter import publish_post
11
- from storage.logger import log_post
12
- import asyncio
13
- import webbrowser
14
-
15
-
16
- def on_trigger():
17
- """
18
- Called every time the hotkey fires.
19
- Full capture → generate → review flow.
20
- """
21
- print("[Gitcast] Hotkey fired — starting capture...")
22
-
23
- capture = run_capture()
24
- ocr = run_ocr(capture["screenshot"]["path"])
25
-
26
- if is_sprint_mode():
27
- log_sprint_capture(
28
- git_diff=capture["git_diff"].get("diff", ""),
29
- ocr_text=ocr.get("text", ""),
30
- raw_thought="",
31
- timestamp=capture["screenshot"]["timestamp"],
32
- )
33
- print("[Gitcast] Sprint Mode — capture logged silently.")
34
- return
35
-
36
- def on_submit(raw_thought):
37
- print(f"[Gitcast] Raw thought: '{raw_thought}'")
38
- payload = build_payload(
39
- raw_thought=raw_thought,
40
- ocr_result=ocr,
41
- capture_result=capture,
42
- )
43
- payload["use_vision_fallback"] = False
44
- payload["screenshot_b64"] = None
45
-
46
- def generate_and_show():
47
- variations = asyncio.run(generate_posts(payload))
48
-
49
- def on_publish(post_text, format_key,
50
- screenshot_path):
51
- print(f"[Gitcast] Publishing: {format_key}")
52
- result = publish_post(
53
- post_text, screenshot_path)
54
- if result.get("success"):
55
- if result.get("fallback"):
56
- print("[Gitcast] Copied to clipboard")
57
- else:
58
- print(f"[Gitcast] Published: "
59
- f"{result.get('tweet_url')}")
60
- log_post(
61
- post_text=post_text,
62
- format_key=format_key,
63
- screenshot_path=screenshot_path,
64
- tweet_url=result.get(
65
- "tweet_url", ""),
66
- tweet_id=result.get(
67
- "tweet_id", ""),
68
- fallback=result.get(
69
- "fallback", False),
70
- )
71
- else:
72
- print(f"[Gitcast] Error: "
73
- f"{result.get('error')}")
74
-
75
- def on_close():
76
- print("[Gitcast] Review closed.")
77
-
78
- show_review(
79
- payload=payload,
80
- variations=variations,
81
- on_publish=on_publish,
82
- on_close=on_close,
83
- )
84
-
85
- threading.Thread(
86
- target=generate_and_show,
87
- daemon=True
88
- ).start()
89
-
90
- def on_dismiss():
91
- print("[Gitcast] Capture dismissed.")
92
-
93
- show_popup(
94
- on_submit=on_submit,
95
- on_dismiss=on_dismiss
96
- )
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