numclassify 0.3.1__tar.gz → 0.3.2.1__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.1}/CHANGELOG.md +13 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/PKG-INFO +1 -1
- numclassify-0.3.2.1/docs/extra/saffron.css +37 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/docs/playground.css +155 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/docs/playground.html +17 -3
- {numclassify-0.3.1 → numclassify-0.3.2.1}/docs/playground.js +181 -21
- {numclassify-0.3.1 → numclassify-0.3.2.1}/mkdocs.yml +6 -2
- {numclassify-0.3.1 → numclassify-0.3.2.1}/pyproject.toml +1 -1
- {numclassify-0.3.1 → numclassify-0.3.2.1}/.github/workflows/ci.yml +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/.github/workflows/docs.yml +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/.github/workflows/publish.yml +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/.gitignore +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/CONTRIBUTING.md +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/LICENSE +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/README.md +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/docs/api.md +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/docs/changelog.md +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/docs/cli.md +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/docs/index.md +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/examples/basic_usage.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/examples/custom_type.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/examples/find_perfect_numbers.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/examples/random_classify.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/examples/stream_large_range.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/numclassify/__init__.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/numclassify/__main__.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/numclassify/_core/__init__.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/numclassify/_core/combinatorial.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/numclassify/_core/digital.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/numclassify/_core/divisors.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/numclassify/_core/figurate.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/numclassify/_core/number_theory.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/numclassify/_core/powers.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/numclassify/_core/primes.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/numclassify/_core/recreational.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/numclassify/_core/sequences.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/numclassify/_registry.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/numclassify/cli.py +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/numclassify/py.typed +0 -0
- {numclassify-0.3.1 → numclassify-0.3.2.1}/tests/test_registry.py +0 -0
|
@@ -6,6 +6,19 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## [0.3.2.1] - 2026-06-12
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- Search autocomplete dropdown not appearing — added fallback fetch of property names if initial Pyodide load fails, plus visible "Loading..." state in dropdown
|
|
13
|
+
|
|
14
|
+
## [0.3.2] - 2026-06-12
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
- Search autocomplete dropdown — suggests property names as you type in the search field
|
|
18
|
+
- Confetti celebration burst when a number scores >50 properties
|
|
19
|
+
- Keyboard shortcuts: `C` (classify), `S` (search), `N` (Number of the Day), `?`/`H` (show shortcuts overlay)
|
|
20
|
+
- `prefers-reduced-motion` support — disables all animations for accessibility
|
|
21
|
+
|
|
9
22
|
## [0.3.1] - 2026-06-12
|
|
10
23
|
|
|
11
24
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: numclassify
|
|
3
|
-
Version: 0.3.1
|
|
3
|
+
Version: 0.3.2.1
|
|
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
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--md-primary-fg-color: #FF9933;
|
|
3
|
+
--md-primary-fg-color--light: #FFB366;
|
|
4
|
+
--md-primary-fg-color--dark: #E6821A;
|
|
5
|
+
--md-primary-bg-color: #0A0A0F;
|
|
6
|
+
--md-primary-bg-color--light: #12121A;
|
|
7
|
+
--md-accent-fg-color: #FF9933;
|
|
8
|
+
--md-accent-fg-color--light: #FFB366;
|
|
9
|
+
--md-accent-fg-color--dark: #E6821A;
|
|
10
|
+
--md-typeset-a-color: #FF9933;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
[data-md-color-scheme="slate"] {
|
|
14
|
+
--md-default-bg-color: #0A0A0F;
|
|
15
|
+
--md-code-bg-color: #12121A;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.md-header {
|
|
19
|
+
background: linear-gradient(135deg, #E6821A, #FF9933) !important;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.md-tabs {
|
|
23
|
+
background: #E6821A !important;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.md-search__form {
|
|
27
|
+
background: rgba(255,255,255,0.15) !important;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
a:hover {
|
|
31
|
+
color: #FFB366 !important;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.md-footer {
|
|
35
|
+
background: #0A0A0F !important;
|
|
36
|
+
border-top: 1px solid rgba(255,153,51,0.2);
|
|
37
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
<header>
|
|
20
20
|
<button class="theme-toggle" onclick="toggleTheme()" title="Toggle theme"><span id="theme-icon">🌙</span></button>
|
|
21
21
|
<div class="logo-badge">numclassify playground</div>
|
|
22
|
-
<div class="version-badge" id="version-badge">v<span id="version-text">—</span></div>
|
|
22
|
+
<div class="version-badge" id="version-badge" data-version="0.3.2.1">v<span id="version-text">—</span></div>
|
|
23
23
|
<h1>Number Intelligence</h1>
|
|
24
24
|
<p class="subtitle">Classify any integer into 3000+ named mathematical types — powered by real Python in your browser.</p>
|
|
25
25
|
</header>
|
|
@@ -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);
|
|
@@ -225,12 +228,18 @@ json.dumps(m)
|
|
|
225
228
|
console.warn('Could not load category map', e);
|
|
226
229
|
}
|
|
227
230
|
|
|
228
|
-
// Fetch version number
|
|
231
|
+
// Fetch version number — prefer local source version as canonical
|
|
232
|
+
const badge = $('version-badge');
|
|
233
|
+
const srcVersion = badge ? badge.dataset.version : null;
|
|
229
234
|
try {
|
|
230
|
-
const
|
|
231
|
-
|
|
235
|
+
const pypiVer = await pyodide.runPythonAsync('nc.__version__');
|
|
236
|
+
// Use the source version (from pyproject.toml) since PyPI may lag behind
|
|
237
|
+
$('version-text').textContent = srcVersion || pypiVer;
|
|
238
|
+
if (srcVersion && pypiVer !== srcVersion) {
|
|
239
|
+
badge.title = 'Source: v' + srcVersion + ' | PyPI: v' + pypiVer;
|
|
240
|
+
}
|
|
232
241
|
} catch(e) {
|
|
233
|
-
$('version-text').textContent = '
|
|
242
|
+
$('version-text').textContent = srcVersion || '?';
|
|
234
243
|
}
|
|
235
244
|
|
|
236
245
|
setProgress(100, 'Ready!');
|
|
@@ -256,6 +265,13 @@ json.dumps(m)
|
|
|
256
265
|
|
|
257
266
|
async function doClassify(n = null) {
|
|
258
267
|
if (!requireReady()) return;
|
|
268
|
+
// If no explicit number and input contains commas/spaces, go to batch mode
|
|
269
|
+
if (n === null) {
|
|
270
|
+
const raw = $('input-classify').value.trim();
|
|
271
|
+
if (raw.includes(',') || /\s/.test(raw)) {
|
|
272
|
+
return doBatchClassify();
|
|
273
|
+
}
|
|
274
|
+
}
|
|
259
275
|
const val = n !== null ? n : parseInt($('input-classify').value);
|
|
260
276
|
if (isNaN(val)) { toast('Enter a valid integer.'); return; }
|
|
261
277
|
|
|
@@ -271,9 +287,12 @@ json.dumps({"number": r["number"], "score": r["score"], "props": r["true_propert
|
|
|
271
287
|
`);
|
|
272
288
|
const data = JSON.parse(result);
|
|
273
289
|
|
|
290
|
+
batchMode = false;
|
|
274
291
|
renderNumber($('classify-number'), data.number);
|
|
275
|
-
animateScore(
|
|
292
|
+
animateScore(data.score);
|
|
276
293
|
makeTags(data.props, $('classify-tags'));
|
|
294
|
+
$('batch-actions').style.display = '';
|
|
295
|
+
if (data.score > 50) burstConfetti();
|
|
277
296
|
|
|
278
297
|
const res = $('result-classify');
|
|
279
298
|
res.style.display = 'block';
|
|
@@ -327,6 +346,7 @@ json.dumps(results)
|
|
|
327
346
|
`);
|
|
328
347
|
const results = JSON.parse(result);
|
|
329
348
|
|
|
349
|
+
batchMode = true;
|
|
330
350
|
const res = $('result-classify');
|
|
331
351
|
res.style.display = 'block';
|
|
332
352
|
res.classList.remove('show');
|
|
@@ -334,7 +354,8 @@ json.dumps(results)
|
|
|
334
354
|
res.classList.add('show');
|
|
335
355
|
|
|
336
356
|
$('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').
|
|
357
|
+
$('classify-score').innerHTML = '<span class="score-count"></span>';
|
|
358
|
+
$('batch-actions').style.display = 'none';
|
|
338
359
|
|
|
339
360
|
let html = '<table class="batch-table"><thead><tr><th>Number</th><th>Score</th><th>Properties</th></tr></thead><tbody>';
|
|
340
361
|
results.forEach(r => {
|
|
@@ -538,6 +559,7 @@ json.dumps({"only_a": only_a, "only_b": only_b, "shared": shared})
|
|
|
538
559
|
// ── Number of the Day ────────────────────────────────────────────────────
|
|
539
560
|
|
|
540
561
|
async function computeNOTD(dateStr) {
|
|
562
|
+
if (!pyReady) return;
|
|
541
563
|
try {
|
|
542
564
|
if ($('notd-content')) $('notd-content').style.display = 'none';
|
|
543
565
|
if ($('notd-loading')) $('notd-loading').style.display = 'block';
|
|
@@ -580,6 +602,7 @@ function onNotdDateChange(input) {
|
|
|
580
602
|
// ── Copy & Share & Download ──────────────────────────────────────────────
|
|
581
603
|
|
|
582
604
|
async function copyResults() {
|
|
605
|
+
if (batchMode) { toast('Cannot copy batch results — classify a single number first.'); return; }
|
|
583
606
|
const num = $('classify-number').textContent;
|
|
584
607
|
const score = $('classify-score').textContent;
|
|
585
608
|
const tags = [...$('classify-tags').querySelectorAll('.tag')].map(t => t.textContent).join(', ');
|
|
@@ -606,10 +629,12 @@ function shareURL() {
|
|
|
606
629
|
}
|
|
607
630
|
|
|
608
631
|
function downloadJSON() {
|
|
632
|
+
if (batchMode) { toast('Cannot download batch results — classify a single number first.'); return; }
|
|
609
633
|
const num = $('classify-number').textContent;
|
|
610
|
-
const
|
|
634
|
+
const scoreEl = document.querySelector('#classify-score .score-count');
|
|
635
|
+
const score = scoreEl ? parseInt(scoreEl.textContent) || 0 : 0;
|
|
611
636
|
const tags = [...$('classify-tags').querySelectorAll('.tag')].map(t => t.textContent);
|
|
612
|
-
const data = { number: parseInt(num) || num, score
|
|
637
|
+
const data = { number: parseInt(num) || num, score, properties: tags };
|
|
613
638
|
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
|
|
614
639
|
const url = URL.createObjectURL(blob);
|
|
615
640
|
const a = document.createElement('a');
|
|
@@ -643,16 +668,149 @@ function scrollToTop() {
|
|
|
643
668
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
644
669
|
}
|
|
645
670
|
|
|
671
|
+
// ── Search Autocomplete ───────────────────────────────────────────────────
|
|
672
|
+
|
|
673
|
+
function setupAutocomplete() {
|
|
674
|
+
const input = $('input-property');
|
|
675
|
+
if (!input) return;
|
|
676
|
+
const dropdown = $('ac-dropdown');
|
|
677
|
+
let selectedIdx = -1;
|
|
678
|
+
|
|
679
|
+
input.addEventListener('input', async () => {
|
|
680
|
+
const val = input.value.trim().toLowerCase();
|
|
681
|
+
if (!val) { dropdown.classList.remove('open'); return; }
|
|
682
|
+
|
|
683
|
+
// Fallback: if allProperties is empty (Pyodide category fetch failed), try on-the-fly
|
|
684
|
+
if (!allProperties.length && pyReady && pyodide) {
|
|
685
|
+
try {
|
|
686
|
+
const sample = await pyodide.runPythonAsync(`
|
|
687
|
+
import json
|
|
688
|
+
from numclassify._registry import REGISTRY
|
|
689
|
+
keys = list(REGISTRY.keys())
|
|
690
|
+
json.dumps(keys)
|
|
691
|
+
`);
|
|
692
|
+
const keys = JSON.parse(sample);
|
|
693
|
+
if (keys.length) {
|
|
694
|
+
allProperties = keys;
|
|
695
|
+
categoryMap = {};
|
|
696
|
+
keys.forEach(k => { categoryMap[k] = ''; });
|
|
697
|
+
console.log('Autocomplete: fallback loaded', keys.length, 'properties');
|
|
698
|
+
}
|
|
699
|
+
} catch(e) {
|
|
700
|
+
console.warn('Autocomplete fallback failed', e);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
const matches = allProperties
|
|
705
|
+
.filter(p => p.includes(val))
|
|
706
|
+
.slice(0, 10);
|
|
707
|
+
if (!matches.length) {
|
|
708
|
+
if (!allProperties.length) {
|
|
709
|
+
dropdown.innerHTML = '<div class="ac-item" style="cursor:default;color:var(--text-muted)">Loading properties...</div>';
|
|
710
|
+
dropdown.classList.add('open');
|
|
711
|
+
} else {
|
|
712
|
+
dropdown.classList.remove('open');
|
|
713
|
+
}
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
selectedIdx = -1;
|
|
717
|
+
dropdown.innerHTML = matches.map((p, i) =>
|
|
718
|
+
`<div class="ac-item${i === 0 ? ' selected' : ''}" data-prop="${p}" onclick="pickAutocomplete('${p}')">
|
|
719
|
+
${p.replace(/_/g, ' ')}
|
|
720
|
+
<span class="ac-cat">${(categoryMap[p] || '').replace(/_/g, ' ')}</span>
|
|
721
|
+
</div>`
|
|
722
|
+
).join('');
|
|
723
|
+
dropdown.classList.add('open');
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
input.addEventListener('keydown', e => {
|
|
727
|
+
const items = dropdown.querySelectorAll('.ac-item');
|
|
728
|
+
if (!items.length) return;
|
|
729
|
+
if (e.key === 'ArrowDown') {
|
|
730
|
+
e.preventDefault();
|
|
731
|
+
selectedIdx = Math.min(selectedIdx + 1, items.length - 1);
|
|
732
|
+
} else if (e.key === 'ArrowUp') {
|
|
733
|
+
e.preventDefault();
|
|
734
|
+
selectedIdx = Math.max(selectedIdx - 1, -1);
|
|
735
|
+
} else if (e.key === 'Enter' && selectedIdx >= 0) {
|
|
736
|
+
e.preventDefault();
|
|
737
|
+
items[selectedIdx].click();
|
|
738
|
+
return;
|
|
739
|
+
} else return;
|
|
740
|
+
items.forEach((el, i) => el.classList.toggle('selected', i === selectedIdx));
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
document.addEventListener('click', e => {
|
|
744
|
+
if (!input.contains(e.target) && !dropdown.contains(e.target)) {
|
|
745
|
+
dropdown.classList.remove('open');
|
|
746
|
+
}
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
function pickAutocomplete(prop) {
|
|
751
|
+
$('input-property').value = prop;
|
|
752
|
+
$('ac-dropdown').classList.remove('open');
|
|
753
|
+
doSearch();
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
// ── Confetti ──────────────────────────────────────────────────────────────
|
|
757
|
+
|
|
758
|
+
function burstConfetti() {
|
|
759
|
+
const container = document.createElement('div');
|
|
760
|
+
container.className = 'confetti-container';
|
|
761
|
+
const colors = ['#FF9933', '#FF6B6B', '#4ECDC4', '#45B7D1', '#F7DC6F', '#DDA0DD', '#98D8C8', '#BB8FCE', '#FFEAA7', '#4CAF7D'];
|
|
762
|
+
for (let i = 0; i < 80; i++) {
|
|
763
|
+
const piece = document.createElement('div');
|
|
764
|
+
piece.className = 'confetti-piece';
|
|
765
|
+
const size = 4 + Math.random() * 8;
|
|
766
|
+
piece.style.width = size + 'px';
|
|
767
|
+
piece.style.height = size + 'px';
|
|
768
|
+
piece.style.background = colors[Math.floor(Math.random() * colors.length)];
|
|
769
|
+
piece.style.left = Math.random() * 100 + '%';
|
|
770
|
+
piece.style.borderRadius = Math.random() > 0.5 ? '50%' : '2px';
|
|
771
|
+
piece.style.animationDuration = (1.5 + Math.random() * 2) + 's';
|
|
772
|
+
piece.style.animationDelay = Math.random() * 0.5 + 's';
|
|
773
|
+
container.appendChild(piece);
|
|
774
|
+
}
|
|
775
|
+
document.body.appendChild(container);
|
|
776
|
+
setTimeout(() => container.remove(), 3500);
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
// ── Keyboard Shortcuts ────────────────────────────────────────────────────
|
|
780
|
+
|
|
781
|
+
const SHORTCUT_MAP = {
|
|
782
|
+
c: 'classify',
|
|
783
|
+
s: 'search',
|
|
784
|
+
n: 'notd',
|
|
785
|
+
};
|
|
786
|
+
|
|
787
|
+
function setupShortcuts() {
|
|
788
|
+
document.addEventListener('keydown', e => {
|
|
789
|
+
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
|
|
790
|
+
const key = e.key.toLowerCase();
|
|
791
|
+
if (key === '?' || key === 'h') {
|
|
792
|
+
e.preventDefault();
|
|
793
|
+
$('shortcuts-overlay').classList.toggle('open');
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
const tab = SHORTCUT_MAP[key];
|
|
797
|
+
if (tab) {
|
|
798
|
+
e.preventDefault();
|
|
799
|
+
switchTab(tab);
|
|
800
|
+
}
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
function closeShortcuts() {
|
|
805
|
+
$('shortcuts-overlay').classList.remove('open');
|
|
806
|
+
}
|
|
807
|
+
|
|
646
808
|
// ── Init ─────────────────────────────────────────────────────────────────
|
|
647
809
|
|
|
648
810
|
document.addEventListener('DOMContentLoaded', () => {
|
|
649
811
|
// Enter key support
|
|
650
812
|
$('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
|
-
}
|
|
813
|
+
if (e.key === 'Enter') doClassify();
|
|
656
814
|
});
|
|
657
815
|
$('input-property')?.addEventListener('keydown', e => {
|
|
658
816
|
if (e.key === 'Enter') doSearch();
|
|
@@ -668,5 +826,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
668
826
|
if ($('theme-icon')) $('theme-icon').textContent = savedTheme === 'light' ? '☀️' : '🌙';
|
|
669
827
|
}
|
|
670
828
|
|
|
829
|
+
setupAutocomplete();
|
|
830
|
+
setupShortcuts();
|
|
671
831
|
initPyodide();
|
|
672
832
|
});
|
|
@@ -7,13 +7,17 @@ repo_name: aratrikghosh2011-tech/numclassify
|
|
|
7
7
|
theme:
|
|
8
8
|
name: material
|
|
9
9
|
palette:
|
|
10
|
-
primary:
|
|
11
|
-
accent:
|
|
10
|
+
primary: custom
|
|
11
|
+
accent: custom
|
|
12
|
+
scheme: slate
|
|
12
13
|
features:
|
|
13
14
|
- navigation.tabs
|
|
14
15
|
- navigation.top
|
|
15
16
|
- content.code.copy
|
|
16
17
|
|
|
18
|
+
extra_css:
|
|
19
|
+
- extra/saffron.css
|
|
20
|
+
|
|
17
21
|
nav:
|
|
18
22
|
- Home: index.md
|
|
19
23
|
- API Reference: api.md
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "numclassify"
|
|
7
|
-
version = "0.3.1"
|
|
7
|
+
version = "0.3.2.1"
|
|
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
|