numclassify 0.3.1__tar.gz → 0.3.2__tar.gz
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.
- {numclassify-0.3.1 → numclassify-0.3.2}/CHANGELOG.md +11 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/PKG-INFO +1 -1
- {numclassify-0.3.1 → numclassify-0.3.2}/docs/playground.css +155 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/docs/playground.html +16 -2
- {numclassify-0.3.1 → numclassify-0.3.2}/docs/playground.js +142 -18
- {numclassify-0.3.1 → numclassify-0.3.2}/pyproject.toml +1 -1
- {numclassify-0.3.1 → numclassify-0.3.2}/.github/workflows/ci.yml +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/.github/workflows/docs.yml +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/.github/workflows/publish.yml +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/.gitignore +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/CONTRIBUTING.md +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/LICENSE +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/README.md +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/docs/api.md +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/docs/changelog.md +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/docs/cli.md +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/docs/index.md +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/examples/basic_usage.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/examples/custom_type.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/examples/find_perfect_numbers.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/examples/random_classify.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/examples/stream_large_range.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/mkdocs.yml +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/numclassify/__init__.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/numclassify/__main__.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/numclassify/_core/__init__.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/numclassify/_core/combinatorial.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/numclassify/_core/digital.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/numclassify/_core/divisors.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/numclassify/_core/figurate.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/numclassify/_core/number_theory.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/numclassify/_core/powers.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/numclassify/_core/primes.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/numclassify/_core/recreational.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/numclassify/_core/sequences.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/numclassify/_registry.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/numclassify/cli.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/numclassify/py.typed +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2}/tests/test_registry.py +0 -0
|
@@ -6,6 +6,17 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## [0.3.2] - 2026-06-12
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- Search autocomplete dropdown — suggests property names as you type in the search field
|
|
13
|
+
- Confetti celebration burst when a number scores >50 properties
|
|
14
|
+
- Keyboard shortcuts: `C` (classify), `S` (search), `N` (Number of the Day), `?`/`H` (show shortcuts overlay)
|
|
15
|
+
- `prefers-reduced-motion` support — disables all animations for accessibility
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- No bugs reported since v0.3.1
|
|
19
|
+
|
|
9
20
|
## [0.3.1] - 2026-06-12
|
|
10
21
|
|
|
11
22
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: numclassify
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: The most comprehensive Python library for number classification - 3000+ number types
|
|
5
5
|
Project-URL: Homepage, https://github.com/aratrikghosh2011-tech/numclassify
|
|
6
6
|
Project-URL: Repository, https://github.com/aratrikghosh2011-tech/numclassify
|
|
@@ -999,3 +999,158 @@ footer a:hover {
|
|
|
999
999
|
.tabs { gap: 2px; }
|
|
1000
1000
|
.tab { font-size: 12px; padding: 8px 6px; }
|
|
1001
1001
|
}
|
|
1002
|
+
|
|
1003
|
+
/* ── Search Autocomplete ────────────────────────────────────────────── */
|
|
1004
|
+
|
|
1005
|
+
.autocomplete-wrap {
|
|
1006
|
+
position: relative;
|
|
1007
|
+
flex: 1;
|
|
1008
|
+
min-width: 180px;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
#ac-dropdown {
|
|
1012
|
+
position: absolute;
|
|
1013
|
+
top: calc(100% + 4px);
|
|
1014
|
+
left: 0;
|
|
1015
|
+
right: 0;
|
|
1016
|
+
background: var(--bg-elevated);
|
|
1017
|
+
border: 1px solid var(--border);
|
|
1018
|
+
border-radius: 8px;
|
|
1019
|
+
max-height: 220px;
|
|
1020
|
+
overflow-y: auto;
|
|
1021
|
+
z-index: 50;
|
|
1022
|
+
display: none;
|
|
1023
|
+
box-shadow: 0 8px 24px rgba(0,0,0,0.3);
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
#ac-dropdown.open { display: block; }
|
|
1027
|
+
|
|
1028
|
+
.ac-item {
|
|
1029
|
+
padding: 8px 14px;
|
|
1030
|
+
font-family: 'JetBrains Mono', monospace;
|
|
1031
|
+
font-size: 12px;
|
|
1032
|
+
cursor: pointer;
|
|
1033
|
+
transition: background 0.15s ease;
|
|
1034
|
+
color: var(--text);
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
.ac-item:first-child { border-radius: 7px 7px 0 0; }
|
|
1038
|
+
.ac-item:last-child { border-radius: 0 0 7px 7px; }
|
|
1039
|
+
|
|
1040
|
+
.ac-item:hover, .ac-item.selected {
|
|
1041
|
+
background: var(--saffron-faint);
|
|
1042
|
+
color: var(--saffron);
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
.ac-item .ac-cat {
|
|
1046
|
+
float: right;
|
|
1047
|
+
font-size: 10px;
|
|
1048
|
+
color: var(--text-muted);
|
|
1049
|
+
font-family: 'Inter', sans-serif;
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
/* ── Confetti ────────────────────────────────────────────────────────── */
|
|
1053
|
+
|
|
1054
|
+
.confetti-container {
|
|
1055
|
+
position: fixed;
|
|
1056
|
+
inset: 0;
|
|
1057
|
+
pointer-events: none;
|
|
1058
|
+
z-index: 9999;
|
|
1059
|
+
overflow: hidden;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
.confetti-piece {
|
|
1063
|
+
position: absolute;
|
|
1064
|
+
width: 8px;
|
|
1065
|
+
height: 8px;
|
|
1066
|
+
top: -10px;
|
|
1067
|
+
animation: confettiFall linear forwards;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
@keyframes confettiFall {
|
|
1071
|
+
0% {
|
|
1072
|
+
transform: translateY(0) rotate(0deg) scale(1);
|
|
1073
|
+
opacity: 1;
|
|
1074
|
+
}
|
|
1075
|
+
100% {
|
|
1076
|
+
transform: translateY(100vh) rotate(720deg) scale(0.3);
|
|
1077
|
+
opacity: 0;
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
/* ── Keyboard Shortcuts Overlay ──────────────────────────────────────── */
|
|
1082
|
+
|
|
1083
|
+
.shortcuts-overlay {
|
|
1084
|
+
position: fixed;
|
|
1085
|
+
inset: 0;
|
|
1086
|
+
background: rgba(0,0,0,0.6);
|
|
1087
|
+
backdrop-filter: blur(4px);
|
|
1088
|
+
z-index: 9998;
|
|
1089
|
+
display: none;
|
|
1090
|
+
align-items: center;
|
|
1091
|
+
justify-content: center;
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
.shortcuts-overlay.open { display: flex; }
|
|
1095
|
+
|
|
1096
|
+
.shortcuts-panel {
|
|
1097
|
+
background: var(--bg-card);
|
|
1098
|
+
border: 1px solid var(--border);
|
|
1099
|
+
border-radius: 16px;
|
|
1100
|
+
padding: 32px 40px;
|
|
1101
|
+
max-width: 400px;
|
|
1102
|
+
width: 90%;
|
|
1103
|
+
animation: slideUp 0.3s ease both;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
.shortcuts-panel h2 {
|
|
1107
|
+
font-size: 16px;
|
|
1108
|
+
font-weight: 700;
|
|
1109
|
+
color: var(--saffron);
|
|
1110
|
+
margin-bottom: 20px;
|
|
1111
|
+
text-transform: uppercase;
|
|
1112
|
+
letter-spacing: 0.08em;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
.shortcut-row {
|
|
1116
|
+
display: flex;
|
|
1117
|
+
align-items: center;
|
|
1118
|
+
justify-content: space-between;
|
|
1119
|
+
padding: 8px 0;
|
|
1120
|
+
border-bottom: 1px solid rgba(255,153,51,0.06);
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
.shortcut-row:last-child { border-bottom: none; }
|
|
1124
|
+
|
|
1125
|
+
.shortcut-row .sk-key {
|
|
1126
|
+
background: var(--bg-input);
|
|
1127
|
+
border: 1px solid var(--border);
|
|
1128
|
+
border-radius: 4px;
|
|
1129
|
+
padding: 2px 8px;
|
|
1130
|
+
font-family: 'JetBrains Mono', monospace;
|
|
1131
|
+
font-size: 12px;
|
|
1132
|
+
color: var(--saffron);
|
|
1133
|
+
min-width: 28px;
|
|
1134
|
+
text-align: center;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
.shortcut-row .sk-desc {
|
|
1138
|
+
font-size: 13px;
|
|
1139
|
+
color: var(--text-muted);
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
.shortcuts-close {
|
|
1143
|
+
margin-top: 20px;
|
|
1144
|
+
width: 100%;
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
/* ── Reduced Motion ──────────────────────────────────────────────────── */
|
|
1148
|
+
|
|
1149
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1150
|
+
*, *::before, *::after {
|
|
1151
|
+
animation-duration: 0.01ms !important;
|
|
1152
|
+
animation-iteration-count: 1 !important;
|
|
1153
|
+
transition-duration: 0.01ms !important;
|
|
1154
|
+
scroll-behavior: auto !important;
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
</div>
|
|
61
61
|
</div>
|
|
62
62
|
<div class="tags-wrap" id="classify-tags"></div>
|
|
63
|
-
<div class="btn-row">
|
|
63
|
+
<div class="btn-row" id="batch-actions">
|
|
64
64
|
<button class="btn btn-icon" onclick="copyResults()">Copy</button>
|
|
65
65
|
<button class="btn btn-icon" onclick="shareURL()">Share</button>
|
|
66
66
|
<button class="btn btn-icon" onclick="downloadJSON()">Download</button>
|
|
@@ -88,7 +88,10 @@
|
|
|
88
88
|
<div class="card">
|
|
89
89
|
<div class="card-title">Search by Property</div>
|
|
90
90
|
<div class="input-row">
|
|
91
|
-
<
|
|
91
|
+
<div class="autocomplete-wrap">
|
|
92
|
+
<input type="text" id="input-property" placeholder="Property name (e.g. prime, perfect, triangular)..." autocomplete="off" />
|
|
93
|
+
<div id="ac-dropdown"></div>
|
|
94
|
+
</div>
|
|
92
95
|
</div>
|
|
93
96
|
<div class="input-row" style="margin-top:10px;">
|
|
94
97
|
<input type="number" id="input-range-start" placeholder="From" value="1" />
|
|
@@ -166,6 +169,17 @@
|
|
|
166
169
|
<button id="scroll-top" onclick="scrollToTop()" title="Scroll to top">↑</button>
|
|
167
170
|
<div id="toast"></div>
|
|
168
171
|
|
|
172
|
+
<div class="shortcuts-overlay" id="shortcuts-overlay" onclick="closeShortcuts()">
|
|
173
|
+
<div class="shortcuts-panel" onclick="event.stopPropagation()">
|
|
174
|
+
<h2>Keyboard Shortcuts</h2>
|
|
175
|
+
<div class="shortcut-row"><span class="sk-key">C</span><span class="sk-desc">Classify tab</span></div>
|
|
176
|
+
<div class="shortcut-row"><span class="sk-key">S</span><span class="sk-desc">Search tab</span></div>
|
|
177
|
+
<div class="shortcut-row"><span class="sk-key">N</span><span class="sk-desc">Number of the Day</span></div>
|
|
178
|
+
<div class="shortcut-row"><span class="sk-key">?</span><span class="sk-desc">Toggle this panel</span></div>
|
|
179
|
+
<button class="btn shortcuts-close" onclick="closeShortcuts()">Close</button>
|
|
180
|
+
</div>
|
|
181
|
+
</div>
|
|
182
|
+
|
|
169
183
|
<script src="playground.js"></script>
|
|
170
184
|
</body>
|
|
171
185
|
</html>
|
|
@@ -5,6 +5,7 @@ let allProperties = [];
|
|
|
5
5
|
let searchPage = 0;
|
|
6
6
|
const SEARCH_PAGE_SIZE = 50;
|
|
7
7
|
let searchResultsAll = [];
|
|
8
|
+
let batchMode = false;
|
|
8
9
|
|
|
9
10
|
// ── Utility ──────────────────────────────────────────────────────────────
|
|
10
11
|
|
|
@@ -166,20 +167,22 @@ function makeTags(props, container, delayBase = 0) {
|
|
|
166
167
|
|
|
167
168
|
// ── Score Counter Animation ──────────────────────────────────────────────
|
|
168
169
|
|
|
169
|
-
function animateScore(
|
|
170
|
+
function animateScore(target, duration = 400) {
|
|
171
|
+
const counter = document.querySelector('#classify-score .score-count');
|
|
172
|
+
if (!counter) return;
|
|
170
173
|
const start = performance.now();
|
|
171
|
-
const startVal = 0;
|
|
172
174
|
function step(now) {
|
|
173
175
|
const t = Math.min((now - start) / duration, 1);
|
|
174
176
|
const ease = 1 - Math.pow(1 - t, 3);
|
|
175
|
-
const current = Math.round(
|
|
176
|
-
|
|
177
|
+
const current = Math.round(target * ease);
|
|
178
|
+
counter.textContent = current;
|
|
177
179
|
if (t < 1) requestAnimationFrame(step);
|
|
178
180
|
else {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
181
|
+
counter.textContent = target;
|
|
182
|
+
const badge = $('classify-score');
|
|
183
|
+
badge.classList.remove('pulse');
|
|
184
|
+
void badge.offsetWidth;
|
|
185
|
+
badge.classList.add('pulse');
|
|
183
186
|
}
|
|
184
187
|
}
|
|
185
188
|
requestAnimationFrame(step);
|
|
@@ -230,7 +233,7 @@ json.dumps(m)
|
|
|
230
233
|
const ver = await pyodide.runPythonAsync('nc.__version__');
|
|
231
234
|
$('version-text').textContent = ver;
|
|
232
235
|
} catch(e) {
|
|
233
|
-
$('version-text').textContent = '
|
|
236
|
+
$('version-text').textContent = '?';
|
|
234
237
|
}
|
|
235
238
|
|
|
236
239
|
setProgress(100, 'Ready!');
|
|
@@ -256,6 +259,13 @@ json.dumps(m)
|
|
|
256
259
|
|
|
257
260
|
async function doClassify(n = null) {
|
|
258
261
|
if (!requireReady()) return;
|
|
262
|
+
// If no explicit number and input contains commas/spaces, go to batch mode
|
|
263
|
+
if (n === null) {
|
|
264
|
+
const raw = $('input-classify').value.trim();
|
|
265
|
+
if (raw.includes(',') || /\s/.test(raw)) {
|
|
266
|
+
return doBatchClassify();
|
|
267
|
+
}
|
|
268
|
+
}
|
|
259
269
|
const val = n !== null ? n : parseInt($('input-classify').value);
|
|
260
270
|
if (isNaN(val)) { toast('Enter a valid integer.'); return; }
|
|
261
271
|
|
|
@@ -271,9 +281,12 @@ json.dumps({"number": r["number"], "score": r["score"], "props": r["true_propert
|
|
|
271
281
|
`);
|
|
272
282
|
const data = JSON.parse(result);
|
|
273
283
|
|
|
284
|
+
batchMode = false;
|
|
274
285
|
renderNumber($('classify-number'), data.number);
|
|
275
|
-
animateScore(
|
|
286
|
+
animateScore(data.score);
|
|
276
287
|
makeTags(data.props, $('classify-tags'));
|
|
288
|
+
$('batch-actions').style.display = '';
|
|
289
|
+
if (data.score > 50) burstConfetti();
|
|
277
290
|
|
|
278
291
|
const res = $('result-classify');
|
|
279
292
|
res.style.display = 'block';
|
|
@@ -327,6 +340,7 @@ json.dumps(results)
|
|
|
327
340
|
`);
|
|
328
341
|
const results = JSON.parse(result);
|
|
329
342
|
|
|
343
|
+
batchMode = true;
|
|
330
344
|
const res = $('result-classify');
|
|
331
345
|
res.style.display = 'block';
|
|
332
346
|
res.classList.remove('show');
|
|
@@ -334,7 +348,8 @@ json.dumps(results)
|
|
|
334
348
|
res.classList.add('show');
|
|
335
349
|
|
|
336
350
|
$('classify-number').innerHTML = `<span style="font-size:24px;background:none;-webkit-text-fill-color:var(--saffron);color:var(--saffron)">Batch (${results.length})</span>`;
|
|
337
|
-
$('classify-score').
|
|
351
|
+
$('classify-score').innerHTML = '<span class="score-count"></span>';
|
|
352
|
+
$('batch-actions').style.display = 'none';
|
|
338
353
|
|
|
339
354
|
let html = '<table class="batch-table"><thead><tr><th>Number</th><th>Score</th><th>Properties</th></tr></thead><tbody>';
|
|
340
355
|
results.forEach(r => {
|
|
@@ -538,6 +553,7 @@ json.dumps({"only_a": only_a, "only_b": only_b, "shared": shared})
|
|
|
538
553
|
// ── Number of the Day ────────────────────────────────────────────────────
|
|
539
554
|
|
|
540
555
|
async function computeNOTD(dateStr) {
|
|
556
|
+
if (!pyReady) return;
|
|
541
557
|
try {
|
|
542
558
|
if ($('notd-content')) $('notd-content').style.display = 'none';
|
|
543
559
|
if ($('notd-loading')) $('notd-loading').style.display = 'block';
|
|
@@ -580,6 +596,7 @@ function onNotdDateChange(input) {
|
|
|
580
596
|
// ── Copy & Share & Download ──────────────────────────────────────────────
|
|
581
597
|
|
|
582
598
|
async function copyResults() {
|
|
599
|
+
if (batchMode) { toast('Cannot copy batch results — classify a single number first.'); return; }
|
|
583
600
|
const num = $('classify-number').textContent;
|
|
584
601
|
const score = $('classify-score').textContent;
|
|
585
602
|
const tags = [...$('classify-tags').querySelectorAll('.tag')].map(t => t.textContent).join(', ');
|
|
@@ -606,10 +623,12 @@ function shareURL() {
|
|
|
606
623
|
}
|
|
607
624
|
|
|
608
625
|
function downloadJSON() {
|
|
626
|
+
if (batchMode) { toast('Cannot download batch results — classify a single number first.'); return; }
|
|
609
627
|
const num = $('classify-number').textContent;
|
|
610
|
-
const
|
|
628
|
+
const scoreEl = document.querySelector('#classify-score .score-count');
|
|
629
|
+
const score = scoreEl ? parseInt(scoreEl.textContent) || 0 : 0;
|
|
611
630
|
const tags = [...$('classify-tags').querySelectorAll('.tag')].map(t => t.textContent);
|
|
612
|
-
const data = { number: parseInt(num) || num, score
|
|
631
|
+
const data = { number: parseInt(num) || num, score, properties: tags };
|
|
613
632
|
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
|
|
614
633
|
const url = URL.createObjectURL(blob);
|
|
615
634
|
const a = document.createElement('a');
|
|
@@ -643,16 +662,119 @@ function scrollToTop() {
|
|
|
643
662
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
644
663
|
}
|
|
645
664
|
|
|
665
|
+
// ── Search Autocomplete ───────────────────────────────────────────────────
|
|
666
|
+
|
|
667
|
+
function setupAutocomplete() {
|
|
668
|
+
const input = $('input-property');
|
|
669
|
+
if (!input) return;
|
|
670
|
+
const dropdown = $('ac-dropdown');
|
|
671
|
+
let selectedIdx = -1;
|
|
672
|
+
|
|
673
|
+
input.addEventListener('input', () => {
|
|
674
|
+
const val = input.value.trim().toLowerCase();
|
|
675
|
+
if (!val) { dropdown.classList.remove('open'); return; }
|
|
676
|
+
const matches = allProperties
|
|
677
|
+
.filter(p => p.includes(val))
|
|
678
|
+
.slice(0, 10);
|
|
679
|
+
if (!matches.length) { dropdown.classList.remove('open'); return; }
|
|
680
|
+
selectedIdx = -1;
|
|
681
|
+
dropdown.innerHTML = matches.map((p, i) =>
|
|
682
|
+
`<div class="ac-item${i === 0 ? ' selected' : ''}" data-prop="${p}" onclick="pickAutocomplete('${p}')">
|
|
683
|
+
${p.replace(/_/g, ' ')}
|
|
684
|
+
<span class="ac-cat">${(categoryMap[p] || '').replace(/_/g, ' ')}</span>
|
|
685
|
+
</div>`
|
|
686
|
+
).join('');
|
|
687
|
+
dropdown.classList.add('open');
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
input.addEventListener('keydown', e => {
|
|
691
|
+
const items = dropdown.querySelectorAll('.ac-item');
|
|
692
|
+
if (!items.length) return;
|
|
693
|
+
if (e.key === 'ArrowDown') {
|
|
694
|
+
e.preventDefault();
|
|
695
|
+
selectedIdx = Math.min(selectedIdx + 1, items.length - 1);
|
|
696
|
+
} else if (e.key === 'ArrowUp') {
|
|
697
|
+
e.preventDefault();
|
|
698
|
+
selectedIdx = Math.max(selectedIdx - 1, -1);
|
|
699
|
+
} else if (e.key === 'Enter' && selectedIdx >= 0) {
|
|
700
|
+
e.preventDefault();
|
|
701
|
+
items[selectedIdx].click();
|
|
702
|
+
return;
|
|
703
|
+
} else return;
|
|
704
|
+
items.forEach((el, i) => el.classList.toggle('selected', i === selectedIdx));
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
document.addEventListener('click', e => {
|
|
708
|
+
if (!input.contains(e.target) && !dropdown.contains(e.target)) {
|
|
709
|
+
dropdown.classList.remove('open');
|
|
710
|
+
}
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
function pickAutocomplete(prop) {
|
|
715
|
+
$('input-property').value = prop;
|
|
716
|
+
$('ac-dropdown').classList.remove('open');
|
|
717
|
+
doSearch();
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// ── Confetti ──────────────────────────────────────────────────────────────
|
|
721
|
+
|
|
722
|
+
function burstConfetti() {
|
|
723
|
+
const container = document.createElement('div');
|
|
724
|
+
container.className = 'confetti-container';
|
|
725
|
+
const colors = ['#FF9933', '#FF6B6B', '#4ECDC4', '#45B7D1', '#F7DC6F', '#DDA0DD', '#98D8C8', '#BB8FCE', '#FFEAA7', '#4CAF7D'];
|
|
726
|
+
for (let i = 0; i < 80; i++) {
|
|
727
|
+
const piece = document.createElement('div');
|
|
728
|
+
piece.className = 'confetti-piece';
|
|
729
|
+
const size = 4 + Math.random() * 8;
|
|
730
|
+
piece.style.width = size + 'px';
|
|
731
|
+
piece.style.height = size + 'px';
|
|
732
|
+
piece.style.background = colors[Math.floor(Math.random() * colors.length)];
|
|
733
|
+
piece.style.left = Math.random() * 100 + '%';
|
|
734
|
+
piece.style.borderRadius = Math.random() > 0.5 ? '50%' : '2px';
|
|
735
|
+
piece.style.animationDuration = (1.5 + Math.random() * 2) + 's';
|
|
736
|
+
piece.style.animationDelay = Math.random() * 0.5 + 's';
|
|
737
|
+
container.appendChild(piece);
|
|
738
|
+
}
|
|
739
|
+
document.body.appendChild(container);
|
|
740
|
+
setTimeout(() => container.remove(), 3500);
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// ── Keyboard Shortcuts ────────────────────────────────────────────────────
|
|
744
|
+
|
|
745
|
+
const SHORTCUT_MAP = {
|
|
746
|
+
c: 'classify',
|
|
747
|
+
s: 'search',
|
|
748
|
+
n: 'notd',
|
|
749
|
+
};
|
|
750
|
+
|
|
751
|
+
function setupShortcuts() {
|
|
752
|
+
document.addEventListener('keydown', e => {
|
|
753
|
+
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
|
|
754
|
+
const key = e.key.toLowerCase();
|
|
755
|
+
if (key === '?' || key === 'h') {
|
|
756
|
+
e.preventDefault();
|
|
757
|
+
$('shortcuts-overlay').classList.toggle('open');
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
const tab = SHORTCUT_MAP[key];
|
|
761
|
+
if (tab) {
|
|
762
|
+
e.preventDefault();
|
|
763
|
+
switchTab(tab);
|
|
764
|
+
}
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
function closeShortcuts() {
|
|
769
|
+
$('shortcuts-overlay').classList.remove('open');
|
|
770
|
+
}
|
|
771
|
+
|
|
646
772
|
// ── Init ─────────────────────────────────────────────────────────────────
|
|
647
773
|
|
|
648
774
|
document.addEventListener('DOMContentLoaded', () => {
|
|
649
775
|
// Enter key support
|
|
650
776
|
$('input-classify')?.addEventListener('keydown', e => {
|
|
651
|
-
if (e.key === 'Enter')
|
|
652
|
-
const raw = e.target.value.trim();
|
|
653
|
-
if (raw.includes(',') || raw.includes(' ')) doBatchClassify();
|
|
654
|
-
else doClassify();
|
|
655
|
-
}
|
|
777
|
+
if (e.key === 'Enter') doClassify();
|
|
656
778
|
});
|
|
657
779
|
$('input-property')?.addEventListener('keydown', e => {
|
|
658
780
|
if (e.key === 'Enter') doSearch();
|
|
@@ -668,5 +790,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
668
790
|
if ($('theme-icon')) $('theme-icon').textContent = savedTheme === 'light' ? '☀️' : '🌙';
|
|
669
791
|
}
|
|
670
792
|
|
|
793
|
+
setupAutocomplete();
|
|
794
|
+
setupShortcuts();
|
|
671
795
|
initPyodide();
|
|
672
796
|
});
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "numclassify"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.2"
|
|
8
8
|
description = "The most comprehensive Python library for number classification - 3000+ number types"
|
|
9
9
|
authors = [{name = "Aratrik Ghosh", email = "aratrikghosh2011@gmail.com"}]
|
|
10
10
|
readme = "README.md"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|