Skip to content
48 changes: 40 additions & 8 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,9 @@ Fastest paths to understand and navigate the codebase:
TypedDicts (`RequestLoadDict`, `VariableDetail`, `LocalOverride`).
- **HTTP subsystem:** Read `src/services/http/__init__.py` — re-exports
`HttpService`, `GraphQLSchemaService`, `SnippetGenerator`,
`HttpResponseDict`, `parse_header_dict`.
`SnippetOptions`, `HttpResponseDict`, `parse_header_dict`.
Auth header injection lives in `src/services/http/auth_handler.py`.
OAuth 2.0 token exchange lives in `src/services/http/oauth2_service.py`.
- **All DB models:** Read `src/database/database.py` — re-exports all four
ORM models (`CollectionModel`, `RequestModel`, `SavedResponseModel`,
`EnvironmentModel`).
Expand Down Expand Up @@ -159,7 +161,13 @@ src/
│ ├── http/ # HTTP request/response handling
│ │ ├── http_service.py # HttpService (httpx) + response TypedDicts
│ │ ├── graphql_schema_service.py # GraphQL introspection + schema parsing
│ │ ├── snippet_generator.py # Code snippet generation (cURL/Python/JS)
│ │ ├── auth_handler.py # Shared auth header injection (all 12 auth types)
│ │ ├── oauth2_service.py # OAuth 2.0 token exchange (4 grant types)
│ │ ├── snippet_generator/ # Code snippet generation sub-package (23 languages)
│ │ │ ├── generator.py # SnippetGenerator, SnippetOptions, LanguageEntry, registry
│ │ │ ├── shell_snippets.py # cURL, HTTP raw, wget, HTTPie, PowerShell
│ │ │ ├── dynamic_snippets.py # Python, JS, Node, Ruby, PHP, Dart
│ │ │ └── compiled_snippets.py # Go, Rust, C, Swift, Java, Kotlin, C#
│ │ └── header_utils.py # Shared header parsing utility
│ └── import_parser/ # Parser sub-package
│ ├── models.py # TypedDict schemas for parsed data
Expand All @@ -170,9 +178,14 @@ src/
├── main_window/ # Top-level MainWindow sub-package
│ ├── window.py # MainWindow widget + signal wiring
│ ├── send_pipeline.py # _SendPipelineMixin — HTTP send/response flow
│ ├── draft_controller.py # _DraftControllerMixin — draft tab open/save
│ ├── tab_controller.py # _TabControllerMixin — tab open/close/switch
│ └── variable_controller.py # _VariableControllerMixin — env variable management
│ └── variable_controller.py # _VariableControllerMixin — env variable + sidebar management
├── loading_screen.py # Loading screen overlay widget
├── sidebar/ # Right sidebar sub-package
│ ├── sidebar_widget.py # RightSidebar (icon rail) + _FlyoutPanel
│ ├── variables_panel.py # VariablesPanel — read-only variable display
│ └── snippet_panel.py # SnippetPanel — inline code snippet generator
├── styling/ # Visual theming and icons
│ ├── theme.py # Palettes, colours, badge geometry, method_color()
│ ├── theme_manager.py # ThemeManager — QPalette + QSettings
Expand All @@ -192,16 +205,17 @@ src/
├── collections/ # Collection sidebar
│ ├── collection_header.py
│ ├── collection_widget.py
│ ├── new_item_popup.py # NewItemPopup — Postman-style icon grid popup
│ └── tree/ # Tree widget sub-package
│ ├── constants.py
│ ├── draggable_tree_widget.py
│ ├── collection_tree.py # CollectionTree widget
│ ├── tree_actions.py # _TreeActionsMixin — context menus, rename, delete
│ └── collection_tree_delegate.py # Custom delegate for method badges
├── dialogs/ # Modal dialogs
│ ├── code_snippet_dialog.py
│ ├── collection_runner.py
│ ├── import_dialog.py
│ ├── save_request_dialog.py # Save draft request to collection
│ └── settings_dialog.py # Settings (theme, colour scheme)
├── environments/ # Environment management widgets
│ ├── environment_editor.py
Expand All @@ -212,9 +226,15 @@ src/
└── request/ # Request/response editing
├── folder_editor.py # Folder/collection detail editor
├── http_worker.py # HttpSendWorker + SchemaFetchWorker (QThread)
├── auth/ # Shared auth sub-package (14 auth types)
│ ├── auth_field_specs.py # Per-type FieldSpec definitions (AUTH_FIELD_SPECS)
│ ├── auth_mixin.py # _AuthMixin — shared by both editors
│ ├── auth_pages.py # FieldSpec dataclass, page builders, auth constants
│ ├── auth_serializer.py # Generic load/save for all auth types
│ └── oauth2_page.py # OAuth 2.0 custom page (grant-type switching)
├── request_editor/ # RequestEditor sub-package
│ ├── editor_widget.py # RequestEditor — main request editing widget
│ ├── auth.py # _AuthMixin — authentication UI
│ ├── auth.py # Re-export of _AuthMixin from auth sub-package
│ ├── body_search.py # _BodySearchMixin — search/replace in body
│ └── graphql.py # _GraphQLMixin — GraphQL mode + schema
├── response_viewer/ # ResponseViewer sub-package
Expand All @@ -223,7 +243,7 @@ src/
├── navigation/ # Tab switching and path navigation
│ ├── breadcrumb_bar.py
│ ├── request_tab_bar.py
│ └── tab_manager.py # TabManager + TabContext (with local_overrides)
│ └── tab_manager.py # TabManager + TabContext (with local_overrides, draft_name)
└── popups/ # Response metadata popups
├── status_popup.py # HTTP status code explanation
├── timing_popup.py # Request timing breakdown
Expand All @@ -243,14 +263,24 @@ tests/
│ └── http/ # HTTP service tests
│ ├── test_http_service.py
│ ├── test_graphql_schema_service.py
│ └── test_snippet_generator.py
│ ├── test_snippet_generator.py
│ ├── test_snippet_shell.py
│ ├── test_snippet_dynamic.py
│ ├── test_snippet_compiled.py
│ ├── test_auth_handler.py
│ └── test_oauth2_service.py
└── ui/ # End-to-end PySide6 widget tests
├── conftest.py # _no_fetch (autouse) + helpers
├── test_main_window.py
├── test_main_window_save.py # SaveButton + RequestSaveEndToEnd tests
├── test_main_window_draft.py # Draft tab open/save lifecycle tests
├── styling/ # Theme and icon tests
│ ├── test_theme_manager.py
│ └── test_icons.py
├── sidebar/ # Sidebar widget tests
│ ├── test_sidebar.py
│ ├── test_variables_panel.py
│ └── test_snippet_panel.py
├── widgets/ # Shared component tests
│ ├── test_code_editor.py
│ ├── test_code_editor_folding.py
Expand All @@ -266,9 +296,11 @@ tests/
│ ├── test_collection_tree.py
│ ├── test_collection_tree_actions.py
│ ├── test_collection_tree_delegate.py
│ └── test_collection_widget.py
│ ├── test_collection_widget.py
│ └── test_new_item_popup.py
├── dialogs/ # Dialog tests
│ ├── test_import_dialog.py
│ ├── test_save_request_dialog.py
│ └── test_settings_dialog.py
├── environments/ # Environment widget tests
│ ├── test_environment_editor.py
Expand Down
24 changes: 23 additions & 1 deletion .github/instructions/architecture.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ Key signals to know (always-on summary):

