flutter-dev 0.1.6__tar.gz → 0.1.7__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 (42) hide show
  1. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/CHANGELOG.md +7 -0
  2. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/PKG-INFO +12 -2
  3. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/README.md +11 -1
  4. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/managers/gui.py +105 -24
  5. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/pyproject.toml +1 -1
  6. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/tests/test_gui.py +64 -0
  7. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/.env.example +0 -0
  8. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/LICENSE +0 -0
  9. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/MANIFEST.in +0 -0
  10. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/common_utils.py +0 -0
  11. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/core/__init__.py +0 -0
  12. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/core/constants.py +0 -0
  13. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/core/state.py +0 -0
  14. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/create_page.py +0 -0
  15. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/fdev.py +0 -0
  16. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/flutter_dev.egg-info/SOURCES.txt +0 -0
  17. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/gemini_api.py +0 -0
  18. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/git_diff_output_editor.py +0 -0
  19. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/install_legacy.py +0 -0
  20. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/managers/__init__.py +0 -0
  21. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/managers/ai.py +0 -0
  22. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/managers/app.py +0 -0
  23. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/managers/brew.py +0 -0
  24. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/managers/build.py +0 -0
  25. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/managers/datetime.py +0 -0
  26. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/managers/device.py +0 -0
  27. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/managers/doctor.py +0 -0
  28. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/managers/git.py +0 -0
  29. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/managers/git_account.py +0 -0
  30. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/managers/localization.py +0 -0
  31. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/managers/merge.py +0 -0
  32. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/managers/mirror.py +0 -0
  33. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/managers/package.py +0 -0
  34. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/managers/project.py +0 -0
  35. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/managers/web_deploy.py +0 -0
  36. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/requirements.txt +0 -0
  37. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/setup.cfg +0 -0
  38. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/setup.py +0 -0
  39. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/switch_ai.py +0 -0
  40. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/tests/test_fdev_cli.py +0 -0
  41. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/tests/test_localization.py +0 -0
  42. {flutter_dev-0.1.6 → flutter_dev-0.1.7}/tests/test_package_commands.py +0 -0
@@ -2,6 +2,13 @@
2
2
 
3
3
  All notable changes to this project are documented in this file.
4
4
 
5
+ ## 0.1.7 - 2026-06-25
6
+
7
+ ### Fixed
8
+
9
+ - Reworked the Tkinter dashboard to use classic Tk widgets with explicit colors so `fdev` stays visible on macOS Xcode/system Python runtimes that can render `ttk` widgets as a blank popup.
10
+ - Silenced the macOS system Tk deprecation warning before launching the dashboard.
11
+
5
12
  ## 0.1.6 - 2026-06-25
6
13
 
7
14
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flutter-dev
3
- Version: 0.1.6
3
+ Version: 0.1.7
4
4
  Summary: A cross-platform CLI toolkit for Flutter development workflows.
5
5
  Author: Royal Court BD
6
6
  License-Expression: MIT
@@ -42,7 +42,7 @@ The package is published on PyPI as `flutter-dev` and installs console commands
42
42
  python -m pip install flutter-dev
43
43
  ```
44
44
 
45
- Current package metadata declares version `0.1.6`, Python `>=3.8`, MIT license, and support for macOS, Linux, and Windows.
45
+ Current package metadata declares version `0.1.7`, Python `>=3.8`, MIT license, and support for macOS, Linux, and Windows.
46
46
 
47
47
  ## Key Features
48
48
 
@@ -433,6 +433,16 @@ fdev delete
433
433
 
434
434
  Both commands print the detected OS and exact Python executable before running. They use that executable with `-m pip`, not a hardcoded `python`, `python3`, `pip`, or `pip3`, so the active venv/global Python that launched `fdev` is the environment that gets upgraded or deleted. On Windows, `fdev delete` and `fdev upgrade` schedule the same `sys.executable -m pip ...` operation in a delayed command process so the currently running console launcher can exit first.
435
435
 
436
+ ### Blank Dashboard On macOS
437
+
438
+ If `fdev` prints an Xcode/system Tk warning or opens a blank dashboard, upgrade to `flutter-dev` 0.1.7 or newer:
439
+
440
+ ```bash
441
+ fdev upgrade
442
+ ```
443
+
444
+ `fdev` prints the Python executable it is using. If that path is `/Applications/Xcode.app/Contents/Developer/usr/bin/python3`, the CLI is running on Xcode's bundled Python and system Tk; 0.1.7 uses a more conservative Tk UI path for that runtime.
445
+
436
446
  ### Flutter Project Not Detected
437
447
 
438
448
  Run project-specific commands from the directory that contains `pubspec.yaml`.
@@ -8,7 +8,7 @@ The package is published on PyPI as `flutter-dev` and installs console commands
8
8
  python -m pip install flutter-dev
9
9
  ```
