gitcast 1.0.22__tar.gz → 1.0.24__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 (90) hide show
  1. {gitcast-1.0.22 → gitcast-1.0.24}/MANIFEST.in +2 -0
  2. {gitcast-1.0.22/gitcast.egg-info → gitcast-1.0.24}/PKG-INFO +1 -1
  3. {gitcast-1.0.22 → gitcast-1.0.24}/ai/generator.py +1 -1
  4. {gitcast-1.0.22 → gitcast-1.0.24}/api/routes.py +2 -15
  5. {gitcast-1.0.22 → gitcast-1.0.24/gitcast.egg-info}/PKG-INFO +1 -1
  6. {gitcast-1.0.22 → gitcast-1.0.24}/gitcast.egg-info/SOURCES.txt +1 -0
  7. {gitcast-1.0.22 → gitcast-1.0.24}/setup.py +1 -1
  8. gitcast-1.0.24/storage/data/prompts.json +37 -0
  9. {gitcast-1.0.22 → gitcast-1.0.24}/web/index.html +40 -84
  10. {gitcast-1.0.22 → gitcast-1.0.24}/.env.example +0 -0
  11. {gitcast-1.0.22 → gitcast-1.0.24}/LICENSE +0 -0
  12. {gitcast-1.0.22 → gitcast-1.0.24}/README.md +0 -0
  13. {gitcast-1.0.22 → gitcast-1.0.24}/ai/__init__.py +0 -0
  14. {gitcast-1.0.22 → gitcast-1.0.24}/ai/formatter.py +0 -0
  15. {gitcast-1.0.22 → gitcast-1.0.24}/ai/prompts.py +0 -0
  16. {gitcast-1.0.22 → gitcast-1.0.24}/ai/viral_patterns.py +0 -0
  17. {gitcast-1.0.22 → gitcast-1.0.24}/api/__init__.py +0 -0
  18. {gitcast-1.0.22 → gitcast-1.0.24}/api/analytics.py +0 -0
  19. {gitcast-1.0.22 → gitcast-1.0.24}/api/auth.py +0 -0
  20. {gitcast-1.0.22 → gitcast-1.0.24}/api/auth_middleware.py +0 -0
  21. {gitcast-1.0.22 → gitcast-1.0.24}/api/auth_routes.py +0 -0
  22. {gitcast-1.0.22 → gitcast-1.0.24}/api/monitoring.py +0 -0
  23. {gitcast-1.0.22 → gitcast-1.0.24}/api/payload.py +0 -0
  24. {gitcast-1.0.22 → gitcast-1.0.24}/api/ratelimit.py +0 -0
  25. {gitcast-1.0.22 → gitcast-1.0.24}/api/server.py +0 -0
  26. {gitcast-1.0.22 → gitcast-1.0.24}/api/validators.py +0 -0
  27. {gitcast-1.0.22 → gitcast-1.0.24}/assets/__init__.py +0 -0
  28. {gitcast-1.0.22 → gitcast-1.0.24}/assets/favicon-16x16.png +0 -0
  29. {gitcast-1.0.22 → gitcast-1.0.24}/assets/favicon-32x32.png +0 -0
  30. {gitcast-1.0.22 → gitcast-1.0.24}/assets/favicon-64x64.png +0 -0
  31. {gitcast-1.0.22 → gitcast-1.0.24}/assets/favicon.ico +0 -0
  32. {gitcast-1.0.22 → gitcast-1.0.24}/assets/icon.png +0 -0
  33. {gitcast-1.0.22 → gitcast-1.0.24}/cli/.env.example +0 -0
  34. {gitcast-1.0.22 → gitcast-1.0.24}/cli/__init__.py +0 -0
  35. {gitcast-1.0.22 → gitcast-1.0.24}/cli/gitcast.py +0 -0
  36. {gitcast-1.0.22 → gitcast-1.0.24}/config/__init__.py +0 -0
  37. {gitcast-1.0.22 → gitcast-1.0.24}/config/settings.py +0 -0
  38. {gitcast-1.0.22 → gitcast-1.0.24}/core/__init__.py +0 -0
  39. {gitcast-1.0.22 → gitcast-1.0.24}/core/capture.py +0 -0
  40. {gitcast-1.0.22 → gitcast-1.0.24}/core/codebase_reader.py +0 -0
  41. {gitcast-1.0.22 → gitcast-1.0.24}/core/framing.py +0 -0
  42. {gitcast-1.0.22 → gitcast-1.0.24}/core/hotkey.py +0 -0
  43. {gitcast-1.0.22 → gitcast-1.0.24}/core/log_stream.py +0 -0
  44. {gitcast-1.0.22 → gitcast-1.0.24}/core/ocr.py +0 -0
  45. {gitcast-1.0.22 → gitcast-1.0.24}/core/screenshot_session.py +0 -0
  46. {gitcast-1.0.22 → gitcast-1.0.24}/core/security.py +0 -0
  47. {gitcast-1.0.22 → gitcast-1.0.24}/core/tray.py +0 -0
  48. {gitcast-1.0.22 → gitcast-1.0.24}/core/trigger.py +0 -0
  49. {gitcast-1.0.22 → gitcast-1.0.24}/docs/api-keys.md +0 -0
  50. {gitcast-1.0.22 → gitcast-1.0.24}/docs/article-generator.md +0 -0
  51. {gitcast-1.0.22 → gitcast-1.0.24}/docs/capture-flow.md +0 -0
  52. {gitcast-1.0.22 → gitcast-1.0.24}/docs/changelog.md +0 -0
  53. {gitcast-1.0.22 → gitcast-1.0.24}/docs/contributing.md +0 -0
  54. {gitcast-1.0.22 → gitcast-1.0.24}/docs/dashboard.md +0 -0
  55. {gitcast-1.0.22 → gitcast-1.0.24}/docs/insights.md +0 -0
  56. {gitcast-1.0.22 → gitcast-1.0.24}/docs/introduction.md +0 -0
  57. {gitcast-1.0.22 → gitcast-1.0.24}/docs/post-formats.md +0 -0
  58. {gitcast-1.0.22 → gitcast-1.0.24}/docs/providers.md +0 -0
  59. {gitcast-1.0.22 → gitcast-1.0.24}/docs/pypi-errors.md +0 -0
  60. {gitcast-1.0.22 → gitcast-1.0.24}/docs/quickstart.md +0 -0
  61. {gitcast-1.0.22 → gitcast-1.0.24}/docs/sprint-mode.md +0 -0
  62. {gitcast-1.0.22 → gitcast-1.0.24}/gitcast.egg-info/dependency_links.txt +0 -0
  63. {gitcast-1.0.22 → gitcast-1.0.24}/gitcast.egg-info/entry_points.txt +0 -0
  64. {gitcast-1.0.22 → gitcast-1.0.24}/gitcast.egg-info/requires.txt +0 -0
  65. {gitcast-1.0.22 → gitcast-1.0.24}/gitcast.egg-info/top_level.txt +0 -0
  66. {gitcast-1.0.22 → gitcast-1.0.24}/publisher/__init__.py +0 -0
  67. {gitcast-1.0.22 → gitcast-1.0.24}/publisher/clipboard.py +0 -0
  68. {gitcast-1.0.22 → gitcast-1.0.24}/publisher/twitter.py +0 -0
  69. {gitcast-1.0.22 → gitcast-1.0.24}/pyproject.toml +0 -0
  70. {gitcast-1.0.22 → gitcast-1.0.24}/requirements.txt +0 -0
  71. {gitcast-1.0.22 → gitcast-1.0.24}/setup.cfg +0 -0
  72. {gitcast-1.0.22 → gitcast-1.0.24}/storage/__init__.py +0 -0
  73. {gitcast-1.0.22 → gitcast-1.0.24}/storage/cleanup.py +0 -0
  74. {gitcast-1.0.22 → gitcast-1.0.24}/storage/engagement.py +0 -0
  75. {gitcast-1.0.22 → gitcast-1.0.24}/storage/insights.py +0 -0
  76. {gitcast-1.0.22 → gitcast-1.0.24}/storage/key_manager.py +0 -0
  77. {gitcast-1.0.22 → gitcast-1.0.24}/storage/logger.py +0 -0
  78. {gitcast-1.0.22 → gitcast-1.0.24}/storage/metrics.py +0 -0
  79. {gitcast-1.0.22 → gitcast-1.0.24}/storage/sprint.py +0 -0
  80. {gitcast-1.0.22 → gitcast-1.0.24}/storage/streak.py +0 -0
  81. {gitcast-1.0.22 → gitcast-1.0.24}/storage/supabase_client.py +0 -0
  82. {gitcast-1.0.22 → gitcast-1.0.24}/storage/tone_memory.py +0 -0
  83. {gitcast-1.0.22 → gitcast-1.0.24}/tests/test_keys.py +0 -0
  84. {gitcast-1.0.22 → gitcast-1.0.24}/tests/test_ocr_fix.py +0 -0
  85. {gitcast-1.0.22 → gitcast-1.0.24}/tests/test_validation.py +0 -0
  86. {gitcast-1.0.22 → gitcast-1.0.24}/ui/__init__.py +0 -0
  87. {gitcast-1.0.22 → gitcast-1.0.24}/ui/popup.py +0 -0
  88. {gitcast-1.0.22 → gitcast-1.0.24}/ui/review.py +0 -0
  89. {gitcast-1.0.22 → gitcast-1.0.24}/web/__init__.py +0 -0
  90. {gitcast-1.0.22 → gitcast-1.0.24}/web/landing.html +0 -0
