@champpaba/gslide 0.1.4 → 0.1.5

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@champpaba/gslide",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "CLI tool for automating Google Slides 'Help me visualize' feature via browser automation",
5
5
  "bin": {
6
6
  "gslide": "./bin/gslide.mjs"
package/pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "gslide"
3
- version = "0.1.4"
3
+ version = "0.1.5"
4
4
  description = "CLI tool for automating Google Slides 'Help me visualize' feature via browser automation"
5
5
  requires-python = ">=3.10"
6
6
  dependencies = [
@@ -1,4 +1,4 @@
1
1
  """gslide — CLI for automating Google Slides 'Help me visualise' feature."""
2
2
 
3
- __version__ = "0.1.4"
3
+ __version__ = "0.1.5"
4
4
  __all__ = ["__version__"]
@@ -1,9 +1,7 @@
1
1
  """Authentication session management — file I/O and browser operations."""
2
2
 
3
- import json
4
3
  import sys
5
4
  from pathlib import Path
6
- from typing import Any
7
5
 
8
6
  import click
9
7
 
@@ -18,10 +16,13 @@ def is_logged_in() -> bool:
18
16
  return get_storage_path().exists()
19
17
 
20
18
 
21
- def save_storage_state(data: dict[str, Any]) -> None:
22
- path = get_storage_path()
23
- path.parent.mkdir(parents=True, exist_ok=True)
24
- path.write_text(json.dumps(data, indent=2))
19
+ def require_login() -> Path:
20
+ """Return storage path or exit if not logged in."""
21
+ storage_path = get_storage_path()
22
+ if not storage_path.exists():
23
+ click.echo("Not logged in. Run: gslide auth login", err=True)
24
+ sys.exit(1)
25
+ return storage_path
25
26
 
26
27
 
27
28
  def delete_storage_state() -> None:
@@ -56,11 +57,7 @@ def login() -> None:
56
57
 
57
58
  def status() -> None:
58
59
  """Check if saved session is still valid."""
59
- path = get_storage_path()
60
-
61
- if not path.exists():
62
- click.echo("Not logged in. Run: gslide auth login")
63
- sys.exit(1)
60
+ path = require_login()
64
61
 
65
62
  from gslide.browser import BrowserSession
66
63
 
package/src/gslide/gen.py CHANGED
@@ -16,7 +16,7 @@ import time
16
16
  import click
17
17
  from playwright.sync_api import Page, TimeoutError as PwTimeout
18
18
 
19
- from gslide.auth import get_storage_path
19
+ from gslide.auth import require_login
20
20
  from gslide.browser import BrowserSession
21
21
  from gslide.prompts import PromptsData
22
22
 
@@ -73,9 +73,7 @@ def _reopen_panel(page: Page, retries: int = 2) -> None:
73
73
  """Reopen the HMV panel after insert overlay is dismissed."""
74
74
  for attempt in range(retries + 1):
75
75
  try:
76
- hmv = page.locator('div[aria-label="Help me visualize"]')
77
- hmv.click()
78
- page.wait_for_selector('[role="tab"]', timeout=PANEL_LOAD_TIMEOUT_MS)
76
+ open_panel(page)
79
77
  return
80
78
  except (PwTimeout, Exception):
81
79
  if attempt < retries:
@@ -92,11 +90,9 @@ def select_tab(page: Page, tab_name: str) -> None:
92
90
 
93
91
 
94
92
  def _find_visible_textarea(page: Page) -> object:
95
- textareas = page.locator("textarea")
96
- for i in range(textareas.count()):
97
- ta = textareas.nth(i)
98
- if ta.is_visible():
99
- return ta
93
+ ta = page.locator("textarea").first
94
+ if ta.is_visible():
95
+ return ta
100
96
  raise GenerationError("No visible text input found in panel")
101
97
 
102
98
 
@@ -201,18 +197,11 @@ def _click_insert_button(page: Page, text: str) -> None:
201
197
  raise GenerationError(f"'{text}' button not found after clicking preview")
202
198
 
203
199
 
204
- def insert_infographic(page: Page) -> None:
205
- """Click preview then 'Insert on new slide' for infographic tab."""
206
- _click_preview_image(page)
207
- _click_insert_button(page, "Insert on new slide")
208
- _wait_for_new_slide(page)
209
-
210
-
211
- def insert_slide(page: Page) -> None:
212
- """Click preview then 'Insert on new slide' for slide tab."""
200
+ def _insert_on_new_slide(page: Page) -> None:
201
+ """Click preview then 'Insert on new slide' (used by both infographic and slide tabs)."""
213
202
  _click_preview_image(page)
214
203
  _click_insert_button(page, "Insert on new slide")
215
- _wait_for_new_slide(page)
204
+ page.wait_for_timeout(2000)
216
205
 
217
206
 
218
207
  def insert_image(page: Page, insert_as: str = "image") -> None:
@@ -221,16 +210,11 @@ def insert_image(page: Page, insert_as: str = "image") -> None:
221
210
 
222
211
  option_text = "Insert as background" if insert_as == "background" else "Insert as image"
223
212
 
224
- btn = page.get_by_text(option_text)
225
- if btn.count() > 0 and btn.first.is_visible():
226
- btn.first.click()
227
- else:
213
+ try:
214
+ _click_insert_button(page, option_text)
215
+ except GenerationError:
228
216
  # Fallback: try "Insert on new slide"
229
- fallback = page.get_by_text("Insert on new slide")
230
- if fallback.count() > 0 and fallback.first.is_visible():
231
- fallback.first.click()
232
- else:
233
- raise GenerationError(f"'{option_text}' button not found")
217
+ _click_insert_button(page, "Insert on new slide")
234
218
 
235
219
  page.wait_for_timeout(2000)
236
220
 
@@ -240,29 +224,16 @@ def check_url(page: Page, presentation_id: str) -> None:
240
224
  raise GenerationError("Browser navigated away from target presentation")
241
225
 
242
226
 
243
- def _wait_for_new_slide(page: Page) -> None:
244
- page.wait_for_timeout(2000)
245
-
246
-
247
227
  # --- Orchestration ---
248
228
 
249
229
 
250
230
  _INSERT_FN = {
251
- "infographic": lambda page, **_: insert_infographic(page),
252
- "slide": lambda page, **_: insert_slide(page),
231
+ "infographic": lambda page, **_: _insert_on_new_slide(page),
232
+ "slide": lambda page, **_: _insert_on_new_slide(page),
253
233
  "image": lambda page, **opts: insert_image(page, opts.get("insert_as", "image")),
254
234
  }
255
235
 
256
236
 
257
- def _require_login() -> "Path":
258
- """Return storage path or exit if not logged in."""
259
- storage_path = get_storage_path()
260
- if not storage_path.exists():
261
- click.echo("Not logged in. Run: gslide auth login", err=True)
262
- sys.exit(1)
263
- return storage_path
264
-
265
-
266
237
  def gen_single(
267
238
  presentation_id: str,
268
239
  tab: str,
@@ -273,7 +244,7 @@ def gen_single(
273
244
  insert_as: str = "image",
274
245
  ) -> None:
275
246
  """Generate a single slide/infographic/image via browser automation."""
276
- storage_path = _require_login()
247
+ storage_path = require_login()
277
248
  timeout_ms = timeout * 1000
278
249
 
279
250
  with BrowserSession(storage_state=storage_path) as context:
@@ -281,7 +252,6 @@ def gen_single(
281
252
 
282
253
  try:
283
254
  navigate_to_presentation(page, presentation_id)
284
- check_url(page, presentation_id)
285
255
 
286
256
  if tab == "image" and slide_index is not None:
287
257
  _navigate_to_slide(page, slide_index)
@@ -328,7 +298,7 @@ def gen_batch(
328
298
  timeout: int = 120,
329
299
  ) -> None:
330
300
  """Generate all slides from prompts data in a single browser session."""
331
- storage_path = _require_login()
301
+ storage_path = require_login()
332
302
  timeout_ms = timeout * 1000
333
303
  total_slides = len(prompts_data.slides)
334
304
  total_images = len(prompts_data.images)
@@ -421,5 +391,5 @@ def gen_batch(
421
391
  for idx, tab, err in errors:
422
392
  click.echo(f" #{idx} ({tab}): {err}")
423
393
  click.echo(
424
- f"Presentation: https://docs.google.com/presentation/d/{prompts_data.presentation_id}/edit"
394
+ f"Presentation: {PRESENTATION_URL.format(id=prompts_data.presentation_id)}"
425
395
  )