10
10
 
11
- Current package metadata declares version `0.1.6`, Python `>=3.8`, MIT license, and support for macOS, Linux, and Windows.
11
+ Current package metadata declares version `0.1.7`, Python `>=3.8`, MIT license, and support for macOS, Linux, and Windows.
12
12
 
13
13
  ## Key Features
14
14
 
@@ -399,6 +399,16 @@ fdev delete
399
399
 
400
400
  Both commands print the detected OS and exact Python executable before running. They use that executable with `-m pip`, not a hardcoded `python`, `python3`, `pip`, or `pip3`, so the active venv/global Python that launched `fdev` is the environment that gets upgraded or deleted. On Windows, `fdev delete` and `fdev upgrade` schedule the same `sys.executable -m pip ...` operation in a delayed command process so the currently running console launcher can exit first.
401
401
 
402
+ ### Blank Dashboard On macOS
403
+
404
+ If `fdev` prints an Xcode/system Tk warning or opens a blank dashboard, upgrade to `flutter-dev` 0.1.7 or newer:
405
+
406
+ ```bash
407
+ fdev upgrade
408
+ ```
409
+
410
+ `fdev` prints the Python executable it is using. If that path is `/Applications/Xcode.app/Contents/Developer/usr/bin/python3`, the CLI is running on Xcode's bundled Python and system Tk; 0.1.7 uses a more conservative Tk UI path for that runtime.
411
+
402
412
  ### Flutter Project Not Detected
403
413
 
404
414
  Run project-specific commands from the directory that contains `pubspec.yaml`.
@@ -5,6 +5,7 @@ Small Tkinter GUI for running fdev commands with live logs.
5
5
 
6
6
  from __future__ import annotations
7
7
 
8
+ import os
8
9
  import shlex
9
10
  import subprocess
10
11
  import sys
@@ -37,6 +38,21 @@ ANSI_COLORS = {
37
38
  97: "bright_white",
38
39
  }
39
40
 
41
+ UI_BG = "#f8fafc"
42
+ SIDEBAR_BG = "#e5e7eb"
43
+ PANEL_BG = "#ffffff"
44
+ TEXT_FG = "#111827"
45
+ MUTED_FG = "#475569"
46
+ BUTTON_BG = "#e2e8f0"
47
+ BUTTON_ACTIVE_BG = "#cbd5e1"
48
+ LOG_BG = "#111827"
49
+ LOG_FG = "#e5e7eb"
50
+
51
+
52
+ def prepare_tk_environment():
53
+ """Prepare Tk for older macOS system Tk runtimes before importing tkinter."""
54
+ os.environ.setdefault("TK_SILENCE_DEPRECATION", "1")
55
+
40
56
 
41
57
  def _apply_ansi_codes(codes: List[int], current_tag: Optional[str]) -> Optional[str]:
42
58
  if not codes:
@@ -303,52 +319,73 @@ class FdevGui:
303
319
 
304
320
  def _build_widgets(self):
305
321
  import tkinter as tk
306
- from tkinter import ttk
322
+
323
+ self.root.configure(bg=UI_BG)
307
324
 
308
325
  self.root.columnconfigure(1, weight=1)
309
326
  self.root.rowconfigure(0, weight=1)
310
327
 
311
- sidebar = ttk.Frame(self.root, padding=(10, 10))
328
+ sidebar = tk.Frame(self.root, bg=SIDEBAR_BG, padx=10, pady=10)
312
329
  sidebar.grid(row=0, column=0, sticky="ns")