@@ -6,3 +6,5 @@ include requirements.txt
6
6
  recursive-include web *
7
7
  recursive-include assets *
8
8
  recursive-include docs *
9
+ recursive-include storage/data prompts.json
10
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitcast
3
- Version: 1.0.22
3
+ Version: 1.0.24
4
4
  Summary: git diff → published post. under 60 seconds.
5
5
  Home-page: https://github.com/drizzy765/gitcast
6
6
  Author: Timilehin Agoro
@@ -39,7 +39,7 @@ PROVIDERS = {
39
39
  "base_url": os.getenv("DEEPSEEK_API_BASE", "https://api.deepseek.com/v1"),
40
40
  "api_key": DEEPSEEK_API_KEY,
41
41
  "model": os.getenv("DEEPSEEK_MODEL", "deepseek-chat"),
42
- "tasks": ["pr_desc"]
42
+ "tasks": ["pr_generator", "pr_desc"]
43
43
  },
44
44
  "cerebras": {
45
45
  "base_url": "https://api.cerebras.ai/v1",
@@ -861,13 +861,7 @@ def get_current_draft():
861
861
  raise HTTPException(status_code=500, detail=f"Error reading draft: {e}")
862
862
 
863
863
 
864
- REFINE_PROVIDER = {
865
- "article": "kimi",
866
- "x_post": "groq",
867
- "quick_win": "groq",
868
- "linkedin": "groq",
869
- "pr_desc": "deepseek",
870
- }
864
+
871
865
 
872
866
 
873
867
  @router.post("/chat")
@@ -908,14 +902,7 @@ Return the refined post now:"""
908
902
  if "variations" not in draft:
909
903
  draft["variations"] = {}
910
904
 
911
- # Map incoming public format_key to backend variation key
912
- variation_key = format_key
913
- if format_key == "x_post":
914
- variation_key = "deep_tech"
915
- elif format_key == "pr_desc":
916
- variation_key = "pr_generator"
917
-
918
- draft["variations"][variation_key] = refined_text
905
+ draft["variations"][format_key] = refined_text
919
906
 
920
907
  with open(CURRENT_DRAFT, "w", encoding="utf-8") as f:
921
908
  json.dump(draft, f, indent=4)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gitcast
3
- Version: 1.0.22
3
+ Version: 1.0.24
4
4
  Summary: git diff → published post. under 60 seconds.
5
5
  Home-page: https://github.com/drizzy765/gitcast
6
6
  Author: Timilehin Agoro
@@ -76,6 +76,7 @@ storage/sprint.py
76
76
  storage/streak.py
77
77
  storage/supabase_client.py
78
78
  storage/tone_memory.py
79
+ storage/data/prompts.json
79
80
  tests/test_keys.py
80
81
  tests/test_ocr_fix.py
81
82
  tests/test_validation.py
@@ -51,7 +51,7 @@ if os.path.exists(requirements_path):
51
51
 
52
52
  setup(
53
53
  name="gitcast",
54
- version="1.0.22",
54
+ version="1.0.24",
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,37 @@
1
+ {
2
+ "deep_tech": {
3
+ "label": "Deep Tech",
4
+ "description": "Short, punchy technical updates.",
5
+ "system_prompt": "You are a developer writing a raw, technically precise post for X (Twitter).\n\nYour goal is to communicate a technical update or finding to other developers. This is a signal flare for engineers.\n\nIMPORTANT: Prioritize the developer's raw thought/prompt if one is provided. Even if the developer's raw thought is brief, you MUST elaborate on the technical details, background, architecture, or implications. If there are no captured code changes or screenshots, brainstorm realistic engineering challenges or design choices for the project. Do not write a short 1-2 sentence post. Maximize the character budget to provide a substantive technical update.\n\nFormat guidance:\n- Lead with the specific technical detail: the project, function, bug, architecture decision, or metric.\n- Explain the 'how' or 'why' behind the update, citing code symbols or files where appropriate.\n- End with one sharp insight or takeaway.\n- Target length: 250–280 characters (or longer, up to 800 characters, if the X Premium user plan block is active)."
6
+ },
7
+ "linkedin": {
8
+ "label": "LinkedIn Post",
9
+ "description": "Professional but human brand-building updates.",
10
+ "system_prompt": "You are a developer writing a professional but human post for LinkedIn.\n\nYour goal is to write a high-value post that builds your professional brand without sounding like a corporate robot. LinkedIn posts perform best with white space, a strong hook, and a personal narrative.\n\nIMPORTANT: Prioritize the developer's raw thought/prompt if one is provided. Even if the developer's thought/prompt is brief, you MUST elaborate extensively, explaining the project narrative, technical context, challenges, choices, and what it unlocks next. If there are no captured code changes, screenshots, or project narrative, use your imagination to brainstorm and detail realistic goals, architecture details, or feature plans for the project. Do NOT write a short 2-3 sentence post under any circumstances. Your output MUST meet the target length of 800–1300 characters.\n\nFormat guidance:\n- Hook: Line 1 must be a compelling one-sentence hook that stops the scroll.\n- Body: Explain the context, story, or project in a highly readable way. Use double line breaks between short paragraphs (1-2 sentences max) to ensure easy readability on mobile devices.\n- Takeaway/Insight: Share one key lesson, insight, or value add that other professionals or builders can appreciate.\n- CTA: End with an engaging question to invite comments.\n- Style: Keep it professional but human. Avoid corporate jargon, robotic enthusiasm (no generic 'thrilled to announce' or 'game-changing journey'), and hashtag spam (max 2 hashtags).\n- Character target: 800–1300 characters. Make it comprehensive, detailed, and creative, not overly short."
11
+ },
12
+ "struggle": {
13
+ "label": "The Struggle",
14
+ "description": "Vulnerable posts about debugging and challenges.",
15
+ "system_prompt": "You are a developer writing an honest, relatable post for X (Twitter) about a real struggle in the build process.\n\nYour goal is to make other developers feel seen.\n\nIMPORTANT: Prioritize the developer's raw thought/prompt if one is provided. If the raw thought is empty, fall back to writing about a struggle based on the captured screenshot, code diff, or logs.\n\nFormat guidance:\n- Open with the feeling or the symptom, not the solution. Drop the reader into the frustration first.\n- Walk through what you tried that did not work — be specific, not vague.\n- Land on the breakthrough moment. What was the actual fix or insight?\n- End with a question or observation that invites other developers to share their own experience.\n- Target length: 220–280 characters. Can be a 2-tweet thread if the story needs it."
16
+ },
17
+ "quick_win": {
18
+ "label": "Quick Win",
19
+ "description": "Fast, confident, specific build updates.",
20
+ "system_prompt": "You are a developer writing a short, punchy build update for X (Twitter).\n\nYour goal is to document a micro-win in a way that is energizing to read.\n\nIMPORTANT: Prioritize the developer's raw thought/prompt if one is provided. If the raw thought is empty, document a micro-win based on the captured screenshot, code diff, or logs.\n\nFormat guidance:\n- Lead with the outcome. What is now working or shipped?\n- Provide one sentence of context.\n- Provide one sentence of forward momentum (what this unlocks next).\n- Target length: 140–200 characters. Single tweet only."
21
+ },
22
+ "pr_generator": {
23
+ "label": "PR Description",
24
+ "description": "Structured pull request descriptions.",
25
+ "system_prompt": "You are a senior software engineer writing a pull request description for GitHub.\n\nYour goal is to produce a clear, structured PR description that gives reviewers everything they need to understand, review, and merge this change confidently.\n\nIMPORTANT: Prioritize the developer's raw thought/prompt if one is provided. If the raw thought is empty, analyze the git diff and screenshots to document the changes.\n\nOutput a Markdown document with exactly these sections:\n\n## What changed\nA 2–3 sentence plain-English summary of what this PR does. Write it so a non-expert team member can understand it.\n\n## Why\nThe problem this solves or the feature this adds. Reference the bug, the user need, or the architectural reason.\n\n## How\nThe technical approach taken. Mention key functions, files, or patterns changed. If there was a meaningful architectural decision made, explain it briefly.\n\n## Testing\nWhat was tested and how. If manual testing was done, describe the steps. If automated tests were added or updated, mention them.\n\n## Notes for reviewer\nAnything the reviewer should pay special attention to, known edge cases, follow-up tickets created, or areas of uncertainty."
26
+ },
27
+ "article": {
28
+ "label": "Medium / Dev.to Article",
29
+ "description": "Long-form technical blog posts.",
30
+ "system_prompt": "You are a developer writing a long-form technical article for Medium or Dev.to based on a recent build session.\n\nYour goal is to expand the raw thoughts and code changes into a compelling narrative.\n\nIMPORTANT: Prioritize the developer's raw thought/prompt if one is provided. If the raw thought is empty, fall back to writing about the captured build session and code changes.\n\nFormat guidance:\n- Title: Catchy and descriptive.\n- Introduction: Hook the reader with the problem and the stakes.\n- The Deep Dive: Use the git diff and OCR context to explain the technical implementation. Use code blocks for key snippets.\n- Lessons Learned: Share 2-3 specific insights gained during the process.\n- Conclusion: What's next for the project?\n\nRules:\n- Tone should be informative yet conversational.\n- Use Markdown formatting for headers, bolding, and code.\n- Target length: 600-1000 words. Be comprehensive."
31
+ },
32
+ "x_thread": {
33
+ "label": "X Thread",
34
+ "description": "Multi-tweet educational or story threads.",
35
+ "system_prompt": "You are a developer writing a high-value technical thread for X (Twitter).\n\nYour goal is to break down a complex task or a long coding session into a series of punchy, sequential tweets.\n\nIMPORTANT: Prioritize the developer's raw thought/prompt if one is provided. If the raw thought is empty, fall back to writing about the captured build session.\n\nFormat guidance:\n- Tweet 1 (Hook): Start with a dramatic result or a common problem. Hook the reader immediately.\n- Tweets 2-4 (Process): Break down the implementation. Use short sentences.\n- Tweet 5 (Conclusion): Show the final result and what you learned.\n- Number each tweet (1/5, 2/5, etc.).\n- Each tweet should be under 280 characters.\n\nOutput each tweet separated by a blank line."
36
+ }
37
+ }
@@ -2002,9 +2002,9 @@
2002
2002
  <main class="main-content" id="main-container">
2003
2003
  <header class="main-header">
2004
2004
  <div class="tabs" id="tab-bar">
2005
- <div class="tab active" data-tab="x_post">[X_POST]</div>
2005
+ <div class="tab active" data-tab="deep_tech">[X_POST]</div>
2006
2006
  <div class="tab" data-tab="linkedin">[LINKEDIN]</div>
2007
- <div class="tab" data-tab="pr_desc">[PR_DESC]</div>
2007
+ <div class="tab" data-tab="pr_generator">[PR_DESC]</div>
2008
2008
  <div class="tab" data-tab="quick_win">[QUICK_WIN]</div>
2009
2009
  </div>
2010
2010
  <button class="btn-brutalist" onclick="triggerCapture()">[NEW_CAPTURE]</button>
@@ -2023,9 +2023,9 @@
2023
2023
  <div id="draft-warning-banner" class="hidden" style="background: rgba(255, 184, 0, 0.1); border: 2px solid var(--accent-amber); color: var(--accent-amber); padding: 12px; margin-bottom: 12px; font-family: 'JetBrains Mono', monospace; font-size: var(--text-sm); line-height: 1.4; display: none;">
2024
2024
  [WARNING] No meaningful context captured — type a real thought or capture a code window
2025
2025
  </div>
2026
- <textarea id="editor-x_post" class="editor-field" placeholder="Generating post content...▋" style="flex: 1; min-height: 180px; width: 100%; border: none; outline: none; background: transparent; color: var(--text-primary); font-family: 'JetBrains Mono', monospace; font-size: var(--text-lg); line-height: 1.6; resize: none; caret-color: var(--accent-green);"></textarea>
2026
+ <textarea id="editor-deep_tech" class="editor-field" placeholder="Generating post content...▋" style="flex: 1; min-height: 180px; width: 100%; border: none; outline: none; background: transparent; color: var(--text-primary); font-family: 'JetBrains Mono', monospace; font-size: var(--text-lg); line-height: 1.6; resize: none; caret-color: var(--accent-green);"></textarea>
2027
2027
  <textarea id="editor-linkedin" class="editor-field hidden" placeholder="Generating post content...▋" style="flex: 1; min-height: 180px; width: 100%; border: none; outline: none; background: transparent; color: var(--text-primary); font-family: 'JetBrains Mono', monospace; font-size: var(--text-lg); line-height: 1.6; resize: none; caret-color: var(--accent-green); display: none;"></textarea>
2028
- <textarea id="editor-pr_desc" class="editor-field hidden" placeholder="Generating post content...▋" style="flex: 1; min-height: 180px; width: 100%; border: none; outline: none; background: transparent; color: var(--text-primary); font-family: 'JetBrains Mono', monospace; font-size: var(--text-lg); line-height: 1.6; resize: none; caret-color: var(--accent-green); display: none;"></textarea>
2028
+ <textarea id="editor-pr_generator" class="editor-field hidden" placeholder="Generating post content...▋" style="flex: 1; min-height: 180px; width: 100%; border: none; outline: none; background: transparent; color: var(--text-primary); font-family: 'JetBrains Mono', monospace; font-size: var(--text-lg); line-height: 1.6; resize: none; caret-color: var(--accent-green); display: none;"></textarea>
2029
2029
  <textarea id="editor-quick_win" class="editor-field hidden" placeholder="Generating post content...▋" style="flex: 1; min-height: 180px; width: 100%; border: none; outline: none; background: transparent; color: var(--text-primary); font-family: 'JetBrains Mono', monospace; font-size: var(--text-lg); line-height: 1.6; resize: none; caret-color: var(--accent-green); display: none;"></textarea>
2030
2030
  <textarea id="editor-article" class="editor-field hidden" placeholder="Generating post content...▋" style="flex: 1; min-height: 180px; width: 100%; border: none; outline: none; background: transparent; color: var(--text-primary); font-family: 'JetBrains Mono', monospace; font-size: var(--text-lg); line-height: 1.6; resize: none; caret-color: var(--accent-green); display: none;"></textarea>
2031
2031
  <div class="editor-inner-footer">
@@ -2207,15 +2207,15 @@
2207
2207
  };
2208
2208
 
2209
2209
  const tabState = {
2210
- x_post: { content: '', history: [] },
2210
+ deep_tech: { content: '', history: [] },
2211
2211
  linkedin: { content: '', history: [] },
2212
- pr_desc: { content: '', history: [] },
2212
+ pr_generator: { content: '', history: [] },
2213
2213
  quick_win: { content: '', history: [] },
2214
2214
  article: { content: '', history: [] },
2215
2215
  };
2216
2216
 
2217
2217
  const TAB_CHIPS = {
2218
- x_post: [
2218
+ deep_tech: [
2219
2219
  "make it viral",
2220
2220
  "add a hook",
2221
2221
  "turn into thread",
@@ -2231,7 +2231,7 @@
2231
2231
  "shorter hook",
2232
2232
  "add emojis",
2233
2233
  ],
2234
- pr_desc: [
2234
+ pr_generator: [
2235
2235
  "more technical",
2236
2236
  "add test details",
2237
2237
  "simplify language",
@@ -2384,22 +2384,17 @@
2384
2384
  }
2385
2385
 
2386
2386
  // Setup input event listeners for each tab's editor
2387
- ['x_post', 'linkedin', 'pr_desc', 'quick_win', 'article'].forEach(tabKey => {
2387
+ ['deep_tech', 'linkedin', 'pr_generator', 'quick_win', 'article'].forEach(tabKey => {
2388
2388
  const editor = document.getElementById(`editor-${tabKey}`);
2389
2389
  if (editor) {
2390
2390
  editor.oninput = (e) => {
2391
2391
  const text = e.target.value;
2392
2392
  tabState[tabKey].content = text;
2393
2393
 
2394
- const keyMapRev = {
2395
- x_post: 'deep_tech',
2396
- pr_desc: 'pr_generator'
2397
- };
2398
- const legacyKey = keyMapRev[tabKey] || tabKey;
2399
- if (legacyKey === 'article') {
2394
+ if (tabKey === 'article') {
2400
2395
  state.article.text = text;
2401
- } else if (state.tabs[legacyKey]) {
2402
- state.tabs[legacyKey].text = text;
2396
+ } else if (state.tabs[tabKey]) {
2397
+ state.tabs[tabKey].text = text;
2403
2398
  }
2404
2399
  updatePreview(tabKey, text);
2405
2400
  };
@@ -2981,15 +2976,15 @@
2981
2976
  hideOnboarding();
2982
2977
  showToast('Onboarding complete', 'success');
2983
2978
  } else {
2984
- if (status) status.innerText = '[!!] onboarding save failed';
2985
- if (btn) {
2986
- btn.disabled = false;
2987
- btn.innerText = '[OPEN_DASHBOARD →]';
2988
- }
2989
- }
2990
- }
2979
+ if (status) status.innerText = '[!!] onboarding save failed';
2980
+ if (btn) {
2981
+ btn.disabled = false;
2982
+ btn.innerText = '[OPEN_DASHBOARD →]';
2983
+ }
2984
+ }
2985
+ }
2991
2986
 
2992
- async function fetchDraft() {
2987
+ async function fetchDraft() {
2993
2988
  const draft = await api('/api/draft');
2994
2989
  if (draft && draft.status === 'ready') {
2995
2990
  const isNewDraft = draft.timestamp !== state.draft?.timestamp;
@@ -2998,16 +2993,9 @@
2998
2993
  const payload = draft.payload || {};
2999
2994
  const screenshots = Array.isArray(payload.screenshots) ? payload.screenshots : [];
3000
2995
  const errors = [];
3001
- const keyMap = {
3002
- deep_tech: 'x_post',
3003
- linkedin: 'linkedin',
3004
- pr_generator: 'pr_desc',
3005
- quick_win: 'quick_win'
3006
- };
3007
2996
  Object.keys(state.tabs).forEach(k => {
3008
- const mappedKey = keyMap[k] || k;
3009
- if (isNewDraft || !tabState[mappedKey].content) {
3010
- tabState[mappedKey].content = variations[k] || '';
2997
+ if (isNewDraft || !tabState[k].content) {
2998
+ tabState[k].content = variations[k] || '';
3011
2999
  }
3012
3000
  if (isNewDraft || !state.tabs[k].text) state.tabs[k].text = variations[k] || '';
3013
3001
  state.tabs[k].shots = screenshots.map(s => ({...s, status: 'pending'}));
@@ -3023,14 +3011,10 @@
3023
3011
  } else if (String(variations[k] || '').startsWith('[Error]')) {
3024
3012
  errors.push(k);
3025
3013
  }
3026
- });
3027
- if (errors.length) showToast(`Generation failed for ${errors.join(', ')}`, 'error');
3028
3014
 
3029
- // Populate individual textareas
3030
- Object.keys(keyMap).forEach(k => {
3031
- const mappedKey = keyMap[k];
3032
- const val = tabState[mappedKey].content;
3033
- const editor = document.querySelector(`#editor-${mappedKey}`);
3015
+ // Populate individual textareas
3016
+ const val = tabState[k].content;
3017
+ const editor = document.querySelector(`#editor-${k}`);
3034
3018
  if (editor) {
3035
3019
  if (editor.tagName === 'TEXTAREA') {
3036
3020
  editor.value = val;
@@ -3039,6 +3023,7 @@
3039
3023
  }
3040
3024
  }
3041
3025
  });
3026
+ if (errors.length) showToast(`Generation failed for ${errors.join(', ')}`, 'error');
3042
3027
 
3043
3028
  // Toggle warning banner
3044
3029
  const warnings = draft.warnings || [];
@@ -3076,7 +3061,7 @@
3076
3061
 
3077
3062
  if (!warnings || warnings.length === 0) {
3078
3063
  el.style.color = '#00ff88'
3079
- el.textContent = '[OK] context good'
3064
+ el.textContent = '[OK] context'
3080
3065
  } else {
3081
3066
  el.style.color = '#ffb800'
3082
3067
  el.textContent = '[!!] thin context'
@@ -3143,19 +3128,14 @@
3143
3128
 
3144
3129
  // Editor visibility and value assignment
3145
3130
  const activeTab = getActiveTab();
3146
- ['x_post', 'linkedin', 'pr_desc', 'quick_win', 'article'].forEach(t => {
3131
+ ['deep_tech', 'linkedin', 'pr_generator', 'quick_win', 'article'].forEach(t => {
3147
3132
  const ed = document.getElementById(`editor-${t}`);
3148
3133
  if (ed) {
3149
3134
  if (t === activeTab) {
3150
3135
  ed.classList.remove('hidden');
3151
3136
  ed.style.display = 'block';
3152
3137
 
3153
- const keyMapRev = {
3154
- x_post: 'deep_tech',
3155
- pr_desc: 'pr_generator'
3156
- };
3157
- const legacyKey = keyMapRev[t] || t;
3158
- const legacyVal = (legacyKey === 'article') ? state.article.text : (state.tabs[legacyKey]?.text || '');
3138
+ const legacyVal = (t === 'article') ? state.article.text : (state.tabs[t]?.text || '');
3159
3139
  if (legacyVal && !tabState[t].content) {
3160
3140
  tabState[t].content = legacyVal;
3161
3141
  }
@@ -3219,12 +3199,7 @@
3219
3199
  let text = refined;
3220
3200
  if (text === undefined) {
3221
3201
  const active = activeTab || getActiveTab();
3222
- const keyMapRev = {
3223
- x_post: 'deep_tech',
3224
- pr_desc: 'pr_generator'
3225
- };
3226
- const legacyKey = keyMapRev[active] || active;
3227
- text = (legacyKey === 'article' ? state.article.text : state.tabs[legacyKey]?.text) || '';
3202
+ text = (active === 'article' ? state.article.text : state.tabs[active]?.text) || '';
3228
3203
  }
3229
3204
  const previewTitle = document.getElementById('preview-title');
3230
3205
  const primaryBtn = document.getElementById('primary-action-btn');
@@ -3324,12 +3299,9 @@
3324
3299
  if (state.view === 'article') return 'article';
3325
3300
  const active = document.querySelector('.tab-btn.active, [data-tab].active, .tab.active');
3326
3301
  if (active) {
3327
- const tab = active.dataset.tab || active.id.replace('tab-', '');
3328
- if (tab === 'deep_tech') return 'x_post';
3329
- if (tab === 'pr_generator') return 'pr_desc';
3330
- return tab;
3302
+ return active.dataset.tab || active.id.replace('tab-', '');
3331
3303
  }
3332
- return 'x_post';
3304
+ return 'deep_tech';
3333
3305
  }
3334
3306
 
3335
3307
  function switchTab(tabKey) {
@@ -3339,15 +3311,10 @@
3339
3311
  tabState[prevTab].content = prevEditor.value || prevEditor.innerText;
3340
3312
  }
3341
3313
 
3342
- const keyMapRev = {
3343
- x_post: 'deep_tech',
3344
- pr_desc: 'pr_generator'
3345
- };
3346
- const legacyKey = keyMapRev[tabKey] || tabKey;
3347
- state.tab = legacyKey;
3314
+ state.tab = tabKey;
3348
3315
 
3349
3316
  document.querySelectorAll('.tab').forEach(el => {
3350
- if (el.dataset.tab === legacyKey || el.dataset.tab === tabKey) {
3317
+ if (el.dataset.tab === tabKey) {
3351
3318
  el.classList.add('active');
3352
3319
  } else {
3353
3320
  el.classList.remove('active');
@@ -3416,16 +3383,10 @@
3416
3383
  }
3417
3384
  tabState[activeTab].content = refined;
3418
3385
 
3419
- // Keep content in sync with legacy state if needed
3420
- const keyMapRev = {
3421
- x_post: 'deep_tech',
3422
- pr_desc: 'pr_generator'
3423
- };
3424
- const legacyKey = keyMapRev[activeTab] || activeTab;
3425
- if (legacyKey === 'article') {
3386
+ if (activeTab === 'article') {
3426
3387
  state.article.text = refined;
3427
- } else if (state.tabs[legacyKey]) {
3428
- state.tabs[legacyKey].text = refined;
3388
+ } else if (state.tabs[activeTab]) {
3389
+ state.tabs[activeTab].text = refined;
3429
3390
  }
3430
3391
 
3431
3392
  updatePreview(activeTab, refined);
@@ -3457,15 +3418,10 @@
3457
3418
  }
3458
3419
  tabState[activeTab].content = prev;
3459
3420
 
3460
- const keyMapRev = {
3461
- x_post: 'deep_tech',
3462
- pr_desc: 'pr_generator'
3463
- };
3464
- const legacyKey = keyMapRev[activeTab] || activeTab;
3465
- if (legacyKey === 'article') {
3421
+ if (activeTab === 'article') {
3466
3422
  state.article.text = prev;
3467
- } else if (state.tabs[legacyKey]) {
3468
- state.tabs[legacyKey].text = prev;
3423
+ } else if (state.tabs[activeTab]) {
3424
+ state.tabs[activeTab].text = prev;
3469
3425
  }
3470
3426
 
3471
3427
  updatePreview(activeTab, prev);
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