@furifont/core 0.2.0 → 0.3.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/dist/index.d.mts CHANGED
@@ -18,6 +18,7 @@ export declare const DEFAULTS: {
18
18
  /** Uses in Tatoeba's example sentences that select a word whatever its bucket: about 1,000 more words, and 50 glyphs. */
19
19
  readonly minTatoeba: 3;
20
20
  readonly places: true;
21
+ readonly untagged: true;
21
22
  readonly isolated: true;
22
23
  readonly isolatedMinShare: 0.8;
23
24
  /** Sizes in em, relative to the base font. */
@@ -119,6 +120,8 @@ interface Dictionary {
119
120
  form: string;
120
121
  furigana: string;
121
122
  }[];
123
+ /** Word lists kept by hand, by source name, in inline notation; like place names, they fill runs no common word claims. */
124
+ manual?: Record<string, string[]>;
122
125
  }
123
126
  /** A ruby annotation over characters `[start, end)` of a run. */
124
127
  interface RubySpan {
@@ -154,6 +157,8 @@ export declare function formatNotation(text: string, spans: readonly RubySpan[])
154
157
  export declare const RUBY_SCRIPTS: readonly ["hiragana", "katakanaOn", "hepburn"];
155
158
  export declare const PRIORITY_TAGS: readonly ["ichi1", "ichi2", "news1", "news2", "spec1", "spec2", "gai1", "gai2"];
156
159
  /** Easiest first. */
160
+ /** The dictionary's manually maintained sources, which a plan may include by name. */
161
+ export declare const MANUAL_SOURCES: readonly ["mahjong-yaku", "jr-stations", "yojijukugo"];
157
162
  export declare const JLPT_LEVELS: readonly ["N5", "N4", "N3", "N2", "N1"];
158
163
  type RubyScript = typeof RUBY_SCRIPTS[number];
159
164
  /** Sizes in em, relative to the base font. */
@@ -200,6 +205,10 @@ export declare const PlanOptionsSchema: v.SchemaWithPipe<readonly [v.ObjectSchem
200
205
  readonly skipJlpt: v.OptionalSchema<v.PicklistSchema<readonly ["N5", "N4", "N3", "N2", "N1"], undefined>, undefined>;
201
206
  /** Annotate place names from the dictionary where no common word claims the run. */
202
207
  readonly places: v.OptionalSchema<v.BooleanSchema<undefined>, true>;
208
+ /** Annotate the words of JMdict that no list ranks or tags (such as 四字熟語), where no common word, manual list or place name claims the run; each gets its first reading. */
209
+ readonly untagged: v.OptionalSchema<v.BooleanSchema<undefined>, true>;
210
+ /** The dictionary's manually maintained sources (such as `mahjong-yaku`) to annotate; all of them when left out. */
211
+ readonly manual: v.OptionalSchema<v.ArraySchema<v.PicklistSchema<readonly ["mahjong-yaku", "jr-stations", "yojijukugo"], undefined>, undefined>, undefined>;
203
212
  /** Annotate a kanji alone in its run, or one no word claims after another kanji (a suffix); when off, only selectors give them ruby. */
204
213
  readonly isolated: v.OptionalSchema<v.BooleanSchema<undefined>, true>;
205
214
  /** A kanji alone in its run is annotated only when one reading has this share of the evidence. */
@@ -231,6 +240,8 @@ export declare const PlanOptionsSchema: v.SchemaWithPipe<readonly [v.ObjectSchem
231
240
  minTatoeba: number;
232
241
  skipJlpt?: "N5" | "N4" | "N3" | "N2" | "N1" | undefined;
233
242
  places: boolean;
243
+ untagged: boolean;
244
+ manual?: ("mahjong-yaku" | "jr-stations" | "yojijukugo")[] | undefined;
234
245
  isolated: boolean;
235
246
  isolatedMinShare: number;
236
247
  words: {
package/dist/index.mjs CHANGED
@@ -23,6 +23,7 @@ const DEFAULTS = {
23
23
  /** Uses in Tatoeba's example sentences that select a word whatever its bucket: about 1,000 more words, and 50 glyphs. */
24
24
  minTatoeba: 3,
25
25
  places: true,
26
+ untagged: true,
26
27
  isolated: true,
27
28
  isolatedMinShare: .8,
28
29
  /** Sizes in em, relative to the base font. */
@@ -680,6 +681,40 @@ function addPlaces(patterns, places, skipped, ctx) {
680
681
  }]);
681
682
  }
682
683
  }
684
+ /** Words of the manual sources fill the runs no common word claims, nor a skipped one; they come before place names. */
685
+ function addManual(patterns, manual, skipped, ctx) {
686
+ const names = ctx.options.manual ?? Object.keys(manual);
687
+ for (const notation of names.flatMap((name) => manual[name] ?? [])) {
688
+ const { form, spans } = parseNotation(notation);
689
+ for (const run of hanRuns(form, spans).filter(ctx.renderable)) {
690
+ if (length(run.text) === 1 || patterns.has(run.text) || skipped.has(run.text) || ctx.removed.has(run.text)) continue;
691
+ patterns.set(run.text, [{
692
+ spans: run.spans,
693
+ weight: 0,
694
+ tagged: false,
695
+ order: 0,
696
+ nf: 0,
697
+ jlpt: 0,
698
+ tags: /* @__PURE__ */ new Set(),
699
+ place: true
700
+ }]);
701
+ }
702
+ }
703
+ }
704
+ /** Words no list ranks fill the runs nothing else claims, each with its first reading: they are no evidence for the kanji's own. */
705
+ function addUntagged(patterns, index, skipped, ctx) {
706
+ for (const [text, readings] of index.runs) {
707
+ if (patterns.has(text) || skipped.has(text) || ctx.removed.has(text)) continue;
708
+ const first = [...readings.values()].filter((r) => ctx.renderable({
709
+ text,
710
+ spans: r.spans
711
+ })).sort((a, b) => a.order - b.order)[0];
712
+ if (first) patterns.set(text, [{
713
+ ...first,
714
+ place: true
715
+ }]);
716
+ }
717
+ }
683
718
  /**
684
719
  * Runs of common words, then place names and custom words; pinned readings of
685
720
  * lone kanji are returned apart. A word is skipped whole: were only its known
@@ -700,7 +735,9 @@ function selectWords(index, dictionary, ctx) {
700
735
  }));
701
736
  if (common.length) patterns.set(text, common.map((r) => ({ ...r })));
702
737
  }
738
+ if (dictionary.manual) addManual(patterns, dictionary.manual, skipped, ctx);
703
739
  if (ctx.options.places) addPlaces(patterns, dictionary.places, skipped, ctx);
740
+ if (ctx.options.untagged) addUntagged(patterns, index, skipped, ctx);
704
741
  const lonePins = /* @__PURE__ */ new Map();
705
742
  for (const notation of ctx.options.words.add) addCustomWord(patterns, lonePins, notation, 0, ctx);
706
743
  for (const notation of dictionary.defaults ?? []) addCustomWord(patterns, lonePins, notation, 1, ctx);
@@ -1226,6 +1263,12 @@ const PRIORITY_TAGS = [
1226
1263
  "gai2"
1227
1264
  ];
1228
1265
  /** Easiest first. */
1266
+ /** The dictionary's manually maintained sources, which a plan may include by name. */
1267
+ const MANUAL_SOURCES = [
1268
+ "mahjong-yaku",
1269
+ "jr-stations",
1270
+ "yojijukugo"
1271
+ ];
1229
1272
  const JLPT_LEVELS = [
1230
1273
  "N5",
1231
1274
  "N4",
@@ -1276,6 +1319,10 @@ const PlanOptionsSchema = withNfWindow(v.object({
1276
1319
  skipJlpt: v.optional(v.picklist(JLPT_LEVELS)),
1277
1320
  /** Annotate place names from the dictionary where no common word claims the run. */
1278
1321
  places: v.optional(v.boolean(), DEFAULTS.plan.places),
1322
+ /** Annotate the words of JMdict that no list ranks or tags (such as 四字熟語), where no common word, manual list or place name claims the run; each gets its first reading. */
1323
+ untagged: v.optional(v.boolean(), DEFAULTS.plan.untagged),
1324
+ /** The dictionary's manually maintained sources (such as `mahjong-yaku`) to annotate; all of them when left out. */
1325
+ manual: v.optional(v.array(v.picklist(MANUAL_SOURCES))),
1279
1326
  /** Annotate a kanji alone in its run, or one no word claims after another kanji (a suffix); when off, only selectors give them ruby. */
1280
1327
  isolated: v.optional(v.boolean(), DEFAULTS.plan.isolated),
1281
1328
  /** A kanji alone in its run is annotated only when one reading has this share of the evidence. */
@@ -1343,4 +1390,4 @@ const PlanSchema = v.object({
1343
1390
  }))
1344
1391
  });
1345
1392
  //#endregion
1346
- export { DEFAULTS, JLPT_LEVELS, MAX_READINGS, PREBUILT, PRIORITY_TAGS, PlanOptionsSchema, PlanSchema, RUBY_SCRIPTS, SELECTOR_FIRST_READING, SELECTOR_SUPPRESS, VERSION_PROBE, createFontPlanner, formatNotation, glyphName, hanRuns, isHan, isOnyomi, parseFurigana, parseNotation, resolveOptions, toHepburn, toHiragana, toKatakana, withNfWindow };
1393
+ export { DEFAULTS, JLPT_LEVELS, MANUAL_SOURCES, MAX_READINGS, PREBUILT, PRIORITY_TAGS, PlanOptionsSchema, PlanSchema, RUBY_SCRIPTS, SELECTOR_FIRST_READING, SELECTOR_SUPPRESS, VERSION_PROBE, createFontPlanner, formatNotation, glyphName, hanRuns, isHan, isOnyomi, parseFurigana, parseNotation, resolveOptions, toHepburn, toHiragana, toKatakana, withNfWindow };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@furifont/core",
3
3
  "type": "module",
4
- "version": "0.2.0",
4
+ "version": "0.3.0",
5
5
  "description": "Plan furigana glyphs and OpenType rules from a Japanese dictionary",
6
6
  "author": "Anthony Fu <anthonyfu117@hotmail.com>",
7
7
  "license": "MIT",