313
330
 
314
- title = ttk.Label(sidebar, text="fdev", font=("TkDefaultFont", 16, "bold"))
331
+ title = tk.Label(
332
+ sidebar,
333
+ text="fdev",
334
+ font=("TkDefaultFont", 16, "bold"),
335
+ bg=SIDEBAR_BG,
336
+ fg=TEXT_FG,
337
+ )
315
338
  title.grid(row=0, column=0, sticky="w", pady=(0, 10))
316
339
 
317
340
  for index, section_name in enumerate(self.sections, start=1):
318
- button = ttk.Button(
341
+ button = tk.Button(
319
342
  sidebar,
320
343
  text=section_name,
321
344
  command=lambda name=section_name: self._show_section(name),
345
+ bg=BUTTON_BG,
346
+ fg=TEXT_FG,
347
+ activebackground=BUTTON_ACTIVE_BG,
348
+ activeforeground=TEXT_FG,
349
+ relief="raised",
350
+ bd=1,
351
+ padx=10,
352
+ pady=4,
322
353
  )
323
354
  button.grid(row=index, column=0, sticky="ew", pady=3)
324
355
 
325
- content = ttk.Frame(self.root, padding=(8, 10, 10, 10))
356
+ content = tk.Frame(self.root, bg=UI_BG, padx=8, pady=10)
326
357
  content.grid(row=0, column=1, sticky="nsew")
327
358
  content.columnconfigure(0, weight=1)
328
359
  content.rowconfigure(1, weight=1)
329
360
 
330
- header = ttk.Frame(content)
361
+ header = tk.Frame(content, bg=UI_BG)
331
362
  header.grid(row=0, column=0, sticky="ew", pady=(0, 8))
332
363
  header.columnconfigure(0, weight=1)
333
364
 
334
- self.section_label = ttk.Label(header, text="", font=("TkDefaultFont", 14, "bold"))
365
+ self.section_label = tk.Label(
366
+ header,
367
+ text="",
368
+ font=("TkDefaultFont", 14, "bold"),
369
+ bg=UI_BG,
370
+ fg=TEXT_FG,
371
+ )
335
372
  self.section_label.grid(row=0, column=0, sticky="w")
336
373
 
337
- self.status_label = ttk.Label(header, text="Ready")
374
+ self.status_label = tk.Label(header, text="Ready", bg=UI_BG, fg=MUTED_FG)
338
375
  self.status_label.grid(row=0, column=1, sticky="e")
339
376
 
340
- body = ttk.Frame(content)
377
+ body = tk.Frame(content, bg=UI_BG)
341
378
  body.grid(row=1, column=0, sticky="nsew")
342
379
  body.columnconfigure(0, weight=1)
343
380
  body.rowconfigure(0, weight=0)
344
381
  body.rowconfigure(1, weight=1)
345
382
 
346
- self.command_frame = ttk.Frame(body, padding=(0, 0, 0, 6))
383
+ self.command_frame = tk.Frame(body, bg=UI_BG, pady=6)
347
384
  self.command_frame.grid(row=0, column=0, sticky="ew")
348
385
  self.command_frame.columnconfigure(0, weight=1)
349
386
  self.command_frame.columnconfigure(1, weight=1)
350
387
 
351
- log_frame = ttk.Frame(body)
388
+ log_frame = tk.Frame(body, bg=UI_BG)
352
389
  log_frame.grid(row=1, column=0, sticky="nsew", pady=(6, 0))
353
390
  log_frame.columnconfigure(0, weight=1)
354
391
  log_frame.rowconfigure(0, weight=1)
@@ -358,31 +395,51 @@ class FdevGui:
358
395
  height=12,
359
396
  wrap="word",
360
397
  state="disabled",
361
- bg="#111827",
362
- fg="#e5e7eb",
363
- insertbackground="#e5e7eb",
398
+ bg=LOG_BG,
399
+ fg=LOG_FG,
400
+ insertbackground=LOG_FG,
401
+ relief="sunken",
402
+ bd=1,
364
403
  )
