first commit

This commit is contained in:
Rafał Paluch 2025-12-04 23:31:01 +01:00
commit 2863fc8284
8 changed files with 394 additions and 0 deletions

0
README.md Normal file
View File

56
cad Executable file
View File

@ -0,0 +1,56 @@
#!/bin/bash
if [ -z "$1" ]; then
echo "❌ Błąd: Musisz podać plik!"
echo "👉 Użycie: cad nazwa_pliku.py"
exit 1
fi
FILE=$1
FULL_PATH=$(realpath $FILE)
SESSION="cad_session"
LIVE_SCRIPT="/home/pali112/Build123d/live.py"
# 1. Zabijamy starą sesję
tmux kill-session -t $SESSION 2>/dev/null
echo "🚀 Tworzę sesję..."
# ==========================================
# OKNO 1: CODE
# ==========================================
tmux new-session -d -s $SESSION -n 'Code' "vim $FILE"
# ==========================================
# OKNO 2: SERVER
# ==========================================
echo "🖥️ Przygotowuję maszynownię..."
# 1. Tworzymy drugie okno
tmux new-window -t $SESSION -n 'Server'
sleep 0.5 # Daj mu chwilę na wstanie
# 2. URUCHAMIAMY SERWER (Góra)
# W tym momencie mamy tylko jeden panel, więc ślemy do niego.
tmux send-keys -t "${SESSION}:Server" "python3 -m ocp_vscode --host 0.0.0.0 --port 3939" C-m
# 3. DZIELIMY OKNO
echo "✂️ Dzielę ekran..."
# Dzielimy aktywne okno (Server).
# Flaga -d nie jest użyta, więc focus przenosi się do NOWEGO (dolnego) panelu.
# Używamy -l 15 zamiast %, żeby wymusić stałą wysokość (15 linii tekstu na dole) - to pewniejsze.
tmux split-window -t "${SESSION}:Server" -v -l 15
sleep 0.5 # Ważna pauza!
# 4. URUCHAMIAMY RUNNERA (Dół)
# Skoro po splicie kursor jest na dole, ślemy po prostu do okna Server.
tmux send-keys -t "${SESSION}:Server" "python3 $LIVE_SCRIPT $FULL_PATH" C-m
# ==========================================
# FINISH
# ==========================================
# Wracamy do kodu
tmux select-window -t "${SESSION}:Code"
echo "✅ Gotowe! Podłączam..."
tmux attach-session -t $SESSION

90
live.py Normal file
View File

@ -0,0 +1,90 @@
import sys
import time
import importlib
import os
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
from threading import Timer
# --- KONFIGURACJA ---
VIEWER_PORT = 3939
DELAY_SECONDS = 0.1 # Czeka na ciszę przez 0.5s zanim przeładuje
# --------------------
if len(sys.argv) < 2:
print("❌ Błąd: Nie podałeś nazwy pliku!")
sys.exit(1)
RAW_PATH = sys.argv[1]
ABS_PATH = os.path.abspath(RAW_PATH)
DIR_NAME = os.path.dirname(ABS_PATH)
FILE_NAME = os.path.basename(ABS_PATH)
MODULE_NAME = os.path.splitext(FILE_NAME)[0]
print(f"📂 Katalog roboczy: {DIR_NAME}")
os.chdir(DIR_NAME)
if "." not in sys.path:
sys.path.insert(0, ".")
# --- IMPORTY CAD ---
print(f"🔥 Ładuję biblioteki CAD...")
import build123d
from ocp_vscode import show, set_port
# Konfigurujemy klienta, żeby gadał z portem 3939
set_port(VIEWER_PORT)
print(f"🚀 GOTOWE! Nasłuchuję zmian w: {FILE_NAME}")
# --- OBSŁUGA ZMIAN PLIKU ---
class RebuildHandler(FileSystemEventHandler):
def __init__(self):
self.timer = None
def on_modified(self, event):
# Reagujemy tylko na zmianę TEGO konkretnego pliku
if os.path.abspath(event.src_path) == ABS_PATH:
# Jeśli zegar już tyka, anulujemy go (bo przyszło nowe zdarzenie = Vim jeszcze pisze)
if self.timer:
self.timer.cancel()
# Ustawiamy nowy timer. Odpali się tylko jeśli przez 0.5s nic nowego nie przyjdzie.
self.timer = Timer(DELAY_SECONDS, self.process_reload)
self.timer.start()
def process_reload(self):
print(f"♻️ Przeliczam {MODULE_NAME}... ", end="", flush=True)
start_time = time.time()
try:
# Magia przeładowania modułu bez restartu procesu
if MODULE_NAME in sys.modules:
importlib.reload(sys.modules[MODULE_NAME])
else:
importlib.import_module(MODULE_NAME)
print(f"Gotowe ({time.time() - start_time:.2f}s)")
except Exception as e:
# Łapiemy błędy składni/CAD, żeby runner się nie wywalił
print(f"\n❌ BŁĄD KODU:\n{e}")
if __name__ == "__main__":
# 1. Pierwsze uruchomienie przy starcie
try:
importlib.import_module(MODULE_NAME)
except Exception as e:
print(f"⚠️ Startowy błąd w kodzie: {e}")
# 2. Uruchomienie obserwatora (Watchdog)
event_handler = RebuildHandler()
observer = Observer()
# Obserwujemy katalog, ale w handlerze filtrujemy tylko nasz plik
observer.schedule(event_handler, path='.', recursive=False)
observer.start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
observer.stop()
print("\n🛑 Koniec pracy.")
observer.join()