- `CollectionWidget.item_action_triggered(str, int, str)` → opens
requests/folders in MainWindow.
- `CollectionWidget.draft_request_requested()` → opens a new draft
(unsaved) request tab in MainWindow.
- `NewItemPopup.new_request_clicked()` / `new_collection_clicked()` →
emitted by the icon grid popup when tiles are clicked.
- `RequestEditorWidget.send_requested()` → triggers HTTP send flow.
- `ThemeManager.theme_changed()` → widgets refresh dynamic styles.
- `VariablePopup` uses **class-level callbacks**, not signals — wired once
Expand Down Expand Up @@ -190,6 +194,20 @@ signal instead of relying on `_safe_svc_call`.
Children within a folder are **not sorted** — they appear in dict iteration
order (insertion order in Python 3.7+).

### 6. Auth inheritance convention

`auth = None` in the database means "inherit from parent" — the request
or folder walks up its ancestor chain until it finds a folder with an
explicit `auth` dict. `{"type": "noauth"}` means "no authentication" and
**stops** the inheritance chain. The UI maps `None` to
`"Inherit auth from parent"` in the auth type combo.

- `_get_auth_data()` returns `None` for inherit, `{"type": "noauth"}` for
explicit no-auth.
- `_load_auth(None)` / `_load_auth({})` → selects "Inherit auth from parent".
- `get_request_inherited_auth(request_id)` / `get_collection_inherited_auth(collection_id)`
resolve the effective auth by walking ancestors.