365
404
  self.log_text.grid(row=0, column=0, sticky="nsew")
366
405
 
367
- scrollbar = ttk.Scrollbar(log_frame, orient="vertical", command=self.log_text.yview)
406
+ scrollbar = tk.Scrollbar(log_frame, orient="vertical", command=self.log_text.yview)
368
407
  scrollbar.grid(row=0, column=1, sticky="ns")
369
408
  self.log_text.configure(yscrollcommand=scrollbar.set)
370
409
  self._configure_log_tags()
371
410
 
372
- input_bar = ttk.Frame(content)
411
+ input_bar = tk.Frame(content, bg=UI_BG)
373
412
  input_bar.grid(row=2, column=0, sticky="ew", pady=(8, 0))
374
413
  input_bar.columnconfigure(0, weight=1)
375
414
 
376
- self.input_entry = ttk.Entry(input_bar)
415
+ self.input_entry = tk.Entry(input_bar, bg=PANEL_BG, fg=TEXT_FG, insertbackground=TEXT_FG)
377
416
  self.input_entry.grid(row=0, column=0, sticky="ew", padx=(0, 6))
378
417
  self.input_entry.bind("<Return>", lambda _event: self._send_input())
379
418
 
380
- ttk.Button(input_bar, text="Send Input", command=self._send_input).grid(row=0, column=1, padx=(0, 6))
381
- ttk.Button(input_bar, text="Stop", command=self._stop_process).grid(row=0, column=2, padx=(0, 6))
382
- ttk.Button(input_bar, text="Clear", command=self._clear_log).grid(row=0, column=3)
419
+ tk.Button(
420
+ input_bar,
421
+ text="Send Input",
422
+ command=self._send_input,
423
+ bg=BUTTON_BG,
424
+ activebackground=BUTTON_ACTIVE_BG,
425
+ ).grid(row=0, column=1, padx=(0, 6))
426
+ tk.Button(
427
+ input_bar,
428
+ text="Stop",
429
+ command=self._stop_process,
430
+ bg=BUTTON_BG,
431
+ activebackground=BUTTON_ACTIVE_BG,
432
+ ).grid(row=0, column=2, padx=(0, 6))
433
+ tk.Button(
434
+ input_bar,
435
+ text="Clear",
436
+ command=self._clear_log,
437
+ bg=BUTTON_BG,
438
+ activebackground=BUTTON_ACTIVE_BG,
439
+ ).grid(row=0, column=3)
383
440
 
384
441
  def _show_section(self, section_name: str):
385
- from tkinter import ttk
442
+ import tkinter as tk
386
443
 
387
444
  self.current_section = section_name
388
445
  self.section_label.configure(text=section_name)
@@ -394,18 +451,41 @@ class FdevGui:
394
451
  row = index // 2
395
452
  column = index % 2
396
453
 
397
- container = ttk.Frame(self.command_frame, padding=6)
454
+ container = tk.Frame(
455
+ self.command_frame,
456
+ bg=PANEL_BG,
457
+ bd=1,
458
+ relief="solid",
459
+ padx=6,
460
+ pady=6,
461
+ )
398
462
  container.grid(row=row, column=column, sticky="nsew", padx=4, pady=4)
399
463
  container.columnconfigure(0, weight=1)
400
464
 
