From beeaa02c35f59bfb197b8b3bb87c12ce34837362 Mon Sep 17 00:00:00 2001 From: Scheianu Ionut Date: Sat, 20 Sep 2025 13:56:57 +0300 Subject: [PATCH] upfdated to ptint plugin --- py_app/app/__pycache__/routes.cpython-312.pyc | Bin 59071 -> 65588 bytes py_app/app/routes.py | 156 ++++++++++ py_app/app/templates/download_extension.html | 144 ++++++++++ py_app/app/templates/print_module.html | 84 ++++++ py_app/chrome_extension/background.js | 185 ++++++++++++ py_app/chrome_extension/content.js | 272 ++++++++++++++++++ py_app/chrome_extension/icons/README.md | 7 + py_app/chrome_extension/icons/create_icons.py | 27 ++ py_app/chrome_extension/icons/icon128.txt | 4 + py_app/chrome_extension/icons/icon16.txt | 4 + py_app/chrome_extension/icons/icon48.txt | 4 + py_app/chrome_extension/manifest.json | 44 +++ py_app/chrome_extension/popup.html | 102 +++++++ py_app/chrome_extension/popup.js | 29 ++ 14 files changed, 1062 insertions(+) create mode 100644 py_app/app/templates/download_extension.html create mode 100644 py_app/chrome_extension/background.js create mode 100644 py_app/chrome_extension/content.js create mode 100644 py_app/chrome_extension/icons/README.md create mode 100644 py_app/chrome_extension/icons/create_icons.py create mode 100644 py_app/chrome_extension/icons/icon128.txt create mode 100644 py_app/chrome_extension/icons/icon16.txt create mode 100644 py_app/chrome_extension/icons/icon48.txt create mode 100644 py_app/chrome_extension/manifest.json create mode 100644 py_app/chrome_extension/popup.html create mode 100644 py_app/chrome_extension/popup.js diff --git a/py_app/app/__pycache__/routes.cpython-312.pyc b/py_app/app/__pycache__/routes.cpython-312.pyc index 256aa1f820e349cf7d686a0ea8b9e6ae80d71b48..875aedaf4a2190fd34fe984bc0a04b4fe6c793ad 100644 GIT binary patch delta 6621 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 CX^Q*+ delta 998 zcmZuveMnPL6o2<^dz+iuU%JXHjbzOxR8(XUn3VKEABtp{wZ`7cp>B1zLL+KOAyg82 zA}T6_qSE=1TuUnk+p^+_8ag~-r(K#yEA3YxD{_Z)y^Ks8PEdlM5*P7T@ zF)>C7znfz(3qvofVrR3AX3GnO#lwiQ+@QuEWc9OyGAz>P8&vc3C7`#-K?W^TM_6y0 zF~uA98`XGZ%9C!ThCK<>At;F?sm@bUa0*KK*)S4iUZBqKrwpp=A5IQ&AU$eNxo6NT8%VtY8d{&REZ}XI z*(Qj7m;}y_a%Bse?r7j^$2H?Ngh355w~Few&oy%=y#_GYM9g}p)8+FesVVC_;LtxA;hj$H(`P$_}M{SLZkg7NdJ8dfIAtdk%QH2h#d)n2w>f4@>rO z83LorMAS#KMt1_0OCV+7EISU(14+67-j_nxK*g*AL{`RQF&rKRQ2aSTcN%po;oRqT ztbKDhnR%cnJWru|Fh^xLQFjL6TG13<$8?nlGezV}zija!JQ(h+pD1WhGD3>a@9?{w zoVuB7Wg&yB)a~=zy+trG7-Z+*>d?HnP!#ui{q{o+e__Z_;wW;dZtjDpLpIZS^x{<{ zJTN{~uDgwJvj_|?mf23I9XX@alI<>}N9HR*ghg7hH?onT@#eSd<~lMqlF-w;DOz(`&MHnyhcMR=$D?%Y~Ysy|{U0zi#cy zB)`f1$|6K<') +def extension_files(filename): + """Serve Chrome extension files for download""" + import os + from flask import send_from_directory, current_app + + extension_dir = os.path.join(os.path.dirname(current_app.root_path), 'chrome_extension') + return send_from_directory(extension_dir, filename) + +@bp.route('/create_extension_package', methods=['POST']) +def create_extension_package(): + """Create and serve ZIP package of Chrome extension""" + import os + import zipfile + from flask import current_app, jsonify, send_file + import tempfile + + try: + # Use correct path to chrome_extension directory (it's in py_app, not py_app/app) + extension_dir = os.path.join(os.path.dirname(current_app.root_path), 'chrome_extension') + print(f"Looking for extension files in: {extension_dir}") + + if not os.path.exists(extension_dir): + return jsonify({ + 'success': False, + 'error': f'Extension directory not found: {extension_dir}' + }), 500 + + # List files in extension directory for debugging + all_files = [] + for root, dirs, files in os.walk(extension_dir): + for file in files: + file_path = os.path.join(root, file) + all_files.append(file_path) + + print(f"Found files: {all_files}") + + # Create static directory if it doesn't exist + static_dir = os.path.join(current_app.root_path, 'static') + os.makedirs(static_dir, exist_ok=True) + + zip_filename = 'quality_recticel_print_helper.zip' + zip_path = os.path.join(static_dir, zip_filename) + + # Create ZIP file directly in static directory + with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf: + files_added = 0 + + # Add all extension files to ZIP + for root, dirs, files in os.walk(extension_dir): + for file in files: + # Include all relevant files + if file.endswith(('.json', '.js', '.html', '.css', '.png', '.md', '.txt')): + file_path = os.path.join(root, file) + # Create relative path for archive + arcname = os.path.relpath(file_path, extension_dir) + + print(f"Adding file: {file_path} as {arcname}") + zipf.write(file_path, arcname) + files_added += 1 + + # Add a README file with installation instructions + readme_content = """# Quality Recticel Print Helper Chrome Extension + +## Installation Instructions: + +1. Extract this ZIP file to a folder on your computer +2. Open Chrome and go to: chrome://extensions/ +3. Enable "Developer mode" in the top right +4. Click "Load unpacked" and select the extracted folder +5. The extension icon 🖨️ should appear in your toolbar + +## Usage: + +1. Go to the Print Module in the Quality Recticel application +2. Select an order from the table +3. Click the "🖨️ Print Direct" button +4. The label will print automatically to your default printer + +## Troubleshooting: + +- Make sure your default printer is set up correctly +- Click the extension icon to test printer connection +- Check Chrome printer settings: chrome://settings/printing + +For support, contact your system administrator. +""" + zipf.writestr('README.txt', readme_content) + files_added += 1 + + print(f"Total files added to ZIP: {files_added}") + + # Verify ZIP was created and has content + if os.path.exists(zip_path): + zip_size = os.path.getsize(zip_path) + print(f"ZIP file created: {zip_path}, size: {zip_size} bytes") + + if zip_size > 0: + return jsonify({ + 'success': True, + 'download_url': f'/static/{zip_filename}', + 'files_included': files_added, + 'zip_size': zip_size + }) + else: + return jsonify({ + 'success': False, + 'error': 'ZIP file was created but is empty' + }), 500 + else: + return jsonify({ + 'success': False, + 'error': 'Failed to create ZIP file' + }), 500 + + except Exception as e: + print(f"Error creating extension package: {e}") + import traceback + traceback.print_exc() + return jsonify({ + 'success': False, + 'error': str(e) + }), 500 + +@bp.route('/test_extension_files') +def test_extension_files(): + """Test route to check extension files""" + import os + from flask import current_app, jsonify + + extension_dir = os.path.join(os.path.dirname(current_app.root_path), 'chrome_extension') + + result = { + 'extension_dir': extension_dir, + 'dir_exists': os.path.exists(extension_dir), + 'files': [] + } + + if os.path.exists(extension_dir): + for root, dirs, files in os.walk(extension_dir): + for file in files: + file_path = os.path.join(root, file) + file_size = os.path.getsize(file_path) + result['files'].append({ + 'path': file_path, + 'relative_path': os.path.relpath(file_path, extension_dir), + 'size': file_size + }) + + return jsonify(result) + @bp.route('/label_templates') def label_templates(): return render_template('label_templates.html') diff --git a/py_app/app/templates/download_extension.html b/py_app/app/templates/download_extension.html new file mode 100644 index 0000000..45beacc --- /dev/null +++ b/py_app/app/templates/download_extension.html @@ -0,0 +1,144 @@ +{% extends "base.html" %} + +{% block title %}Chrome Extension Download{% endblock %} + +{% block content %} +
+
+
+
+
+

🖨️ Quality Recticel Print Helper - Chrome Extension

+
+
+
+ Direct Printing Solution: This Chrome extension enables direct printing from the Print Module to your default printer without browser dialogs. +
+ +

Features:

+
    +
  • ✅ Print labels directly to default printer
  • +
  • ✅ No browser print dialogs
  • +
  • ✅ Automatic printer detection
  • +
  • ✅ Print status notifications
  • +
  • ✅ Enhanced print button with visual feedback
  • +
+ +

Installation Instructions:

+
    +
  1. Download Extension Files: +
    + +

    +
    + Alternative: Download individual files: +
    + manifest.json | + background.js | + content.js | + popup.html | + popup.js +
    +
    +
  2. + +
  3. Extract Files: +

    Extract the downloaded ZIP file to a folder on your computer (e.g., Desktop/print_extension)

    +
  4. + +
  5. Open Chrome Extensions: +

    In Google Chrome, go to: chrome://extensions/

    +
  6. + +
  7. Enable Developer Mode: +

    Toggle the "Developer mode" switch in the top-right corner

    +
  8. + +
  9. Load Extension: +

    Click "Load unpacked" and select the folder where you extracted the files

    +
  10. + +
  11. Verify Installation: +

    The extension icon 🖨️ should appear in your Chrome toolbar

    +
  12. +
+ +

Usage:

+
    +
  1. Navigate to the Print Module
  2. +
  3. Select an order from the table
  4. +
  5. Click the enhanced "🖨️ Print Direct" button
  6. +
  7. The label will print automatically to your default printer
  8. +
+ +
+ System Requirements: +
    +
  • Google Chrome browser (version 88 or higher)
  • +
  • Default printer configured on your system
  • +
  • Printer drivers installed and working
  • +
+
+ +
+ Troubleshooting: +
    +
  • Click the extension icon 🖨️ to test printer connection
  • +
  • Check Chrome's printer settings: chrome://settings/printing
  • +
  • Ensure your default printer is set correctly
  • +
  • Reload the Print Module page after installing the extension
  • +
+
+ +
+ + 🖨️ Go to Print Module + + +
+
+
+
+
+
+ + +{% endblock %} \ No newline at end of file diff --git a/py_app/app/templates/print_module.html b/py_app/app/templates/print_module.html index 0d67043..2d1d575 100644 --- a/py_app/app/templates/print_module.html +++ b/py_app/app/templates/print_module.html @@ -239,6 +239,11 @@ table tbody tr.selected td { + @@ -349,6 +354,9 @@ document.getElementById('check-db-btn').addEventListener('click', function() { if (data.length > 0) { updateLabelPreview(data[0]); + // Add fallback print functionality if extension is not available + addFallbackPrintHandler(); + // Auto-select first row setTimeout(() => { const firstRow = document.querySelector('.print-module-table tbody tr'); @@ -428,5 +436,81 @@ function updateLabelPreview(order) { // Update barcode with the same prod order data document.getElementById('barcode-text').textContent = prodOrder; } + +// Fallback print handler for when Chrome extension is not available +function addFallbackPrintHandler() { + // Check if Chrome extension modified the button + setTimeout(() => { + const printButton = document.getElementById('print-label-btn'); + + // If button text hasn't changed, extension is not active + if (printButton && !printButton.innerHTML.includes('🖨️ Print Direct')) { + console.log('Chrome extension not detected, adding fallback print handler'); + + // Add fallback event listener + printButton.addEventListener('click', function(e) { + e.preventDefault(); + + const labelPreview = document.getElementById('label-preview'); + if (!labelPreview) { + alert('Please select an order first.'); + return; + } + + // Create a new window for printing + const printWindow = window.open('', '_blank'); + const printContent = ` + + + + Quality Recticel Label + + + + + + + `; + + printWindow.document.write(printContent); + printWindow.document.close(); + + // Wait for content to load, then print + printWindow.onload = function() { + printWindow.focus(); + printWindow.print(); + printWindow.close(); + }; + }); + + // Update button text to indicate fallback mode + printButton.innerHTML = '🖨️ Print (Browser)'; + printButton.title = 'Print using browser dialog - Install Chrome Extension for direct printing'; + } + }, 2000); // Wait 2 seconds for extension to load +} {% endblock %} \ No newline at end of file diff --git a/py_app/chrome_extension/background.js b/py_app/chrome_extension/background.js new file mode 100644 index 0000000..a8f7f23 --- /dev/null +++ b/py_app/chrome_extension/background.js @@ -0,0 +1,185 @@ +// Background service worker for Chrome extension +console.log('Quality Recticel Print Helper - Background script loaded'); + +// Listen for messages from content script +chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { + console.log('Background received message:', request); + + if (request.action === 'print_label') { + handlePrintLabel(request.data, sendResponse); + return true; // Keep message channel open for async response + } + + if (request.action === 'fallback_print') { + handleFallbackPrint(request.data, sendResponse); + return true; + } + + if (request.action === 'get_printers') { + getPrinters(sendResponse); + return true; + } + + if (request.action === 'check_extension') { + sendResponse({status: 'installed', version: chrome.runtime.getManifest().version}); + return true; + } +}); + +// Function to handle label printing +async function handlePrintLabel(printData, sendResponse) { + try { + console.log('Attempting to print label with data:', printData); + + // Check if printing API is available + if (!chrome.printing || !chrome.printing.getPrinters) { + console.error('Chrome printing API not available'); + sendResponse({ + success: false, + error: 'Chrome printing API not available. Please ensure you are using Chrome 85+ and the extension has proper permissions.' + }); + return; + } + + // Get available printers + const printers = await chrome.printing.getPrinters(); + console.log('Available printers:', printers); + + if (printers.length === 0) { + sendResponse({success: false, error: 'No printers found. Please ensure a printer is installed and set as default.'}); + return; + } + + // Find default printer or use first available + const defaultPrinter = printers.find(p => p.isDefault) || printers[0]; + console.log('Using printer:', defaultPrinter); + + // Create print job + const printJob = { + printerId: defaultPrinter.id, + ticket: { + version: '1.0', + print: { + color: { + type: 'STANDARD_MONOCHROME' + }, + duplex: { + type: 'NO_DUPLEX' + }, + page_orientation: { + type: 'PORTRAIT' + }, + copies: { + copies: 1 + }, + dpi: { + horizontal_dpi: 300, + vertical_dpi: 300 + }, + media_size: { + width_microns: 210000, // A4 width + height_microns: 297000 // A4 height + }, + collate: { + collate: false + } + } + }, + documentBlob: new Blob([printData.html], {type: 'text/html'}) + }; + + // Submit print job + const result = await chrome.printing.submitJob(printJob); + console.log('Print job result:', result); + + if (result.status === 'OK') { + sendResponse({success: true, jobId: result.jobId}); + + // Store successful print in extension storage + chrome.storage.local.set({ + lastPrint: { + timestamp: Date.now(), + jobId: result.jobId, + printer: defaultPrinter.displayName + } + }); + } else { + sendResponse({success: false, error: result.status}); + } + + } catch (error) { + console.error('Print error:', error); + sendResponse({success: false, error: error.message}); + } +} + +// Function to get available printers +async function getPrinters(sendResponse) { + try { + // Check if printing API is available + if (!chrome.printing || !chrome.printing.getPrinters) { + sendResponse({ + success: false, + error: 'Chrome printing API not available' + }); + return; + } + + const printers = await chrome.printing.getPrinters(); + sendResponse({success: true, printers: printers}); + } catch (error) { + console.error('Error getting printers:', error); + sendResponse({success: false, error: error.message}); + } +} + +// Extension installation/startup +chrome.runtime.onInstalled.addListener((details) => { + console.log('Quality Recticel Print Helper installed:', details); + + // Set initial storage values + chrome.storage.local.set({ + extensionVersion: chrome.runtime.getManifest().version, + installDate: Date.now() + }); +}); + +// Fallback print method using tabs API +async function handleFallbackPrint(printData, sendResponse) { + try { + console.log('Using fallback print method'); + + // Create a new tab with the print content + const tab = await chrome.tabs.create({ + url: 'data:text/html,' + encodeURIComponent(printData.html), + active: false + }); + + // Wait for tab to load, then print + setTimeout(async () => { + try { + await chrome.tabs.executeScript(tab.id, { + code: 'window.print();' + }); + + // Close the tab after printing + setTimeout(() => { + chrome.tabs.remove(tab.id); + }, 1000); + + sendResponse({success: true, method: 'fallback'}); + } catch (error) { + sendResponse({success: false, error: 'Fallback print failed: ' + error.message}); + } + }, 1000); + + } catch (error) { + console.error('Fallback print error:', error); + sendResponse({success: false, error: error.message}); + } +} + +// Keep service worker alive +chrome.runtime.onConnect.addListener((port) => { + console.log('Port connected:', port.name); +}); \ No newline at end of file diff --git a/py_app/chrome_extension/content.js b/py_app/chrome_extension/content.js new file mode 100644 index 0000000..71ff214 --- /dev/null +++ b/py_app/chrome_extension/content.js @@ -0,0 +1,272 @@ +// Content script for Quality Recticel Print Helper +console.log('Quality Recticel Print Helper - Content script loaded'); + +// Check if we're on the print module page +if (window.location.pathname.includes('print_module')) { + console.log('Print module page detected, initializing extension features'); + + // Wait for DOM to be ready + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initializePrintExtension); + } else { + initializePrintExtension(); + } +} + +function initializePrintExtension() { + console.log('Initializing print extension features'); + + // Add extension status indicator + addExtensionStatusIndicator(); + + // Override the print button functionality + overridePrintButton(); + + // Add printer selection dropdown + addPrinterSelection(); + + // Check extension status + checkExtensionStatus(); +} + +function addExtensionStatusIndicator() { + // Create status indicator element + const statusIndicator = document.createElement('div'); + statusIndicator.id = 'extension-status'; + statusIndicator.style.cssText = ` + position: fixed; + top: 10px; + right: 10px; + background: #28a745; + color: white; + padding: 5px 10px; + border-radius: 4px; + font-size: 12px; + z-index: 9999; + box-shadow: 0 2px 4px rgba(0,0,0,0.2); + `; + statusIndicator.textContent = '🖨️ Print Extension Active'; + + document.body.appendChild(statusIndicator); + + // Hide after 3 seconds + setTimeout(() => { + statusIndicator.style.opacity = '0'; + statusIndicator.style.transition = 'opacity 0.5s'; + setTimeout(() => statusIndicator.remove(), 500); + }, 3000); +} + +function overridePrintButton() { + // Find the print button + const printButton = document.getElementById('print-label-btn'); + + if (printButton) { + console.log('Found print button, overriding functionality'); + + // Remove existing event listeners by cloning the element + const newPrintButton = printButton.cloneNode(true); + printButton.parentNode.replaceChild(newPrintButton, printButton); + + // Add new event listener for extension printing + newPrintButton.addEventListener('click', handleExtensionPrint); + + // Update button text to indicate direct printing + newPrintButton.innerHTML = '🖨️ Print Direct'; + newPrintButton.title = 'Print directly to default printer (Chrome Extension)'; + + console.log('Print button override complete'); + } else { + console.log('Print button not found, will retry...'); + // Retry after 1 second if button not found yet + setTimeout(overridePrintButton, 1000); + } +} + +function handleExtensionPrint(event) { + event.preventDefault(); + event.stopPropagation(); + + console.log('Extension print button clicked'); + + // Get the label preview element + const labelPreview = document.getElementById('label-preview'); + + if (!labelPreview) { + alert('Label preview not found. Please select an order first.'); + return; + } + + // Show printing status + showPrintStatus('Preparing to print...', 'info'); + + // Create complete HTML for printing + const printHTML = createPrintableHTML(labelPreview); + + // Try direct printing first, then fallback + chrome.runtime.sendMessage({ + action: 'print_label', + data: { + html: printHTML, + timestamp: Date.now() + } + }, (response) => { + if (response && response.success) { + handlePrintResponse(response); + } else { + console.log('Direct printing failed, trying fallback method'); + showPrintStatus('Direct printing unavailable, using browser print...', 'info'); + + // Try fallback method + chrome.runtime.sendMessage({ + action: 'fallback_print', + data: { + html: printHTML, + timestamp: Date.now() + } + }, handlePrintResponse); + } + }); +} + +function createPrintableHTML(labelElement) { + // Get the computed styles and create a complete HTML document + const styles = ` + + `; + + const bodyContent = ` + + `; + + return ` + + + + + Quality Recticel Label + ${styles} + + + ${bodyContent} + + + `; +} + +function handlePrintResponse(response) { + console.log('Print response:', response); + + if (response && response.success) { + showPrintStatus('✅ Label sent to printer successfully!', 'success'); + + // Update the order status in the table if possible + updateOrderPrintStatus(); + + } else { + const errorMsg = response?.error || 'Unknown error occurred'; + showPrintStatus(`❌ Print failed: ${errorMsg}`, 'error'); + + // Fallback to browser print dialog + setTimeout(() => { + if (confirm('Direct printing failed. Open browser print dialog?')) { + window.print(); + } + }, 2000); + } +} + +function showPrintStatus(message, type) { + // Remove existing status messages + const existingStatus = document.getElementById('print-status-message'); + if (existingStatus) { + existingStatus.remove(); + } + + // Create status message + const statusDiv = document.createElement('div'); + statusDiv.id = 'print-status-message'; + statusDiv.style.cssText = ` + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: ${type === 'success' ? '#28a745' : type === 'error' ? '#dc3545' : '#17a2b8'}; + color: white; + padding: 15px 25px; + border-radius: 8px; + font-size: 14px; + z-index: 10000; + box-shadow: 0 4px 8px rgba(0,0,0,0.3); + text-align: center; + min-width: 300px; + `; + statusDiv.textContent = message; + + document.body.appendChild(statusDiv); + + // Auto-remove after 3 seconds for success/info, 5 seconds for errors + const timeout = type === 'error' ? 5000 : 3000; + setTimeout(() => { + statusDiv.style.opacity = '0'; + statusDiv.style.transition = 'opacity 0.5s'; + setTimeout(() => statusDiv.remove(), 500); + }, timeout); +} + +function addPrinterSelection() { + // Get available printers and add selection dropdown + chrome.runtime.sendMessage({action: 'get_printers'}, (response) => { + if (response && response.success && response.printers.length > 0) { + console.log('Available printers:', response.printers); + // Could add printer selection UI here if needed + } + }); +} + +function updateOrderPrintStatus() { + // Find selected row in table and update print status + const selectedRow = document.querySelector('.print-module-table tbody tr.selected'); + if (selectedRow) { + const printStatusCell = selectedRow.querySelector('td:nth-last-child(2)'); // Second to last column + if (printStatusCell) { + printStatusCell.innerHTML = '✓ Yes'; + } + } +} + +function checkExtensionStatus() { + // Verify extension is working + chrome.runtime.sendMessage({action: 'check_extension'}, (response) => { + if (response) { + console.log('Extension status:', response); + } else { + console.error('Extension communication failed'); + } + }); +} + +// Add CSS for extension-specific styling +const extensionStyles = document.createElement('style'); +extensionStyles.textContent = ` + #print-label-btn { + background: linear-gradient(45deg, #28a745, #20c997) !important; + border: none !important; + transition: all 0.3s ease !important; + } + + #print-label-btn:hover { + background: linear-gradient(45deg, #218838, #1ea97c) !important; + transform: translateY(-1px) !important; + box-shadow: 0 4px 8px rgba(0,0,0,0.2) !important; + } +`; +document.head.appendChild(extensionStyles); \ No newline at end of file diff --git a/py_app/chrome_extension/icons/README.md b/py_app/chrome_extension/icons/README.md new file mode 100644 index 0000000..5a5a982 --- /dev/null +++ b/py_app/chrome_extension/icons/README.md @@ -0,0 +1,7 @@ +# Placeholder for icon files - in production, add actual PNG icons: +# - icon16.png (16x16 pixels) +# - icon48.png (48x48 pixels) +# - icon128.png (128x128 pixels) + +# For now, create simple text-based icons using SVG converted to PNG +# These should be replaced with proper icons later \ No newline at end of file diff --git a/py_app/chrome_extension/icons/create_icons.py b/py_app/chrome_extension/icons/create_icons.py new file mode 100644 index 0000000..a051067 --- /dev/null +++ b/py_app/chrome_extension/icons/create_icons.py @@ -0,0 +1,27 @@ +# Simple text-based icons for the Chrome extension +# These are placeholder files - replace with actual PNG icons for production + +# Create simple SVG icons that can be converted to PNG +ICON_16_SVG = ''' + + + 🖨 + +''' + +ICON_48_SVG = ''' + + + 🖨️ + +''' + +ICON_128_SVG = ''' + + + 🖨️ + +''' + +# For now, create simple text placeholders +# In production, convert these SVGs to PNG files \ No newline at end of file diff --git a/py_app/chrome_extension/icons/icon128.txt b/py_app/chrome_extension/icons/icon128.txt new file mode 100644 index 0000000..6b301ee --- /dev/null +++ b/py_app/chrome_extension/icons/icon128.txt @@ -0,0 +1,4 @@ +# Placeholder for 128x128 icon +# This is a text file placeholder +# Replace with actual icon128.png file +🖨️ \ No newline at end of file diff --git a/py_app/chrome_extension/icons/icon16.txt b/py_app/chrome_extension/icons/icon16.txt new file mode 100644 index 0000000..8b2cace --- /dev/null +++ b/py_app/chrome_extension/icons/icon16.txt @@ -0,0 +1,4 @@ +# Placeholder for 16x16 icon +# This is a text file placeholder +# Replace with actual icon16.png file +🖨️ \ No newline at end of file diff --git a/py_app/chrome_extension/icons/icon48.txt b/py_app/chrome_extension/icons/icon48.txt new file mode 100644 index 0000000..521d5ba --- /dev/null +++ b/py_app/chrome_extension/icons/icon48.txt @@ -0,0 +1,4 @@ +# Placeholder for 48x48 icon +# This is a text file placeholder +# Replace with actual icon48.png file +🖨️ \ No newline at end of file diff --git a/py_app/chrome_extension/manifest.json b/py_app/chrome_extension/manifest.json new file mode 100644 index 0000000..5dc6122 --- /dev/null +++ b/py_app/chrome_extension/manifest.json @@ -0,0 +1,44 @@ +{ + "manifest_version": 3, + "name": "Quality Recticel Print Helper", + "version": "1.0", + "description": "Direct printing extension for Quality Recticel label printing system", + "permissions": [ + "activeTab", + "storage", + "tabs" + ], + "optional_permissions": [ + "printing" + ], + "host_permissions": [ + "http://localhost:*/*", + "http://127.0.0.1:*/*", + "*://*/print_module*" + ], + "background": { + "service_worker": "background.js" + }, + "content_scripts": [ + { + "matches": [ + "http://localhost:*/print_module*", + "http://127.0.0.1:*/print_module*", + "*://*/print_module*" + ], + "js": ["content.js"], + "run_at": "document_end" + } + ], + "action": { + "default_popup": "popup.html", + "default_title": "Quality Recticel Print Helper" + }, + + "web_accessible_resources": [ + { + "resources": ["content.js"], + "matches": [""] + } + ] +} \ No newline at end of file diff --git a/py_app/chrome_extension/popup.html b/py_app/chrome_extension/popup.html new file mode 100644 index 0000000..508f951 --- /dev/null +++ b/py_app/chrome_extension/popup.html @@ -0,0 +1,102 @@ + + + + + Quality Recticel Print Helper + + + +
+ +
Quality Recticel Print Helper
+
Version 1.0
+
+ +
+ ✅ Extension Active +
+ +
+ This extension enables direct printing from the Quality Recticel label system to your default printer without browser dialogs. +
+ + + + +
+ Usage: Navigate to the Print Module page and use the enhanced print button for direct printing. +
+ + + + \ No newline at end of file diff --git a/py_app/chrome_extension/popup.js b/py_app/chrome_extension/popup.js new file mode 100644 index 0000000..03b4ed5 --- /dev/null +++ b/py_app/chrome_extension/popup.js @@ -0,0 +1,29 @@ +// Popup script for Quality Recticel Print Helper +document.addEventListener('DOMContentLoaded', function() { + console.log('Popup loaded'); + + // Test print button + document.getElementById('test-print').addEventListener('click', function() { + chrome.runtime.sendMessage({action: 'get_printers'}, function(response) { + if (response && response.success) { + if (response.printers.length > 0) { + alert(`✅ Found ${response.printers.length} printer(s):\n${response.printers.map(p => p.displayName).join('\n')}`); + } else { + alert('⚠️ No printers found. Please ensure a printer is installed and set as default.'); + } + } else { + const error = response?.error || 'Unknown error'; + if (error.includes('Chrome printing API not available')) { + alert('⚠️ Chrome Printing API not available.\n\nThis may be due to:\n• Chrome version too old (need 85+)\n• Extension permissions not granted\n• Corporate/managed Chrome installation\n\nThe extension will use browser print dialog as fallback.'); + } else { + alert('❌ Error getting printers: ' + error); + } + } + }); + }); + + // Settings button + document.getElementById('open-settings').addEventListener('click', function() { + chrome.tabs.create({url: 'chrome://settings/printing'}); + }); +}); \ No newline at end of file