@keyline-icons/cli 0.1.3 → 0.2.0

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keyline-icons/cli",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "description": "Search Keyline Icons and copy them into your project from the terminal.",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
package/src/index.mjs CHANGED
@@ -24,7 +24,7 @@ const data = JSON.parse(
24
24
  )
25
25
  const { icons, styles, keywords = {} } = data
26
26
  const NAMES = Object.keys(icons)
27
- const VERSION = "0.1.3"
27
+ const VERSION = "0.2.0"
28
28
 
29
29
  /** Colour only when a human is looking. Piped output stays clean. */
30
30
  const tty = process.stdout.isTTY
@@ -142,6 +142,45 @@ function keywordsFor(name) {
142
142
 
143
143
  const haystackFor = (name) => `${name} ${keywordsFor(name).join(" ")}`
144
144
 
145
+ /**
146
+ * What another set calls a drawing here: `message-square` for `message`,
147
+ * `arrow-big-up` for `arrow-up`, `loader-2` for `loader`.
148
+ *
149
+ * Matched against the whole query and never split into words, which is the
150
+ * entire reason it is a map of its own rather than more keywords. Folded into
151
+ * the vocabulary instead, `message-square` puts "square" in the message icon's
152
+ * words, and a search for `square` answers with every message in the set —
153
+ * measured on the site's own grid, where the first row of a `square` search
154
+ * came back holding no squares at all.
155
+ */
156
+ const foreign = data.names ?? {}
157
+
158
+ /**
159
+ * Whether `hay` carries `needle` as a whole word, hyphen or space delimited.
160
+ *
161
+ * The rule here used to be a plain substring, and a substring lands inside
162
+ * words that have nothing to do with the query: `box` matched `inbox`, `ad`
163
+ * matched `upload`, `car` matched `caret-up`, `monitor` matched `activity`
164
+ * through its words. Run another set's 1775 names through this and 140 came back
165
+ * holding an icon that shares no word with what was asked for. That reads as a
166
+ * hit, which is worse than an empty result twice over: it hides the gap from
167
+ * anyone counting, and it hands the caller a drawing they did not ask for.
168
+ *
169
+ * The site keeps the looser rule on purpose. It answers every keystroke, and
170
+ * someone typing `arro` has not finished the word yet. Nothing types here. An
171
+ * agent sends a whole name, and the honest answer to a name this set does not
172
+ * have is nothing at all.
173
+ */
174
+ const wholeWord = (hay, needle) => {
175
+ for (let i = hay.indexOf(needle); i !== -1; i = hay.indexOf(needle, i + 1)) {
176
+ const before = hay[i - 1] ?? "-"
177
+ const after = hay[i + needle.length] ?? "-"
178
+ if ((before === "-" || before === " ") && (after === "-" || after === " "))
179
+ return true
180
+ }
181
+ return false
182
+ }
183
+
145
184
  function search(query, style, limit) {
146
185
  const q = query.toLowerCase().trim()
147
186
  const words = wordsOf(query)
@@ -150,24 +189,24 @@ function search(query, style, limit) {
150
189
  return NAMES.filter(
151
190
  (n) =>
152
191
  (!style || icons[n][style]) &&
153
- (n.includes(q) ||
154
- words.every((w) => n.includes(w)) ||
155
- words.every((w) => haystackFor(n).includes(w)))
192
+ (foreign[q] === n ||
193
+ wholeWord(n, q) ||
194
+ words.every((w) => wholeWord(n, w)) ||
195
+ words.every((w) => wholeWord(haystackFor(n), w)))
156
196
  )
157
197
  .map((n) => {
158
- const i = n.indexOf(q)
159
198
  const rank =
160
- i === -1
161
- ? words.every((w) => n.includes(w))
162
- ? 4
163
- : 5
164
- : n === q
165
- ? 0
166
- : n.startsWith(q)
167
- ? 1
168
- : /(^|-)/.test(n[i - 1] ?? "-")
169
- ? 2
170
- : 3
199
+ n === q
200
+ ? 0
201
+ : foreign[q] === n
202
+ ? 1
203
+ : n.startsWith(q + "-")
204
+ ? 2
205
+ : wholeWord(n, q)
206
+ ? 3
207
+ : words.every((w) => wholeWord(n, w))
208
+ ? 4
209
+ : 5
171
210
  return { n, rank }
172
211
  })
173
212
  .sort(