401
- button = ttk.Button(
465
+ button = tk.Button(
402
466
  container,
403
467
  text=command.label,
404
468
  command=lambda item=command: self._run_command(item),
469
+ bg=BUTTON_BG,
470
+ fg=TEXT_FG,
471
+ activebackground=BUTTON_ACTIVE_BG,
472
+ activeforeground=TEXT_FG,
473
+ relief="raised",
474
+ bd=1,
475
+ padx=8,
476
+ pady=4,
405
477
  )
406
478
  button.grid(row=0, column=0, sticky="ew")
407
479
 
408
- description = ttk.Label(container, text=command.description, wraplength=260)
480
+ description = tk.Label(
481
+ container,
482
+ text=command.description,
483
+ wraplength=260,
484
+ bg=PANEL_BG,
485
+ fg=MUTED_FG,
486
+ justify="left",
487
+ anchor="w",
488
+ )
409
489
  description.grid(row=1, column=0, sticky="w", pady=(4, 0))
410
490
 
411
491
  def _run_command(self, command: GuiCommand):
@@ -523,6 +603,7 @@ class FdevGui:
523
603
 
524
604
  def launch_gui() -> int:
525
605
  """Launch the fdev Tkinter dashboard."""
606
+ prepare_tk_environment()
526
607
  try:
527
608
  import tkinter as tk
528
609
  except ImportError:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "flutter-dev"
7
- version = "0.1.6"
7
+ version = "0.1.7"
8
8
  description = "A cross-platform CLI toolkit for Flutter development workflows."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -2,10 +2,12 @@ import sys
2
2
  import unittest
3
3
  from pathlib import Path
4
4
  from tkinter import TclError
5
+ from unittest.mock import patch
5
6
 
6
7
  from managers.gui import (
7
8
  FdevGui,
8
9
  GuiCommand,
10
+ prepare_tk_environment,
9
11
  parse_ansi_segments,
10
12
  build_fdev_command,
11
13
  get_gui_sections,
@@ -144,6 +146,14 @@ class GuiCatalogTests(unittest.TestCase):
144
146
  ["sync", "dev", "main"],
145
147
  )
146
148
 
149
+ def test_prepare_tk_environment_silences_macos_system_tk_warning(self):
150
+ with patch.dict("os.environ", {}, clear=True):
151
+ prepare_tk_environment()
152
+
153
+ import os
154
+
155
+ self.assertEqual(os.environ["TK_SILENCE_DEPRECATION"], "1")
156
+
147
157
  def test_dashboard_uses_stable_grid_layout_without_paned_window(self):
148
158
  try:
149
159
  import tkinter as tk
@@ -172,6 +182,60 @@ class GuiCatalogTests(unittest.TestCase):
172
182
  finally:
173
183
  root.destroy()
174
184
 
185
+ def test_dashboard_maps_initial_widgets_after_mainloop_starts(self):
186
+ try:
187
+ import tkinter as tk
188
+ except ImportError:
189
+ self.skipTest("Tkinter is not available")
190
+
191
+ try:
192
+ root = tk.Tk()
193
+ except TclError as error:
194
+ self.skipTest(f"Tk display is not available: {error}")
195
+
196
+ try:
197
+ app = FdevGui(root)
198
+ root.after(10, root.quit)
199
+ app.run()
200
+
201
+ self.assertTrue(app.command_frame.winfo_ismapped())
202
+ self.assertTrue(app.log_text.winfo_ismapped())
203
+ self.assertGreater(app.command_frame.winfo_width(), 100)
204
+ self.assertGreater(app.log_text.winfo_width(), 100)
205
+ finally:
206
+ root.destroy()
207
+
208
+ def test_dashboard_uses_classic_tk_widgets_for_system_tk_compatibility(self):
209
+ try:
210
+ import tkinter as tk
211
+ except ImportError:
212
+ self.skipTest("Tkinter is not available")
213
+
214
+ try:
215
+ root = tk.Tk()
216
+ except TclError as error:
217
+ self.skipTest(f"Tk display is not available: {error}")
218
+
219
+ try:
220
+ app = FdevGui(root)
221
+ root.update_idletasks()
222
+
223
+ widget_classes = {
224
+ widget.winfo_class()
225
+ for widget in _walk_widgets(root)
226
+ }
227
+
228
+ self.assertFalse(
229
+ {"TButton", "TEntry", "TFrame", "TLabel", "TScrollbar"} & widget_classes
230
+ )
231
+ self.assertIn("Button", widget_classes)
232
+ self.assertIn("Label", widget_classes)
233
+ self.assertIn("Entry", widget_classes)
234
+ self.assertIn("Scrollbar", widget_classes)
235
+ self.assertGreater(app.command_frame.winfo_width(), 100)
236
+ finally:
237
+ root.destroy()
238
+
175
239
 
176
240
  def _walk_widgets(widget):
177
241
  yield widget
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