## Repository and service reference

> **Full repository function catalogues, service method tables, TypedDict
Expand Down Expand Up @@ -220,7 +238,11 @@ order (insertion order in Python 3.7+).
combined variable map in `MainWindow._refresh_variable_map()` and
tagged with `is_local=True` in `VariableDetail` so the popup can show
Update/Reset buttons.
7. **VariablePopup uses class-level callbacks, not Qt signals** —
7. **`TabContext.draft_name` tracks the display name of unsaved tabs** —
Set to `"Untitled Request"` when a draft tab is opened. Updated when
the user renames via the breadcrumb bar. Used as fallback label in the
save-to-collection dialog. `None` for persisted request tabs.
8. **VariablePopup uses class-level callbacks, not Qt signals** —
`VariablePopup` is a **singleton** `QFrame`. Its callbacks
(`set_save_callback`, `set_local_override_callback`,
`set_reset_local_override_callback`, `set_add_variable_callback`,
Expand Down
14 changes: 14 additions & 0 deletions .github/instructions/pyside6.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,20 @@ standard object names:
| `sidebarSearch` | `QLineEdit` | Collection sidebar search input |
| `sidebarSectionLabel` | `QLabel` | Sidebar section heading |
| `sidebarToolButton` | `QToolButton` | Sidebar toolbar button |
| `newItemPopup` | `QDialog` | Postman-style "Create New" dialog |
| `newItemTile` | `QPushButton` | Tile button inside the new-item dialog |
| `newItemTileLabel` | `QLabel` | Tile label text inside the dialog |
| `newItemTitle` | `QLabel` | Dialog heading ("What do you want to create?") |
| `newItemDescription` | `QLabel` | Description text below tiles |
| `collectionTree` | `QTreeWidget` | Collection tree in SaveRequestDialog |
| `sidebarRail` | `QWidget` | Always-visible icon rail (RightSidebar widget) |
| `sidebarRailButton` | `QToolButton` | Checkable icon button in the rail |
| `sidebarPanelArea` | `QWidget` | Collapsible flyout panel (separate splitter child) |
| `sidebarTitleLabel` | `QLabel` | Bold panel title in flyout header |
| `variableKeyLabel` | `QLabel` | Variable key in sidebar panel |
| `variableValueLabel` | `QLabel` | Variable value in sidebar panel |
| `sidebarSourceDot` | `QLabel` | Colour-coded variable source dot |
| `sidebarSeparator` | `QFrame` | Separator line in sidebar panels |

### When inline setStyleSheet() is still acceptable

Expand Down
16 changes: 14 additions & 2 deletions .github/instructions/testing.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,17 @@ tests/
│ └── http/ # HTTP service tests
│ ├── test_http_service.py
│ ├── test_graphql_schema_service.py
│ └── test_snippet_generator.py
│ ├── test_snippet_generator.py
│ ├── test_snippet_shell.py
│ ├── test_snippet_dynamic.py
│ ├── test_snippet_compiled.py
│ ├── test_auth_handler.py
│ └── test_oauth2_service.py
└── ui/ # PySide6 widget tests (need qapp + qtbot)
├── conftest.py # _no_fetch (autouse) + helper functions
├── test_main_window.py # Top-level MainWindow smoke tests
├── test_main_window_save.py # SaveButton + RequestSaveEndToEnd tests
├── test_main_window_draft.py # Draft tab open/save lifecycle tests
├── styling/ # Theme and icon tests
│ ├── test_theme_manager.py
│ └── test_icons.py
Expand All @@ -133,14 +139,20 @@ tests/
│ ├── test_variable_line_edit.py
│ ├── test_variable_popup.py
│ └── test_variable_popup_local.py
├── sidebar/ # Sidebar widget tests
│ ├── test_sidebar.py
│ ├── test_variables_panel.py
│ └── test_snippet_panel.py
├── collections/ # Collection sidebar tests
│ ├── test_collection_header.py
│ ├── test_collection_tree.py
│ ├── test_collection_tree_actions.py
│ ├── test_collection_tree_delegate.py
│ └── test_collection_widget.py
│ ├── test_collection_widget.py
│ └── test_new_item_popup.py
├── dialogs/ # Dialog tests
│ ├── test_import_dialog.py
│ ├── test_save_request_dialog.py
│ └── test_settings_dialog.py
├── environments/ # Environment widget tests
│ ├── test_environment_editor.py
Expand Down
Loading
Loading