Skip to content

Commit

Permalink
Better folder support, more informative clipboard comments
Browse files Browse the repository at this point in the history
  • Loading branch information
jiu-virgil committed Aug 16, 2024
1 parent 51e9fb9 commit e2eaf11
Show file tree
Hide file tree
Showing 9 changed files with 280 additions and 171 deletions.
118 changes: 91 additions & 27 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,64 +3,128 @@ name: Build and Release
on:
push:
tags:
- "v*" # This triggers the workflow for tags that start with 'v'
- "v*" # Trigger on version tags
workflow_dispatch:

jobs:
build:
runs-on: ${{ matrix.os }}
# Step 1: Install dependencies and cache them
dependencies:
runs-on: ubuntu-latest
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]

steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v3

- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: "3.x"

- name: Install dependencies
- name: Cache Python packages
id: cache
uses: actions/cache@v3
with:
path: |
.venv
key: ${{ runner.os }}-venv-${{ hashFiles('requirements.txt') }}
restore-keys: |
${{ runner.os }}-venv-
- name: Install dependencies if not cached
if: steps.cache.outputs.cache-hit != 'true'
run: |
python -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pyinstaller
shell: bash

- name: Save environment as artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.os }}-venv
path: .venv/

# Step 2: Build for each OS concurrently
build:
needs: dependencies
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Download cached environment
uses: actions/download-artifact@v4
with:
name: ${{ matrix.os }}-venv

- name: Build with PyInstaller
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.x"

- name: Activate virtual environment and install dependencies (Linux & macOS)
if: runner.os != 'Windows'
run: |
source .venv/bin/activate
shell: bash

- name: Activate virtual environment and install dependencies (Windows)
if: runner.os == 'Windows'
run: |
.\.venv\Scripts\activate
shell: powershell

- name: Build with PyInstaller (Linux & macOS)
if: runner.os != 'Windows'
run: |
source .venv/bin/activate
pyinstaller --onefile --windowed --icon=resources/spoon.png main.py
shell: bash

- name: Upload artifact
uses: actions/upload-artifact@v2
- name: Build with PyInstaller (Windows)
if: runner.os == 'Windows'
run: |
.\.venv\Scripts\activate
pyinstaller --onefile --windowed --icon=resources/spoon.png main.py
shell: powershell

- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.os }}-build
path: dist/

# Step 3: Create a release concurrently with builds
release:
needs: build
runs-on: ubuntu-latest
steps:
- name: Download Ubuntu artifact
uses: actions/download-artifact@v2
with:
name: ubuntu-latest-build

- name: Download macOS artifact
uses: actions/download-artifact@v2
with:
name: macos-latest-build
needs: build
permissions:
contents: write # Required for creating a release
actions: read

