zebra-day 0.0.37__py3-none-any.whl → 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. zebra_day/__init__.py +35 -0
  2. zebra_day/bin/__init__.py +0 -0
  3. zebra_day/cli/__init__.py +240 -0
  4. zebra_day/cli/cognito.py +121 -0
  5. zebra_day/cli/gui.py +338 -0
  6. zebra_day/cli/printer.py +168 -0
  7. zebra_day/cli/template.py +176 -0
  8. zebra_day/cmd_mgr.py +35 -0
  9. zebra_day/etc/Monoid-Regular-HalfTight-Dollar-0-1-l.ttf +0 -0
  10. zebra_day/etc/label_styles/blank.zpl +0 -0
  11. zebra_day/etc/label_styles/cornersStripOf4Squares_1inX1in.zpl +55 -0
  12. zebra_day/etc/label_styles/corners_1inX2in.zpl +28 -0
  13. zebra_day/etc/label_styles/corners_20cmX30cm.zpl +6 -0
  14. zebra_day/etc/label_styles/corners_smallTube.zpl +7 -0
  15. zebra_day/etc/label_styles/corners_unspecifiedDimensions.zpl +15 -0
  16. zebra_day/etc/label_styles/generic_2inX1in.zpl +21 -0
  17. zebra_day/etc/label_styles/plate_1inX0.25in.zpl +9 -0
  18. zebra_day/etc/label_styles/plate_1inX0.25inHD.zpl +9 -0
  19. zebra_day/etc/label_styles/smallTubeWdotHD_prod.zpl +8 -0
  20. zebra_day/etc/label_styles/smallTubeWdot_corners.zpl +7 -0
  21. zebra_day/etc/label_styles/smallTubeWdot_prod.zpl +8 -0
  22. zebra_day/etc/label_styles/smallTubeWdot_prodAlt1.zpl +6 -0
  23. zebra_day/etc/label_styles/smallTubeWdot_prodAlt1b.zpl +3 -0
  24. zebra_day/etc/label_styles/smallTubeWdot_prodV2.zpl +8 -0
  25. zebra_day/etc/label_styles/smallTubeWdot_reagent.zpl +29 -0
  26. zebra_day/etc/label_styles/stripOf4Squares_1inX1in.zpl +32 -0
  27. zebra_day/etc/label_styles/test_800dX800dCoordinateArray.zpl +1 -0
  28. zebra_day/etc/label_styles/tmps/.hold +0 -0
  29. zebra_day/etc/label_styles/tmps/tmp_zpl_templates.here +0 -0
  30. zebra_day/etc/label_styles/tube_20mmX30mmA.zpl +7 -0
  31. zebra_day/etc/label_styles/tube_2inX0.3in.zpl +15 -0
  32. zebra_day/etc/label_styles/tube_2inX0.5in.zpl +15 -0
  33. zebra_day/etc/label_styles/tube_2inX0.5inHD.zpl +15 -0
  34. zebra_day/etc/label_styles/tube_2inX1in.zpl +25 -0
  35. zebra_day/etc/label_styles/tube_2inX1inHD.zpl +22 -0
  36. zebra_day/etc/label_styles/tube_2inX1inHDv3.zpl +21 -0
  37. zebra_day/etc/old_printer_config/.hold +0 -0
  38. zebra_day/etc/old_printer_config/2026-02-01_01:50:25.022846_printer_config.json +1 -0
  39. zebra_day/etc/old_printer_config/2026-02-01_01:50:25.033657_printer_config.json +1 -0
  40. zebra_day/etc/old_printer_config/2026-02-01_01:50:25.039597_printer_config.json +3 -0
  41. zebra_day/etc/old_printer_config/2026-02-01_01:50:25.047295_printer_config.json +1 -0
  42. zebra_day/etc/old_printer_config/2026-02-01_01:50:25.055804_printer_config.json +1 -0
  43. zebra_day/etc/old_printer_config/2026-02-01_01:50:25.061337_printer_config.json +3 -0
  44. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.073326_printer_config.json +1 -0
  45. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.081950_printer_config.json +1 -0
  46. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.088251_printer_config.json +3 -0
  47. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.096501_printer_config.json +1 -0
  48. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.104767_printer_config.json +1 -0
  49. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.110364_printer_config.json +3 -0
  50. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.118239_printer_config.json +1 -0
  51. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.125950_printer_config.json +1 -0
  52. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.349866_printer_config.json +1 -0
  53. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.361085_printer_config.json +3 -0
  54. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.558323_printer_config.json +1 -0
  55. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.565756_printer_config.json +3 -0
  56. zebra_day/etc/old_printer_config/2026-02-01_01:51:29.739070_printer_config.json +16 -0
  57. zebra_day/etc/old_printer_config/2026-02-01_01:51:29.753796_printer_config.json +1 -0
  58. zebra_day/etc/old_printer_config/2026-02-01_01:51:29.760201_printer_config.json +3 -0
  59. zebra_day/etc/old_printer_config/2026-02-01_01:51:29.768747_printer_config.json +1 -0
  60. zebra_day/etc/old_printer_config/2026-02-01_01:51:29.775312_printer_config.json +3 -0
  61. zebra_day/etc/old_printer_config/2026-02-01_01:51:29.782533_printer_config.json +1 -0
  62. zebra_day/etc/old_printer_config/2026-02-01_01:51:29.789287_printer_config.json +1 -0
  63. zebra_day/etc/old_printer_config/2026-02-01_01:51:29.794230_printer_config.json +3 -0
  64. zebra_day/etc/old_printer_config/2026-02-01_01:51:29.800021_printer_config.json +5 -0
  65. zebra_day/etc/printer_config.json +4 -0
  66. zebra_day/etc/printer_config.template.json +24 -0
  67. zebra_day/etc/tmp_printers0.json +5 -0
  68. zebra_day/etc/tmp_printers120.json +10 -0
  69. zebra_day/etc/tmp_printers145.json +10 -0
  70. zebra_day/etc/tmp_printers207.json +10 -0
  71. zebra_day/etc/tmp_printers374.json +5 -0
  72. zebra_day/etc/tmp_printers383.json +5 -0
  73. zebra_day/etc/tmp_printers450.json +5 -0
  74. zebra_day/etc/tmp_printers469.json +10 -0
  75. zebra_day/etc/tmp_printers485.json +10 -0
  76. zebra_day/etc/tmp_printers504.json +5 -0
  77. zebra_day/etc/tmp_printers531.json +10 -0
  78. zebra_day/etc/tmp_printers540.json +10 -0
  79. zebra_day/etc/tmp_printers542.json +10 -0
  80. zebra_day/etc/tmp_printers552.json +10 -0
  81. zebra_day/etc/tmp_printers608.json +5 -0
  82. zebra_day/etc/tmp_printers657.json +5 -0
  83. zebra_day/etc/tmp_printers715.json +10 -0
  84. zebra_day/etc/tmp_printers838.json +5 -0
  85. zebra_day/etc/tmp_printers839.json +5 -0
  86. zebra_day/etc/tmp_printers933.json +5 -0
  87. zebra_day/etc/tmp_printers957.json +5 -0
  88. zebra_day/etc/tmp_printers972.json +10 -0
  89. zebra_day/exceptions.py +88 -0
  90. zebra_day/files/.hold +0 -0
  91. zebra_day/files/blank_preview.png +0 -0
  92. zebra_day/files/corners_20cmX30cm_preview.png +0 -0
  93. zebra_day/files/generic_2inX1in_preview.png +0 -0
  94. zebra_day/files/hold +0 -0
  95. zebra_day/files/test_png_12020.png +0 -0
  96. zebra_day/files/test_png_12352.png +0 -0
  97. zebra_day/files/test_png_15472.png +0 -0
  98. zebra_day/files/test_png_17696.png +0 -0
  99. zebra_day/files/test_png_23477.png +0 -0
  100. zebra_day/files/test_png_24493.png +0 -0
  101. zebra_day/files/test_png_28157.png +0 -0
  102. zebra_day/files/test_png_30069.png +0 -0
  103. zebra_day/files/test_png_35832.png +0 -0
  104. zebra_day/files/test_png_36400.png +0 -0
  105. zebra_day/files/test_png_40816.png +0 -0
  106. zebra_day/files/test_png_47791.png +0 -0
  107. zebra_day/files/test_png_47799.png +0 -0
  108. zebra_day/files/test_png_49564.png +0 -0
  109. zebra_day/files/test_png_53848.png +0 -0
  110. zebra_day/files/test_png_55588.png +0 -0
  111. zebra_day/files/test_png_58809.png +0 -0
  112. zebra_day/files/test_png_62542.png +0 -0
  113. zebra_day/files/test_png_67242.png +0 -0
  114. zebra_day/files/test_png_89893.png +0 -0
  115. zebra_day/files/test_png_91597.png +0 -0
  116. zebra_day/files/test_png_93633.png +0 -0
  117. zebra_day/files/tmpbjo3k7q1.png +0 -0
  118. zebra_day/files/tmpigtr4pwy.png +0 -0
  119. zebra_day/files/tube_20mmX30mmA_preview.png +0 -0
  120. zebra_day/files/zpl_label_tube_2inX1in_2026-02-01_01:51:24.370964.png +0 -0
  121. zebra_day/logging_config.py +74 -0
  122. zebra_day/logs/.hold +0 -0
  123. zebra_day/logs/print_requests.log +2 -0
  124. zebra_day/paths.py +143 -0
  125. zebra_day/print_mgr.py +557 -117
  126. zebra_day/static/datschund.css +140 -0
  127. zebra_day/static/datschund.png +0 -0
  128. zebra_day/static/daylily.png +0 -0
  129. zebra_day/static/favicon.svg +20 -0
  130. zebra_day/static/general.css +99 -0
  131. zebra_day/static/js/zebra_modern.js +172 -0
  132. zebra_day/static/lsmc.css +354 -0
  133. zebra_day/static/moon.jpeg +0 -0
  134. zebra_day/static/oakland.css +197 -0
  135. zebra_day/static/petrichor.css +150 -0
  136. zebra_day/static/popday_daylily.css +140 -0
  137. zebra_day/static/style.css +183 -0
  138. zebra_day/static/triangles.css +122 -0
  139. zebra_day/static/tron.css +277 -0
  140. zebra_day/static/zebra_modern.css +771 -0
  141. zebra_day/static/zebras.css +176 -0
  142. zebra_day/templates/modern/base.html +98 -0
  143. zebra_day/templates/modern/config.html +141 -0
  144. zebra_day/templates/modern/config_backups.html +59 -0
  145. zebra_day/templates/modern/config_editor.html +95 -0
  146. zebra_day/templates/modern/config_new.html +93 -0
  147. zebra_day/templates/modern/dashboard.html +160 -0
  148. zebra_day/templates/modern/print_request.html +145 -0
  149. zebra_day/templates/modern/print_result.html +88 -0
  150. zebra_day/templates/modern/printer_detail.html +244 -0
  151. zebra_day/templates/modern/printers.html +144 -0
  152. zebra_day/templates/modern/save_result.html +46 -0
  153. zebra_day/templates/modern/template_editor.html +175 -0
  154. zebra_day/templates/modern/templates.html +122 -0
  155. zebra_day/web/__init__.py +9 -0
  156. zebra_day/web/app.py +248 -0
  157. zebra_day/web/auth.py +172 -0
  158. zebra_day/web/middleware.py +159 -0
  159. zebra_day/web/routers/__init__.py +2 -0
  160. zebra_day/web/routers/api.py +313 -0
  161. zebra_day/web/routers/ui.py +636 -0
  162. zebra_day/zpl_renderer.py +273 -0
  163. zebra_day-2.0.0.dist-info/METADATA +847 -0
  164. zebra_day-2.0.0.dist-info/RECORD +168 -0
  165. {zebra_day-0.0.37.dist-info → zebra_day-2.0.0.dist-info}/WHEEL +1 -1
  166. zebra_day-2.0.0.dist-info/entry_points.txt +4 -0
  167. zebra_day/bin/scan_for_networed_zebra_printers.py +0 -23
  168. zebra_day/bin/te.py +0 -905
  169. zebra_day/bin/zserve.py +0 -620
  170. zebra_day-0.0.37.dist-info/METADATA +0 -1177
  171. zebra_day-0.0.37.dist-info/RECORD +0 -10
  172. {zebra_day-0.0.37.dist-info → zebra_day-2.0.0.dist-info/licenses}/LICENSE +0 -0
  173. {zebra_day-0.0.37.dist-info → zebra_day-2.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,140 @@
1
+ @font-face {
2
+ font-family: 'Monoid';
3
+ src: url('/etc/Monoid-Regular-HalfTight-Dollar-0-1-l.ttf') format('truetype');
4
+ font-weight: normal;
5
+ font-style: normal;
6
+ }
7
+
8
+ body {
9
+ font-family: 'Monoid', monospace;
10
+ background-color: #FFD700; /* Bold golden background reminiscent of Warhol's color palette */
11
+ color: #D91E63; /* Bold Pink text, Warhol-esque */
12
+ margin: 0;
13
+ padding: 20px;
14
+ background-image: linear-gradient(rgba(21, 2, 2, 0.99), rgba(55, 55, 55, 0.8)), url('./datschund.png');
15
+ background-repeat: repeat;
16
+ }
17
+
18
+ h1, h2 {
19
+ color: rgb(222,221,222); /* Vibrant blue for headers */
20
+ border-bottom: 3px solid #2E86C1;
21
+ padding-bottom: 5px;
22
+ }
23
+
24
+ ul {
25
+ list-style-type: none;
26
+ padding: 0;
27
+ margin-left: 21px;
28
+ }
29
+
30
+ li::before {
31
+ content: '';
32
+ display: inline-block;
33
+ width: 5px;
34
+ height: 5px;
35
+ border-radius: 50%;
36
+ background-color: #D91E63; /* Reusing bold pink color */
37
+ margin-right: 10px;
38
+ vertical-align: middle;
39
+ }
40
+
41
+ a {
42
+ color: rgb(111,255,10); /* A deep green for links, contrast with the daylily theme */
43
+ text-decoration: none;
44
+ transition: color 0.3s ease;
45
+ }
46
+
47
+ a:hover {
48
+ color: #2E86C1; /* Vibrant blue on hover */
49
+ }
50
+
51
+ small {
52
+ color: rgba(221,1,211); /* Purple tint for smaller text, keeping with the vibrant palette */
53
+ }
54
+
55
+ .spinner-hidden {
56
+ display: none;
57
+ position: fixed;
58
+ top: 0; left: 0; right: 0; bottom: 0;
59
+ background: rgba(255,215,0,0.7); /* Transparent golden background, keeping in theme */
60
+ z-index: 1000;
61
+ align-items: center;
62
+ justify-content: center;
63
+ display: flex;
64
+ }
65
+
66
+ #spinner {
67
+ position: fixed;
68
+ top: 50%;
69
+ left: 50%;
70
+ transform: translate(-50%, -50%);
71
+ z-index: 9999;
72
+ }
73
+
74
+ .loader, .cloader {
75
+ border: 8px solid #7D3C98; /* Purple borders */
76
+ border-top: 8px solid #2E86C1; /* Blue on top to keep the spinning noticeable */
77
+ border-radius: 50%;
78
+ width: 50px;
79
+ height: 50px;
80
+ animation: spin 1s linear infinite;
81
+ }
82
+
83
+ @keyframes spin {
84
+ 0% { transform: rotate(0deg); }
85
+ 100% { transform: rotate(360deg); }
86
+ }
87
+
88
+ hr {
89
+ border: 0;
90
+ height: 2px;
91
+ background-image: linear-gradient(to right, #D91E63, #FFD700, #2E86C1);
92
+ margin: 20px 0;
93
+ }
94
+
95
+ input[type=text], textarea, select {
96
+ background-color: #FFFFFF; /* White background for input fields */
97
+ color: #D91E63; /* Pink text */
98
+ border: 2px solid #196F3D; /* Green border */
99
+ border-radius: 5px;
100
+ padding: 5px 10px;
101
+ outline: none;
102
+ transition: background-color 0.3s ease;
103
+ }
104
+
105
+ input[type=text]:hover, textarea:hover, select:hover {
106
+ background-color: #FFFAE5; /* A very light yellow for hover */
107
+ }
108
+
109
+ input[type=submit], button, input[type="button"] {
110
+ background-color: #D91E63; /* Pink buttons */
111
+ color: #FFFFFF; /* White text on buttons */
112
+ border: none;
113
+ border-radius: 5px;
114
+ padding: 8px 15px;
115
+ cursor: pointer;
116
+ outline: none;
117
+ transition: background-color 0.3s ease;
118
+ }
119
+
120
+ input[type=submit]:hover, button:hover, input[type="button"]:hover {
121
+ background-color: #E74C3C; /* Reddish hover effect */
122
+ }
123
+
124
+ textarea::-webkit-scrollbar {
125
+ width: 10px;
126
+ }
127
+
128
+ textarea::-webkit-scrollbar-thumb {
129
+ background-color: #2E86C1;
130
+ border-radius: 5px;
131
+ }
132
+
133
+ textarea::-webkit-scrollbar-track {
134
+ background-color: #FFD700; /* Golden background to keep in theme */
135
+ border-radius: 5px;
136
+ }
137
+
138
+ #spinner.spinner-hidden {
139
+ display: none;
140
+ }
Binary file
Binary file
@@ -0,0 +1,20 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
2
+ <!-- Background circle -->
3
+ <circle cx="16" cy="16" r="15" fill="#f5f5f5" stroke="#333" stroke-width="1"/>
4
+
5
+ <!-- Zebra stripes pattern -->
6
+ <g fill="#1a1a1a">
7
+ <!-- Diagonal stripes -->
8
+ <path d="M4 8 L8 4 L10 4 L4 10 Z"/>
9
+ <path d="M4 14 L14 4 L16 4 L4 16 Z"/>
10
+ <path d="M4 20 L20 4 L22 4 L4 22 Z"/>
11
+ <path d="M4 26 L26 4 L28 4 L6 28 L4 28 Z"/>
12
+ <path d="M10 28 L28 10 L28 12 L12 28 Z"/>
13
+ <path d="M16 28 L28 16 L28 18 L18 28 Z"/>
14
+ <path d="M22 28 L28 22 L28 24 L24 28 Z"/>
15
+ </g>
16
+
17
+ <!-- Z letter overlay -->
18
+ <text x="16" y="22" font-family="system-ui, -apple-system, sans-serif" font-size="16" font-weight="bold" fill="#6b46c1" text-anchor="middle">Z</text>
19
+ </svg>
20
+
@@ -0,0 +1,99 @@
1
+ @font-face {
2
+ font-family: 'Monoid';
3
+ src: url('/etc/Monoid-Regular-HalfTight-Dollar-0-1-l.ttf') format('truetype');
4
+ font-weight: normal;
5
+ font-style: normal;
6
+ }
7
+
8
+ body {
9
+ font-family: 'DejaVu Sans Mono', monospace;
10
+ }
11
+
12
+ :root {
13
+ --hrColor: #C400DB;
14
+ }
15
+
16
+ .hrTiny {
17
+ height: 1px;
18
+ background-color: var(--hrColor);
19
+ width:100%;
20
+ }
21
+
22
+ .hrSmall {
23
+ height: 2px;
24
+ background-color: var(--hrColor);
25
+ width:100%;
26
+ }
27
+
28
+ .hrMed {
29
+ height: 3px;
30
+ width: 100%;
31
+ background-color: var(--hrColor);
32
+ }
33
+
34
+
35
+ .hrLarge {
36
+ height: 4px;
37
+ background-color: var(--hrColor);
38
+ width:100%;
39
+ }
40
+
41
+ .hrVLarge {
42
+ height: 5px;
43
+ background-color: var(--hrColor);
44
+ width:100%;
45
+ }
46
+
47
+ h3 {
48
+ margin-bottom: auto;
49
+ }
50
+
51
+
52
+ .spinner-hidden {
53
+ display: none;
54
+ position: fixed;
55
+ top: 0; left: 0; right: 0; bottom: 0;
56
+ background: rgba(0,0,0,0.6);
57
+ z-index: 1000;
58
+ align-items: center;
59
+ justify-content: center;
60
+ display: flex;
61
+ }
62
+
63
+
64
+ #spinner.spinner-hidden {
65
+ display: none;
66
+ }
67
+
68
+
69
+ #spinner {
70
+ position: fixed;
71
+ top: 50%;
72
+ left: 50%;
73
+ transform: translate(-50%, -50%);
74
+ z-index: 9999;
75
+ }
76
+
77
+
78
+ .loader, .cloader {
79
+ border: 8px solid #7D3C98; /* Purple borders */
80
+ border-top: 8px solid #2E86C1; /* Blue on top to keep the spinning noticeable */
81
+ border-radius: 50%;
82
+ width: 50px;
83
+ height: 50px;
84
+ animation: spin 1s linear infinite;
85
+ }
86
+
87
+ @keyframes spin {
88
+ 0% { transform: rotate(0deg); }
89
+ 100% { transform: rotate(360deg); }
90
+ }
91
+
92
+ @keyframes spin {
93
+ 0% { transform: rotate(0deg); }
94
+ 100% { transform: rotate(360deg); }
95
+ }
96
+
97
+ #spinner.spinner-hidden {
98
+ display: none;
99
+ }
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Zebra Day Modern UI - JavaScript Utilities
3
+ */
4
+
5
+ // Global state
6
+ const ZebraDay = {
7
+ config: window.ZebraConfig || {},
8
+ toasts: [],
9
+ };
10
+
11
+ // DOM Ready
12
+ document.addEventListener('DOMContentLoaded', function() {
13
+ initMobileMenu();
14
+ initTooltips();
15
+ });
16
+
17
+ // Mobile Menu Toggle
18
+ function initMobileMenu() {
19
+ const menuToggle = document.getElementById('menu-toggle');
20
+ const navLinks = document.getElementById('nav-links');
21
+
22
+ if (menuToggle && navLinks) {
23
+ menuToggle.addEventListener('click', () => {
24
+ navLinks.classList.toggle('active');
25
+ });
26
+ }
27
+ }
28
+
29
+ // Tooltips
30
+ function initTooltips() {
31
+ document.querySelectorAll('[title]').forEach(el => {
32
+ el.addEventListener('mouseenter', showTooltip);
33
+ el.addEventListener('mouseleave', hideTooltip);
34
+ });
35
+ }
36
+
37
+ function showTooltip(e) {
38
+ const title = e.target.getAttribute('title');
39
+ if (!title) return;
40
+
41
+ e.target.setAttribute('data-title', title);
42
+ e.target.removeAttribute('title');
43
+
44
+ const tooltip = document.createElement('div');
45
+ tooltip.className = 'tooltip';
46
+ tooltip.textContent = title;
47
+ tooltip.style.cssText = `
48
+ position: fixed;
49
+ background: var(--color-gray-700, #1f1f1f);
50
+ color: white;
51
+ padding: 6px 10px;
52
+ border-radius: 6px;
53
+ font-size: 12px;
54
+ max-width: 280px;
55
+ z-index: 9999;
56
+ pointer-events: none;
57
+ `;
58
+
59
+ document.body.appendChild(tooltip);
60
+
61
+ const rect = e.target.getBoundingClientRect();
62
+ const margin = 8;
63
+ let left = rect.left + (rect.width / 2) - (tooltip.offsetWidth / 2);
64
+ let top = rect.top - tooltip.offsetHeight - margin;
65
+
66
+ left = Math.max(margin, Math.min(left, window.innerWidth - tooltip.offsetWidth - margin));
67
+ if (top < margin) top = rect.bottom + margin;
68
+
69
+ tooltip.style.left = left + 'px';
70
+ tooltip.style.top = top + 'px';
71
+
72
+ e.target._tooltip = tooltip;
73
+ }
74
+
75
+ function hideTooltip(e) {
76
+ const title = e.target.getAttribute('data-title');
77
+ if (title) {
78
+ e.target.setAttribute('title', title);
79
+ e.target.removeAttribute('data-title');
80
+ }
81
+ if (e.target._tooltip) {
82
+ e.target._tooltip.remove();
83
+ delete e.target._tooltip;
84
+ }
85
+ }
86
+
87
+ // Toast Notifications
88
+ function showToast(type, title, message, duration = 5000) {
89
+ const container = document.getElementById('toast-container');
90
+ if (!container) return;
91
+
92
+ const icons = {
93
+ success: 'fa-check-circle',
94
+ error: 'fa-exclamation-circle',
95
+ warning: 'fa-exclamation-triangle',
96
+ info: 'fa-info-circle'
97
+ };
98
+
99
+ const toast = document.createElement('div');
100
+ toast.className = `toast toast-${type}`;
101
+ toast.innerHTML = `
102
+ <div class="toast-icon">
103
+ <i class="fas ${icons[type] || icons.info}"></i>
104
+ </div>
105
+ <div class="toast-content">
106
+ <div class="toast-title">${title}</div>
107
+ <div class="toast-message">${message}</div>
108
+ </div>
109
+ <button class="toast-close" onclick="this.parentElement.remove()">&times;</button>
110
+ `;
111
+
112
+ container.appendChild(toast);
113
+
114
+ if (duration > 0) {
115
+ setTimeout(() => toast.remove(), duration);
116
+ }
117
+
118
+ return toast;
119
+ }
120
+
121
+ // Loading Overlay
122
+ function showLoading(message = 'Loading...') {
123
+ const overlay = document.getElementById('loading-overlay');
124
+ if (overlay) {
125
+ const p = overlay.querySelector('p');
126
+ if (p) p.textContent = message;
127
+ overlay.classList.remove('d-none');
128
+ }
129
+ }
130
+
131
+ function hideLoading() {
132
+ const overlay = document.getElementById('loading-overlay');
133
+ if (overlay) {
134
+ overlay.classList.add('d-none');
135
+ }
136
+ }
137
+
138
+ // Copy to Clipboard
139
+ async function copyToClipboard(text) {
140
+ try {
141
+ await navigator.clipboard.writeText(text);
142
+ showToast('success', 'Copied!', 'Text copied to clipboard');
143
+ } catch (err) {
144
+ showToast('error', 'Copy Failed', 'Could not copy to clipboard');
145
+ }
146
+ }
147
+
148
+ // Format Date
149
+ function formatDate(dateString) {
150
+ const date = new Date(dateString);
151
+ return date.toLocaleDateString('en-US', {
152
+ year: 'numeric',
153
+ month: 'short',
154
+ day: 'numeric',
155
+ hour: '2-digit',
156
+ minute: '2-digit'
157
+ });
158
+ }
159
+
160
+ // Debounce
161
+ function debounce(func, wait) {
162
+ let timeout;
163
+ return function executedFunction(...args) {
164
+ const later = () => {
165
+ clearTimeout(timeout);
166
+ func(...args);
167
+ };
168
+ clearTimeout(timeout);
169
+ timeout = setTimeout(later, wait);
170
+ };
171
+ }
172
+