12 |
13 | ## Features
14 |
15 | - **No backend required** - Works entirely in your browser
16 | - **Customizable labels** - Set custom height (9-24mm) and width (20-100mm) values with precise control
17 | - **Custom icon loading** - Upload your own icons or use the built-in collection
18 | - **Real-time preview** - See your label at actual size before downloading
19 | - **Multi-column support** - Create labels with multiple columns when your container has sections
20 | - **YAML batch processor** - Generate multiple labels at once using YAML input
21 | - **PNG export** - Download labels as high-quality PNG files ready for printing
22 |
23 | ## Example Output
24 |
25 | Here are some examples of labels generated with halagen:
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | ## Multi-column labels
42 |
43 | halagen supports multi-column labels for when your storage containers have multiple sections. This feature allows you to create a single label that covers multiple compartments, with each column representing a different section of your container. Simply specify the number of columns needed and fill in the content for each section.
44 |
45 | ## YAML batch processor
46 |
47 | halagen includes a powerful YAML batch processor that allows you to generate multiple labels simultaneously. This feature is particularly useful when you need to create many labels at once.
48 |
49 | **AI/LLM Integration**: The YAML format is designed to work seamlessly with AI assistants and Large Language Models (LLMs). Simply take fore example a screenshot of your hardware shopping basket or current organizer, and ask an AI to generate the YAML configuration for all the parts it can identify. This makes organizing large quantities of hardware components incredibly efficient.
50 |
51 |
52 |
53 |
54 |
55 | Example YAML format:
56 | ```yaml
57 | # Global settings (optional)
58 | width_mm: 50
59 | height_mm: 12
60 | png_dpi: 300
61 |
62 | labels:
63 | - title: "M4 × 12"
64 | subtext: "DIN 7984"
65 | icon: "heads_hex_socket"
66 | rotate: false
67 | - title: "M6 × 20"
68 | subtext: "Hex Bolt"
69 | icon: "fasteners_screw_hex"
70 | width_mm: 45
71 | height_mm: 18
72 | ```
73 |
74 | ## License
75 |
76 | This project is open source and available under the [MIT License](LICENSE).
77 |
78 | ## Icon attribution
79 |
80 | Some of the icons in the current icon set are based on designs by **Joe Jankowiak**, available at: https://www.printables.com/model/621771-gridfinity-bin-label-icons
81 |
82 | Used under the Creative Commons Attribution 4.0 International License (CC BY 4.0).
83 | https://creativecommons.org/licenses/by/4.0/
--------------------------------------------------------------------------------
/TODO.md:
--------------------------------------------------------------------------------
1 | # Feature Requests from Reddit Feedback
2 |
3 | ## **Priority Recommendations**
4 |
5 | **Phase 1 (Quick wins):**
6 | - UI Framework Migration (Bootstrap 5)
7 | - Icon Count Selection Feature (0/1/2 icons)
8 | - Text centering
9 | - Toggle second row
10 |
11 | **Phase 2 (Enhanced functionality):**
12 | - Basic crimping icons
13 | - Icon overlay system
14 | - QR code integration
15 |
16 | ## **Future Considerations / Maybe Later**
17 | - **Direct printing integration**: Enable direct printing to label printers without saving as PNG first
18 | - Would require integration with browser printing APIs or label printer SDKs
19 | - Complex implementation, uncertain browser support
20 | - Users can continue using current PNG → label printer workflow
21 |
22 | # Feature Details
23 |
24 | ## **UI Framework Migration**
25 | **Move to Bootstrap 5 for better component system and styling**
26 |
27 | **Implementation Plan:**
28 | - Add Bootstrap 5 CSS to existing HTML (CDN or local copy)
29 | - Migrate existing form controls to Bootstrap classes
30 | - Update button styling to use Bootstrap button components
31 | - Implement Bootstrap button groups for icon count selector
32 | - Use Bootstrap grid system for better layout balance
33 | - Update input fields to use Bootstrap form controls
34 | - Maintain current functionality while improving visual consistency
35 | - Test responsive behavior on different screen sizes
36 |
37 | **Benefits:**
38 | - Professional, consistent styling out of the box
39 | - Ready-made components (button groups, form controls, modals)
40 | - No build process required - works with current static setup
41 | - Easier to implement future UI features
42 |
43 | ## **Icon Count Selection Feature**
44 | **Addresses no-image mode and dual icon support requests**
45 |
46 | **Implementation Plan:**
47 | - Add icon count selector as Bootstrap-style button group with three buttons: `[0] [1] [2]`
48 | - Position above height setting, left of width setting for UI balance
49 | - Default state: 1 icon (current behavior)
50 | - Button group styling: Connected buttons, active button highlighted, inactive buttons subdued
51 |
52 | **Behavior per mode:**
53 | - **0 Icons**: Hide all icon selectors, text area expands to full label width
54 | - **1 Icon**: Current behavior, single icon selector visible
55 | - **2 Icons**: Show two icon selectors (Icon 1, Icon 2), arrange side by side in label
56 |
57 | **Layout adjustments:**
58 | - 0 icons: Text spans full width, centered or left-aligned based on text alignment setting
59 | - 1 icon: Current layout (icon left, text right)
60 | - 2 icons: Icons positioned left side (stacked or side-by-side), text area adjusted accordingly
61 |
62 | **UI considerations:**
63 | - When switching from 2→1 icons, preserve Icon 1 selection, clear Icon 2
64 | - When switching from 1→0 icons, hide icon selector but preserve selection for when user switches back
65 | - Update preview canvas in real-time when mode changes
66 |
67 | ## **Text Centering/Justification**
68 | Add center alignment option for text
69 |
70 | ## **Toggle Second Row**
71 | Option to disable subtitle/subtext completely for single-line labels
72 |
73 | ## **Crimping/Electrical Icons**
74 | Add ferrules, terminal connections, and other electrical component icons
75 |
76 | ## **Icon Overlay System**
77 | Implement Cullenect-style overlays where tool interface icons are overlaid on bolt shapes
78 |
79 | ## **QR Code Integration**
80 | Add QR codes to labels for linking to specifications, inventory systems, or part databases
81 |
82 | **Implementation Plan:**
83 | - Add "QR Code" section in label editor UI
84 | - Input field with 20-25 character limit for reliable scanning at 12mm height
85 | - Live character counter showing remaining characters
86 | - Helper text: "Use TinyURL.com to shorten long URLs"
87 | - Real-time QR code preview as user types
88 | - Use client-side QR library (qrcode.js or qrcode-generator)
89 | - Position QR on right side of label, adjust text layout accordingly
90 | - QR version 2-3 (25x25 to 29x29 modules) with error correction level L
91 | - Disable QR generation if over character limit
92 |
93 | ## **Implementation Notes**
94 | - The dual icon feature would work well with the existing fastener/head icon structure you already have
95 | - The overlay concept could leverage your existing SVG head icons to create composite visuals
--------------------------------------------------------------------------------
/lib/codemirror.css:
--------------------------------------------------------------------------------
1 | .CodeMirror{font-family:monospace;height:300px;color:#000;direction:ltr}.CodeMirror-lines{padding:4px 0}.CodeMirror pre.CodeMirror-line,.CodeMirror pre.CodeMirror-line-like{padding:0 4px}.CodeMirror-gutter-filler,.CodeMirror-scrollbar-filler{background-color:#fff}.CodeMirror-gutters{border-right:1px solid #ddd;background-color:#f7f7f7;white-space:nowrap}.CodeMirror-linenumber{padding:0 3px 0 5px;min-width:20px;text-align:right;color:#999;white-space:nowrap}.CodeMirror-guttermarker{color:#000}.CodeMirror-guttermarker-subtle{color:#999}.CodeMirror-cursor{border-left:1px solid #000;border-right:none;width:0}.CodeMirror div.CodeMirror-secondarycursor{border-left:1px solid silver}.cm-fat-cursor .CodeMirror-cursor{width:auto;border:0!important;background:#7e7}.cm-fat-cursor div.CodeMirror-cursors{z-index:1}.cm-fat-cursor .CodeMirror-line::selection,.cm-fat-cursor .CodeMirror-line>span::selection,.cm-fat-cursor .CodeMirror-line>span>span::selection{background:0 0}.cm-fat-cursor .CodeMirror-line::-moz-selection,.cm-fat-cursor .CodeMirror-line>span::-moz-selection,.cm-fat-cursor .CodeMirror-line>span>span::-moz-selection{background:0 0}.cm-fat-cursor{caret-color:transparent}@-moz-keyframes blink{50%{background-color:transparent}}@-webkit-keyframes blink{50%{background-color:transparent}}@keyframes blink{50%{background-color:transparent}}.cm-tab{display:inline-block;text-decoration:inherit}.CodeMirror-rulers{position:absolute;left:0;right:0;top:-50px;bottom:0;overflow:hidden}.CodeMirror-ruler{border-left:1px solid #ccc;top:0;bottom:0;position:absolute}.cm-s-default .cm-header{color:#00f}.cm-s-default .cm-quote{color:#090}.cm-negative{color:#d44}.cm-positive{color:#292}.cm-header,.cm-strong{font-weight:700}.cm-em{font-style:italic}.cm-link{text-decoration:underline}.cm-strikethrough{text-decoration:line-through}.cm-s-default .cm-keyword{color:#708}.cm-s-default .cm-atom{color:#219}.cm-s-default .cm-number{color:#164}.cm-s-default .cm-def{color:#00f}.cm-s-default .cm-variable-2{color:#05a}.cm-s-default .cm-type,.cm-s-default .cm-variable-3{color:#085}.cm-s-default .cm-comment{color:#a50}.cm-s-default .cm-string{color:#a11}.cm-s-default .cm-string-2{color:#f50}.cm-s-default .cm-meta{color:#555}.cm-s-default .cm-qualifier{color:#555}.cm-s-default .cm-builtin{color:#30a}.cm-s-default .cm-bracket{color:#997}.cm-s-default .cm-tag{color:#170}.cm-s-default .cm-attribute{color:#00c}.cm-s-default .cm-hr{color:#999}.cm-s-default .cm-link{color:#00c}.cm-s-default .cm-error{color:red}.cm-invalidchar{color:red}.CodeMirror-composing{border-bottom:2px solid}div.CodeMirror span.CodeMirror-matchingbracket{color:#0b0}div.CodeMirror span.CodeMirror-nonmatchingbracket{color:#a22}.CodeMirror-matchingtag{background:rgba(255,150,0,.3)}.CodeMirror-activeline-background{background:#e8f2ff}.CodeMirror{position:relative;overflow:hidden;background:#fff}.CodeMirror-scroll{overflow:scroll!important;margin-bottom:-50px;margin-right:-50px;padding-bottom:50px;height:100%;outline:0;position:relative;z-index:0}.CodeMirror-sizer{position:relative;border-right:50px solid transparent}.CodeMirror-gutter-filler,.CodeMirror-hscrollbar,.CodeMirror-scrollbar-filler,.CodeMirror-vscrollbar{position:absolute;z-index:6;display:none;outline:0}.CodeMirror-vscrollbar{right:0;top:0;overflow-x:hidden;overflow-y:scroll}.CodeMirror-hscrollbar{bottom:0;left:0;overflow-y:hidden;overflow-x:scroll}.CodeMirror-scrollbar-filler{right:0;bottom:0}.CodeMirror-gutter-filler{left:0;bottom:0}.CodeMirror-gutters{position:absolute;left:0;top:0;min-height:100%;z-index:3}.CodeMirror-gutter{white-space:normal;height:100%;display:inline-block;vertical-align:top;margin-bottom:-50px}.CodeMirror-gutter-wrapper{position:absolute;z-index:4;background:0 0!important;border:none!important}.CodeMirror-gutter-background{position:absolute;top:0;bottom:0;z-index:4}.CodeMirror-gutter-elt{position:absolute;cursor:default;z-index:4}.CodeMirror-gutter-wrapper ::selection{background-color:transparent}.CodeMirror-gutter-wrapper ::-moz-selection{background-color:transparent}.CodeMirror-lines{cursor:text;min-height:1px}.CodeMirror pre.CodeMirror-line,.CodeMirror pre.CodeMirror-line-like{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0;border-width:0;background:0 0;font-family:inherit;font-size:inherit;margin:0;white-space:pre;word-wrap:normal;line-height:inherit;color:inherit;z-index:2;position:relative;overflow:visible;-webkit-tap-highlight-color:transparent;-webkit-font-variant-ligatures:contextual;font-variant-ligatures:contextual}.CodeMirror-wrap pre.CodeMirror-line,.CodeMirror-wrap pre.CodeMirror-line-like{word-wrap:break-word;white-space:pre-wrap;word-break:normal}.CodeMirror-linebackground{position:absolute;left:0;right:0;top:0;bottom:0;z-index:0}.CodeMirror-linewidget{position:relative;z-index:2;padding:.1px}.CodeMirror-rtl pre{direction:rtl}.CodeMirror-code{outline:0}.CodeMirror-gutter,.CodeMirror-gutters,.CodeMirror-linenumber,.CodeMirror-scroll,.CodeMirror-sizer{-moz-box-sizing:content-box;box-sizing:content-box}.CodeMirror-measure{position:absolute;width:100%;height:0;overflow:hidden;visibility:hidden}.CodeMirror-cursor{position:absolute;pointer-events:none}.CodeMirror-measure pre{position:static}div.CodeMirror-cursors{visibility:hidden;position:relative;z-index:3}div.CodeMirror-dragcursors{visibility:visible}.CodeMirror-focused div.CodeMirror-cursors{visibility:visible}.CodeMirror-selected{background:#d9d9d9}.CodeMirror-focused .CodeMirror-selected{background:#d7d4f0}.CodeMirror-crosshair{cursor:crosshair}.CodeMirror-line::selection,.CodeMirror-line>span::selection,.CodeMirror-line>span>span::selection{background:#d7d4f0}.CodeMirror-line::-moz-selection,.CodeMirror-line>span::-moz-selection,.CodeMirror-line>span>span::-moz-selection{background:#d7d4f0}.cm-searching{background-color:#ffa;background-color:rgba(255,255,0,.4)}.cm-force-border{padding-right:.1px}@media print{.CodeMirror div.CodeMirror-cursors{visibility:hidden}}.cm-tab-wrap-hack:after{content:''}span.CodeMirror-selectedtext{background:0 0}
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | halagen
7 |
8 |
9 |
10 |
11 |
12 |