- name: Download Windows artifact
uses: actions/download-artifact@v2
steps:
- name: Download all build artifacts
uses: actions/download-artifact@v4
with:
name: windows-latest-build
name: |
ubuntu-latest-build
macos-latest-build
windows-latest-build
- name: Create Release
uses: softprops/action-gh-release@v1
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: dist/*
files: |
ubuntu-latest-build/*
macos-latest-build/*
windows-latest-build/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Binary file removed .last_selection.pickle
Binary file not shown.
9 changes: 6 additions & 3 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
from PySide6.QtWidgets import QApplication
from ui.file_selector_ui import FileSelectorUI
from utils.file_helpers import find_python_files, load_last_selection
from utils.file_helpers import (
find_python_files,
load_app_state,
) # Import the correct function


def main():
app = QApplication([])

python_files = find_python_files()
last_selection = load_last_selection()
app_state = load_app_state() # Load the full app state

window = FileSelectorUI(python_files, last_selection)
window = FileSelectorUI(python_files, app_state) # Pass app_state to the window
window.show()

app.exec()
Expand Down
17 changes: 0 additions & 17 deletions ui/components.py

This file was deleted.

91 changes: 45 additions & 46 deletions ui/file_selector_ui.py
Original file line number Diff line number Diff line change
@@ -1,47 +1,52 @@
from PySide6.QtWidgets import (
QMainWindow,
QWidget,
QVBoxLayout,
QWidget,
QPushButton,
QHBoxLayout,
QCheckBox,
)
from PySide6.QtGui import QIcon
from PySide6.QtCore import Qt
from ui.file_extension_input import FileExtensionInput
from ui.file_tree_view import FileTreeView
from utils.file_helpers import save_last_selection, collect_file_data, copy_to_clipboard
from ui.ui_helpers import (
toggle_all_tree_items,
setup_main_layout,
create_submit_button,
create_select_all_checkbox,
from utils.file_helpers import (
save_app_state,
collect_file_data,
copy_to_clipboard,
)
from utils.ui_helpers import toggle_all_tree_items


class FileSelectorUI(QMainWindow):

def __init__(self, python_files, last_selection):
def __init__(self, python_files, app_state):
super().__init__()
self.setWindowTitle(" Spoon App")
self.setWindowTitle("Spoon App")
self.setWindowIcon(QIcon("resources/spoon.png"))
self.setGeometry(100, 100, 600, 400)

self.resize(400, 600)

self.selected_files = set(last_selection)
# Initialize the application state from app_state
self.selected_files = set(app_state["selected_files"])
self.select_all_state = app_state["select_all_state"]
self.file_extension = app_state["file_extension"]

# Main layout
main_layout = QVBoxLayout()

# File extension input with lock
self.file_extension_input = FileExtensionInput(".py", self.update_file_filter)
self.select_all_checkbox = create_select_all_checkbox(
self.file_extension_input, self.toggle_select_all_files
self.file_extension_input = FileExtensionInput(
self.file_extension, self.update_file_filter
)
self.select_all_checkbox = QCheckBox(f"Select all {self.file_extension} files")
self.select_all_checkbox.setChecked(self.select_all_state)
self.select_all_checkbox.stateChanged.connect(self.toggle_select_all_files)

self.tree_view = FileTreeView(
python_files, self.file_extension_input.get_extension(), last_selection
python_files, self.file_extension, self.selected_files
)
self.tree_view.itemChanged.connect(self.on_item_changed)
self.tree_view.itemChanged.connect(self.tree_view.on_item_changed)

# Buttons for expanding and collapsing the tree
button_layout = QHBoxLayout()
Expand All @@ -54,22 +59,16 @@ def __init__(self, python_files, last_selection):
button_layout.addWidget(collapse_button)
main_layout.addLayout(button_layout)

submit_button = create_submit_button(self.on_submit)
# Setup the layout with all widgets
main_layout.addLayout(self.file_extension_input.layout)
main_layout.addWidget(self.select_all_checkbox)
main_layout.addWidget(self.tree_view)

setup_main_layout(
main_layout,
self.file_extension_input,
self.select_all_checkbox,
self.tree_view,
submit_button,
)

# Set main widget and layout
container = QWidget()
container.setLayout(main_layout)
self.setCentralWidget(container)

# Ensure all items are selected initially and update checkbox state
# Ensure the initial state is reflected in the UI
self.toggle_select_all_files(initial=True)
self.update_select_all_checkbox()

Expand All @@ -83,53 +82,53 @@ def collapse_all(self):

def on_submit(self):
"""Handle submit button click."""
# Collect the selected files
self.selected_files = self.tree_view.get_selected_files()

if self.selected_files:
save_last_selection(self.selected_files)
collected_data = collect_file_data(self.selected_files)
# Save the application state, including selected files, "Select All" checkbox state, and file extension
save_app_state(
self.selected_files,
self.select_all_checkbox.isChecked(),
self.file_extension_input.get_extension(),
)

# Collect data from the selected files
full_paths = {
os.path.join(folder, filename)
for folder, filename in self.selected_files
}
collected_data = collect_file_data(full_paths)

# Format the collected data and copy to clipboard
formatted_data = "\n\n".join(
f"# {title}\n{content}" for title, content in collected_data.items()
f"# {file_path}\n{content}"
for file_path, content in collected_data.items()
)
copy_to_clipboard(formatted_data)

def update_file_filter(self):
"""Update the tree view based on the file extension input."""
extension = self.file_extension_input.get_extension()
self.tree_view.update_extension(extension)

# Ensure that the "Select All" checkbox reflects the correct state after filtering
self.update_select_all_checkbox()

# If the "Select All" checkbox is checked, ensure that all items in the tree are checked
# Ensure all items are selected if the "Select All" checkbox is checked
if self.select_all_checkbox.isChecked():
self.toggle_select_all_files()

def toggle_select_all_files(self, initial=False):
"""Select/deselect all files in the tree view and focus the tree."""
select_all = initial or self.select_all_checkbox.isChecked()
toggle_all_tree_items(self.tree_view, select_all)

# Focus the tree view when the select all checkbox is interacted with
self.tree_view.setFocus()

# Ensure the checkbox reflects the correct state
self.update_select_all_checkbox()

def on_item_changed(self, item):
"""Handle item state change to select/deselect children for folders."""
if item.childCount() > 0: # If it's a folder
self.tree_view.select_folder_children(item)

# After changing the state of an item, update the select all checkbox
self.update_select_all_checkbox()

def update_select_all_checkbox(self):
"""Update the state of the select all checkbox based on tree view items."""
all_items_checked = self.tree_view.are_all_items_checked()
any_items_checked = not self.tree_view.are_all_items_unchecked()

# Block signals to prevent infinite loops when setting the checkbox state
self.select_all_checkbox.blockSignals(True)

if all_items_checked:
Expand Down
Loading

0 comments on commit e2eaf11

Please sign in to comment.