19
my_configs.vim Normal file
View File

@ -0,0 +1,19 @@
" Dodaj globalne tagi dla biblioteki BOSL2
set tags+=~/.build123_tags
" Skrót do następnego bufora: \ + Tab
nnoremap <silent> <leader><Tab> :bnext<CR>
nnoremap <leader>w :w <bar> !python %<CR>
" Używaj ruff jako lintera (sprawdzanie błędów)
let g:ale_linters = {
\ 'python': ['flake8']
\}
" Używaj ruff jako fixera (do :ALEFix)
let g:ale_fixers = {
\ 'python': ['ruff_format']
\}
" <Leader>[ działa jak przełącznik: raz włącza, raz wyłącza tryb PASTE
nnoremap <leader>[ :set invpaste paste?<CR>

1
my_others/my_others Symbolic link
View File

@ -0,0 +1 @@
/home/pali112/Nextcloud/start/my_others

View File

@ -0,0 +1,85 @@
snippet build123d
from build123d import *
snippet ocp
from ocp_vscode import *
snippet box
Box(${1:length}, ${2:width}, ${3:height})
snippet text
Text("${1:what?}", font_size=${2:ile?})
snippet otwieramy
offset(amount=-${1:grubosc_scianki}, openings=${2:what}.faces().sort_by(Axis.Z)[-1])
snippet loc
with Locations((${1:x}, ${2:y}, ${3:z})):
snippet rec
Rectangle(${1:dlugosc}, ${2:szerokosc})
snippet filletpolyline
FilletPolyline((${1:pts1}, ${2:pts2}), (${3:pts3}, ${4:pts4}), (${5:pts5}, ${6:pts5}), radius=${7:fillet radius})
snippet part
with BuildPart() as ${1:what?}:
snippet sketch
with BuildSketch() as ${1:what?}:
snippet line
with BuildLine() as ${1:what?}:
snippet debug
debug(${1:what?})
snippet move
move(Location((${1:0}, ${2:0}, ${3:0})))
snippet extrude
extrude(amount=${1:ile?})
snippet align
align=Align.${1:what?}
snippet alignl
align=(Align.${1:what?}, Align.${2:what?}, Align.${3:what?})
snippet fs
${1:what?}.faces().sort_by(Axis.${2:what?})
snippet grid
with GridLocations(${1:x_spacing,}, ${2:y_spacing}, ${3:x_count}, ${4:y_count})
snippet mode
mode=Mode.${1:what?}
snippet fillet
fillet(${1:objects}, ${2:radius})
snippet circle
Circle(${1:radius})
snippet mirror
mirror(about=Plane.${1:XY})
snippet triangle
Triangle(a=${1:a}, ${2:b}, ${3:c}, ${4:opcional_Angle1}, ${5:optional_Angle2}, ${6:opcional_Angle3})
snippet cone
Cone(bottom_radius=${1:ile?}, top_radius=${2:ile?}, height=${3:ile?})
snippet scale
scale(${1:what?}, by=${2:scale_factor})
snippet show
show(${1:what?})
snippet showc
show(${1:what?}, colors=["${2:grey}"])
snippet joint_location
joint_location((${1:x}, ${2:y}, ${3:z}))
snippet recr
RectangleRounded(${1:dlugosc}, ${2:szerokosc}, ${3:radius})

104
skrotyApp.py Normal file
View File

@ -0,0 +1,104 @@
#!/usr/bin/env python3
import os
import tkinter as tk
from tkinter import filedialog, messagebox
import subprocess
def browse_exec():
path = filedialog.askopenfilename(title="Wybierz plik wykonywalny (AppImage/skrypt)")
if path:
entry_exec.delete(0, tk.END)
entry_exec.insert(0, path)
def browse_icon():
path = filedialog.askopenfilename(title="Wybierz ikonę", filetypes=[("Pliki graficzne", "*.png *.svg *.ico *.jpg *.jpeg")])
if path:
entry_icon.delete(0, tk.END)
entry_icon.insert(0, path)
def choose_location():
location = var_location.get()
if location == "local":
return os.path.expanduser("~/.local/share/applications")
elif location == "system":
return "/usr/share/applications"
elif location == "desktop":
return os.path.join(os.path.expanduser("~"), "Pulpit")
else:
return os.path.expanduser("~")
def create_desktop_entry():
name = entry_name.get().strip()
exec_path = entry_exec.get().strip()
icon_path = entry_icon.get().strip()
terminal = var_terminal.get()
if not name or not exec_path:
messagebox.showerror("Błąd", "Nazwa aplikacji i ścieżka do pliku wykonywalnego są wymagane.")
return
terminal_value = "true" if terminal else "false"
desktop_content = f"""[Desktop Entry]
Type=Application
Name={name}
Exec="{exec_path}"
{"Icon=" + icon_path if icon_path else ""}
Terminal={terminal_value}
"""
filename = name.lower().replace(" ", "_") + ".desktop"
folder = choose_location()
desktop_path = os.path.join(folder, filename)
try:
if folder.startswith("/usr"):
# Zapis przez pkexec do katalogu systemowego
tmpfile = f"/tmp/{filename}"
with open(tmpfile, "w") as f:
f.write(desktop_content)
subprocess.run(["pkexec", "cp", tmpfile, desktop_path], check=True)
subprocess.run(["pkexec", "chmod", "755", desktop_path], check=True)
os.remove(tmpfile)
else:
os.makedirs(folder, exist_ok=True)
with open(desktop_path, 'w') as f:
f.write(desktop_content)
os.chmod(desktop_path, 0o755)
messagebox.showinfo("Sukces", f"Skrót utworzony: {desktop_path}")
except Exception as e:
messagebox.showerror("Błąd", f"Nie udało się zapisać skrótu:\n{e}")
# GUI
root = tk.Tk()
root.title("Tworzenie skrótu .desktop")
tk.Label(root, text="Nazwa aplikacji:").grid(row=0, column=0, sticky="e")
entry_name = tk.Entry(root, width=40)
entry_name.grid(row=0, column=1, pady=2)
tk.Label(root, text="Ścieżka do pliku:").grid(row=1, column=0, sticky="e")
entry_exec = tk.Entry(root, width=40)
entry_exec.grid(row=1, column=1, pady=2)
tk.Button(root, text="Przeglądaj", command=browse_exec).grid(row=1, column=2)
tk.Label(root, text="Ikona (opcjonalnie):").grid(row=2, column=0, sticky="e")
entry_icon = tk.Entry(root, width=40)
entry_icon.grid(row=2, column=1, pady=2)
tk.Button(root, text="Przeglądaj", command=browse_icon).grid(row=2, column=2)
var_terminal = tk.BooleanVar()
tk.Checkbutton(root, text="Uruchamiane w terminalu", variable=var_terminal).grid(row=3, column=1, pady=4)
tk.Label(root, text="Miejsce zapisu:").grid(row=4, column=0, sticky="e")
var_location = tk.StringVar(value="local")
tk.Radiobutton(root, text="Lokalnie (~/.local/share/applications)", variable=var_location, value="local").grid(row=4, column=1, sticky="w")
tk.Radiobutton(root, text="Systemowo (/usr/share/applications)", variable=var_location, value="system").grid(row=5, column=1, sticky="w")
tk.Radiobutton(root, text="Pulpit", variable=var_location, value="desktop").grid(row=6, column=1, sticky="w")
tk.Button(root, text="Utwórz skrót", command=create_desktop_entry, bg="#4CAF50", fg="white").grid(row=7, column=1, pady=10)
root.mainloop()

39
start.sh Executable file
View File

@ -0,0 +1,39 @@
#!/bin/bash
echo "Utworzenie linków"
ln -s /home/pali112/Nextcloud/start/my_configs.vim /home/pali112/.vim_runtime/my_configs.vim
ln -s /home/pali112/Nextcloud/start/my_others /home/pali112/.vim_runtime/my_others
ln -s /home/pali112/Nextcloud/start/.zshrc /home/pali112/.zshrc
ln -s /home/pali112/Nextcloud/start/.build123_tags /home/pali112/.build123_tags
ln -s /home/pali112/Nextcloud/start/.flake8 /home/pali112/.flake8
ln -s /home/pali112/Nextcloud/start/.tmux.conf /home/pali112/.tmux.conf
ln -s /home/pali112/Nextcloud/start/live.py /home/pali112/Build123d/live.py
KATALOG="/home/pali112/.local/bin"
# 1. Sprawdzenie, czy katalog istnieje
if [ ! -d "$KATALOG" ]; then
# 2. Jeśli NIE istnieje (-d sprawdza katalog, ! odwraca wynik)
echo "Katalog $KATALOG nie istnieje. Tworzę go..."
# 3. Utworzenie katalogu
# Flaga -p zapewnia, że wszystkie potrzebne katalogi nadrzędne (.local) też zostaną utworzone, jeśli ich nie ma.
mkdir -p "$KATALOG"
# 4. Sprawdzenie, czy operacja się powiodła
if [ $? -eq 0 ]; then
echo "Katalog $KATALOG został pomyślnie utworzony."
else
echo "Błąd: Nie udało się utworzyć katalogu $KATALOG."
fi
else
# 5. Jeśli katalog już istnieje
echo "Katalog $KATALOG już istnieje wykonuję kolejne rzeczy"
fi
echo "Tworzenie linku cad"
ln -s /home/pali112/Nextcloud/start/cad /home/pali112/.local/bin/cad