@lokascript/hyperscript-adapter 2.3.1 → 2.4.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.
Files changed (39) hide show
  1. package/dist/hyperscript-i18n-ar.global.js +4 -4
  2. package/dist/hyperscript-i18n-bn.global.js +4 -4
  3. package/dist/hyperscript-i18n-de.global.js +4 -4
  4. package/dist/hyperscript-i18n-east-asian.global.js +4 -4
  5. package/dist/hyperscript-i18n-en.global.js +6 -6
  6. package/dist/hyperscript-i18n-es.global.js +4 -4
  7. package/dist/hyperscript-i18n-fr.global.js +4 -4
  8. package/dist/hyperscript-i18n-he.global.js +4 -4
  9. package/dist/hyperscript-i18n-hi.global.js +4 -4
  10. package/dist/hyperscript-i18n-id.global.js +4 -4
  11. package/dist/hyperscript-i18n-it.global.js +4 -4
  12. package/dist/hyperscript-i18n-ja.global.js +4 -4
  13. package/dist/hyperscript-i18n-ko.global.js +4 -4
  14. package/dist/hyperscript-i18n-ms.global.js +4 -4
  15. package/dist/hyperscript-i18n-pl.global.js +4 -4
  16. package/dist/hyperscript-i18n-pt.global.js +4 -4
  17. package/dist/hyperscript-i18n-qu.global.js +4 -4
  18. package/dist/hyperscript-i18n-ru.global.js +4 -4
  19. package/dist/hyperscript-i18n-slavic.global.js +4 -4
  20. package/dist/hyperscript-i18n-south-asian.global.js +4 -4
  21. package/dist/hyperscript-i18n-southeast-asian.global.js +4 -4
  22. package/dist/hyperscript-i18n-sw.global.js +4 -4
  23. package/dist/hyperscript-i18n-th.global.js +4 -4
  24. package/dist/hyperscript-i18n-tl.global.js +4 -4
  25. package/dist/hyperscript-i18n-tr.global.js +4 -4
  26. package/dist/hyperscript-i18n-uk.global.js +4 -4
  27. package/dist/hyperscript-i18n-vi.global.js +4 -4
  28. package/dist/hyperscript-i18n-western.global.js +4 -4
  29. package/dist/hyperscript-i18n-zh.global.js +4 -4
  30. package/dist/hyperscript-i18n.global.js +24 -35
  31. package/dist/hyperscript-i18n.global.js.map +1 -1
  32. package/dist/index.cjs +8 -14
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.js +10 -16
  35. package/dist/index.js.map +1 -1
  36. package/package.json +2 -2
  37. package/src/generated/syntax-table.ts +12 -1
  38. package/src/preprocessor.ts +12 -22
  39. package/src/slim-preprocessor.ts +6 -24
package/dist/index.cjs CHANGED
@@ -53,13 +53,6 @@ function resolveThreshold(threshold, lang) {
53
53
  if (typeof threshold === "number") return threshold;
54
54
  return threshold[lang] ?? threshold["*"] ?? DEFAULT_THRESHOLD;
55
55
  }
56
- var analyzer = null;
57
- function getAnalyzer() {
58
- if (!analyzer) {
59
- analyzer = (0, import_semantic.createSemanticAnalyzer)();
60
- }
61
- return analyzer;
62
- }
63
56
  function preprocessToEnglish(src, lang, config = {}) {
64
57
  if (lang === "en") return src;
65
58
  const cfg = { ...DEFAULT_CONFIG, ...config };
@@ -99,14 +92,15 @@ function trySemanticTranslation(src, lang, threshold) {
99
92
  if (statements.length > 1) {
100
93
  return translateCompound(statements, lang, threshold);
101
94
  }
102
- const sem = getAnalyzer();
103
- if (!sem.supportsLanguage(lang)) return null;
104
- const result = sem.analyze(src, lang);
105
- if (!(0, import_semantic.shouldUseSemanticResult)(result, threshold)) return null;
106
- if (result.node) {
107
- return (0, import_semantic.render)(result.node, "en");
95
+ if (!(0, import_semantic.isLanguageRegistered)(lang)) return null;
96
+ const result = (0, import_semantic.parseSemantic)(src, lang);
97
+ if (result.confidence < threshold || !result.node) {
98
+ if (result.confidence >= threshold) {
99
+ return (0, import_semantic.translate)(src, lang, "en");
100
+ }
101
+ return null;
108
102
  }
109
- return (0, import_semantic.translate)(src, lang, "en");
103
+ return (0, import_semantic.render)(result.node, "en");
110
104
  } catch {
111
105
  return null;
112
106
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/language-resolver.ts","../src/preprocessor.ts","../src/plugin.ts"],"sourcesContent":["/**\n * @lokascript/hyperscript-adapter\n *\n * Multilingual adapter plugin for the original _hyperscript.\n * Enables writing hyperscript in any of 24 supported languages.\n *\n * @example\n * import { hyperscriptI18n } from '@lokascript/hyperscript-adapter';\n * _hyperscript.use(hyperscriptI18n());\n *\n * @example\n * // With options\n * _hyperscript.use(hyperscriptI18n({\n * defaultLanguage: 'ja',\n * debug: true,\n * }));\n *\n * @example\n * // Standalone preprocessing (for _hyperscript.evaluate() calls)\n * import { preprocess } from '@lokascript/hyperscript-adapter';\n * const english = preprocess('トグル .active', 'ja');\n * _hyperscript(english);\n */\n\nexport { hyperscriptI18n, preprocess, type PluginOptions } from './plugin';\nexport { preprocessToEnglish, type PreprocessorConfig } from './preprocessor';\nexport { resolveLanguage } from './language-resolver';\n","/**\n * Language Resolver\n *\n * Determines the language of hyperscript code on a given element\n * using a cascading resolution strategy.\n */\n\n/**\n * Resolve the language for a hyperscript attribute on an element.\n *\n * Resolution order:\n * 1. `data-lang` attribute on the element itself\n * 2. `data-hyperscript-lang` attribute on the element or closest ancestor\n * 3. `lang` attribute on `<html>` element\n * 4. null (assume English, no preprocessing needed)\n */\nexport function resolveLanguage(elt: Element): string | null {\n // 1. Explicit per-element\n const dataLang = elt.getAttribute('data-lang');\n if (dataLang) return normalizeLangCode(dataLang);\n\n // 2. Inherited from ancestor\n const hsLang =\n elt.getAttribute('data-hyperscript-lang') ??\n elt.closest?.('[data-hyperscript-lang]')?.getAttribute('data-hyperscript-lang');\n if (hsLang) return normalizeLangCode(hsLang);\n\n // 3. Document-level lang\n const htmlLang = typeof document !== 'undefined' ? document.documentElement?.lang : null;\n if (htmlLang && htmlLang !== 'en') return normalizeLangCode(htmlLang);\n\n return null;\n}\n\n/**\n * Normalize a BCP-47 language tag to an ISO 639-1 code.\n * e.g., \"ja-JP\" → \"ja\", \"zh-Hans\" → \"zh\", \"pt-BR\" → \"pt\"\n */\nfunction normalizeLangCode(lang: string): string {\n return lang.split('-')[0].toLowerCase();\n}\n","/**\n * Preprocessor\n *\n * Translates non-English hyperscript to English using the semantic parser\n * (with optional i18n fallback), so the original _hyperscript can parse it.\n */\n\nimport {\n translate,\n render,\n createSemanticAnalyzer,\n shouldUseSemanticResult,\n tryGetProfile,\n type SemanticAnalyzer,\n} from '@lokascript/semantic';\n\nexport interface PreprocessorConfig {\n /**\n * Minimum confidence threshold for semantic parsing (0-1). Default: 0.5.\n * Can be a single number (applies to all languages) or a per-language map.\n * Per-language thresholds are useful because SOV languages (ja, ko, tr) produce\n * inherently lower confidence scores than SVO languages (es, fr, de).\n *\n * @example\n * // Single threshold\n * { confidenceThreshold: 0.5 }\n *\n * @example\n * // Per-language thresholds\n * { confidenceThreshold: { es: 0.7, ja: 0.1, ko: 0.05, '*': 0.5 } }\n */\n confidenceThreshold: number | Record<string, number>;\n /** Strategy: 'semantic' (default), 'i18n', or 'auto' (semantic then i18n) */\n strategy: 'semantic' | 'i18n' | 'auto';\n /** Whether to return original text on failure. Default: true */\n fallbackToOriginal: boolean;\n /** Optional i18n toEnglish function (loaded dynamically if available) */\n i18nToEnglish?: (input: string, locale: string) => string;\n}\n\nconst DEFAULT_THRESHOLD = 0.5;\n\nconst DEFAULT_CONFIG: PreprocessorConfig = {\n confidenceThreshold: DEFAULT_THRESHOLD,\n strategy: 'semantic',\n fallbackToOriginal: true,\n};\n\n/**\n * Resolve the confidence threshold for a specific language.\n * Supports both a single number and a per-language map with '*' as default.\n */\nfunction resolveThreshold(threshold: number | Record<string, number>, lang: string): number {\n if (typeof threshold === 'number') return threshold;\n return threshold[lang] ?? threshold['*'] ?? DEFAULT_THRESHOLD;\n}\n\nlet analyzer: SemanticAnalyzer | null = null;\n\nfunction getAnalyzer(): SemanticAnalyzer {\n if (!analyzer) {\n analyzer = createSemanticAnalyzer();\n }\n return analyzer;\n}\n\n/**\n * Preprocess non-English hyperscript into English.\n *\n * Uses the semantic parser to parse the input in the source language,\n * then renders back to English. Falls through to i18n if configured.\n *\n * Handles _hyperscript feature prefixes (e.g. \"on click\", \"on every keyup\")\n * by stripping them, translating only the command portion, then reassembling.\n */\nexport function preprocessToEnglish(\n src: string,\n lang: string,\n config: Partial<PreprocessorConfig> = {}\n): string {\n // English→English is identity; skip semantic parsing which may mangle the input\n if (lang === 'en') return src;\n\n const cfg = { ...DEFAULT_CONFIG, ...config };\n\n // Try translating the full string first\n const fullResult = tryTranslateWithStrategies(src, lang, cfg);\n if (fullResult !== null) return fullResult;\n\n // If full translation failed, try stripping event/feature prefix.\n // _hyperscript attributes often contain \"on <event> <commands>\" — the semantic\n // parser only understands command syntax, not event declarations.\n const stripped = stripEventPrefix(src);\n if (stripped) {\n const translated = tryTranslateWithStrategies(stripped.commands, lang, cfg);\n if (translated !== null) return stripped.prefix + translated;\n }\n\n // Fallback: return original\n return cfg.fallbackToOriginal ? src : src;\n}\n\n/**\n * Try all configured translation strategies on the input.\n * Returns null if none succeed.\n */\nfunction tryTranslateWithStrategies(\n src: string,\n lang: string,\n cfg: PreprocessorConfig\n): string | null {\n // Strategy: semantic-only\n if (cfg.strategy === 'semantic' || cfg.strategy === 'auto') {\n const threshold = resolveThreshold(cfg.confidenceThreshold, lang);\n const result = trySemanticTranslation(src, lang, threshold);\n if (result !== null) return result;\n }\n\n // Strategy: i18n fallback (auto mode or i18n-only)\n if ((cfg.strategy === 'auto' || cfg.strategy === 'i18n') && cfg.i18nToEnglish) {\n const result = tryI18nTranslation(src, lang, cfg.i18nToEnglish);\n if (result !== null) return result;\n }\n\n return null;\n}\n\n/**\n * Match _hyperscript event handler prefix: \"on [every] <event>[filter][.modifiers] \"\n *\n * Examples:\n * \"on click toggle .active\" → prefix: \"on click \", commands: \"toggle .active\"\n * \"on every click toggle .active\" → prefix: \"on every click \", commands: \"toggle .active\"\n * \"on click.debounce(300) toggle .x\" → prefix: \"on click.debounce(300) \", commands: \"toggle .x\"\n * \"on keyup[key=='Enter'] set x to 1\" → prefix: \"on keyup[key=='Enter'] \", commands: \"set x to 1\"\n * \"on click from body toggle .active\" → prefix: \"on click from body \", commands: \"toggle .active\"\n */\nconst EVENT_PREFIX_RE =\n /^(on\\s+(?:every\\s+)?[\\w-]+(?:\\[.*?\\])?(?:\\.[\\w-]+(?:\\([^)]*\\))?)*(?:\\s+from\\s+\\S+)?(?:\\s+queue\\s+\\w+)?\\s+)/;\n\nfunction stripEventPrefix(src: string): { prefix: string; commands: string } | null {\n const match = src.match(EVENT_PREFIX_RE);\n if (!match) return null;\n const prefix = match[1];\n const commands = src.slice(prefix.length);\n if (!commands) return null;\n return { prefix, commands };\n}\n\n/**\n * Try semantic translation: parse in source language, render to English.\n * Returns null if confidence is below threshold.\n */\nfunction trySemanticTranslation(src: string, lang: string, threshold: number): string | null {\n try {\n // Handle compound statements (split on \"then\" boundaries and newlines)\n const statements = splitStatements(src, lang);\n if (statements.length > 1) {\n return translateCompound(statements, lang, threshold);\n }\n\n // Check if the semantic parser can handle this\n const sem = getAnalyzer();\n if (!sem.supportsLanguage(lang)) return null;\n\n const result = sem.analyze(src, lang);\n if (!shouldUseSemanticResult(result, threshold)) return null;\n\n // Render the semantic node to English\n if (result.node) {\n return render(result.node, 'en');\n }\n\n // Fallback: use translate() which combines parse+render\n return translate(src, lang, 'en');\n } catch {\n return null;\n }\n}\n\n/**\n * Try i18n grammar transformation to English.\n * Returns null if the result is identical to input (no translation happened).\n */\nfunction tryI18nTranslation(\n src: string,\n lang: string,\n toEnglish: (input: string, locale: string) => string\n): string | null {\n try {\n const result = toEnglish(src, lang);\n return result !== src ? result : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Get all \"then\" keyword forms for a language (primary + alternatives).\n * Falls back to English \"then\" if profile is unavailable.\n */\nfunction getThenKeywords(lang: string): string[] {\n const keywords = ['then']; // Always include English\n const profile = tryGetProfile(lang);\n if (profile?.keywords?.then) {\n const kw = profile.keywords.then;\n if (kw.primary && kw.primary !== 'then') keywords.push(kw.primary);\n if (kw.alternatives) {\n for (const alt of kw.alternatives) {\n if (!keywords.includes(alt)) keywords.push(alt);\n }\n }\n }\n return keywords;\n}\n\n/**\n * Split a hyperscript source string into individual statements.\n * Splits on language-specific \"then\" keywords and newlines.\n */\nfunction splitStatements(src: string, lang: string): string[] {\n const thenWords = getThenKeywords(lang);\n // Escape regex special chars and build pattern\n const escaped = thenWords.map(w => w.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'));\n const pattern = new RegExp(`\\\\s+(?:${escaped.join('|')})\\\\s+`, 'i');\n\n // Split on newlines first\n const lines = src\n .split('\\n')\n .map(l => l.trim())\n .filter(l => l.length > 0);\n const result: string[] = [];\n for (const line of lines) {\n const parts = line.split(pattern);\n result.push(...parts);\n }\n return result;\n}\n\n/**\n * Translate compound statements individually and rejoin with \" then \".\n */\nfunction translateCompound(statements: string[], lang: string, threshold: number): string | null {\n const translated: string[] = [];\n for (const stmt of statements) {\n const result = trySemanticTranslation(stmt.trim(), lang, threshold);\n if (result === null) return null; // If any part fails, fail the whole compound\n translated.push(result);\n }\n return translated.join(' then ');\n}\n","/**\n * _hyperscript Plugin\n *\n * Registers with _hyperscript.use() to intercept getScript() and\n * preprocess non-English hyperscript into English before parsing.\n */\n\nimport { resolveLanguage } from './language-resolver';\nimport { preprocessToEnglish, type PreprocessorConfig } from './preprocessor';\n\n/**\n * The _hyperscript global object shape (minimal subset we need).\n */\ninterface HyperscriptGlobal {\n internals: {\n runtime: HyperscriptRuntime;\n };\n config: Record<string, unknown>;\n}\n\ninterface HyperscriptRuntime {\n getScript(elt: Element): string | null;\n}\n\nexport interface PluginOptions extends Partial<PreprocessorConfig> {\n /** Default language for all elements (overridable per-element). */\n defaultLanguage?: string;\n /** Custom attribute name for per-element language. Default: \"data-lang\" */\n languageAttribute?: string;\n /** Enable debug logging to console. Default: false */\n debug?: boolean;\n}\n\n/**\n * Create a _hyperscript plugin that enables multilingual hyperscript.\n *\n * @example\n * // Basic usage\n * _hyperscript.use(hyperscriptI18n());\n *\n * @example\n * // With options\n * _hyperscript.use(hyperscriptI18n({\n * defaultLanguage: 'ja',\n * confidenceThreshold: 0.6,\n * debug: true,\n * }));\n */\nexport function hyperscriptI18n(options: PluginOptions = {}) {\n return function plugin(hs: unknown): void {\n const { internals } = hs as HyperscriptGlobal;\n const runtime = internals.runtime;\n const originalGetScript = runtime.getScript.bind(runtime);\n\n runtime.getScript = function (elt: Element): string | null {\n const src = originalGetScript(elt);\n if (!src) return null;\n\n // Resolve language\n const lang = resolveLanguageWithOptions(elt, options);\n\n // If English or no language detected, pass through\n if (!lang || lang === 'en') return src;\n\n // Preprocess to English\n const english = preprocessToEnglish(src, lang, options);\n\n if (english !== src) {\n if (options.debug) {\n console.log(`[hyperscript-i18n] ${lang}: \"${src}\" → \"${english}\"`);\n }\n } else {\n // Translation produced no change — likely a failure\n console.warn(\n `[hyperscript-i18n] Translation unchanged for lang=\"${lang}\": \"${src.length > 60 ? src.slice(0, 60) + '…' : src}\". ` +\n 'The input may not match any known pattern, or the language may not be registered. ' +\n 'Original text will be passed to _hyperscript as-is.'\n );\n }\n\n return english;\n };\n };\n}\n\n/**\n * Resolve language with plugin options factored in.\n */\nfunction resolveLanguageWithOptions(elt: Element, options: PluginOptions): string | null {\n // Check custom attribute name\n if (options.languageAttribute) {\n const custom = elt.getAttribute(options.languageAttribute);\n if (custom) return custom.split('-')[0].toLowerCase();\n }\n\n // Standard resolution\n const resolved = resolveLanguage(elt);\n if (resolved) return resolved;\n\n // Fall back to default language\n return options.defaultLanguage ?? null;\n}\n\n/**\n * Standalone preprocessing function for programmatic use.\n * Call this when using _hyperscript.evaluate() or _hyperscript(\"code\") directly.\n *\n * @example\n * const english = preprocess(\"トグル .active\", \"ja\");\n * _hyperscript(english);\n */\nexport function preprocess(\n src: string,\n lang: string,\n config: Partial<PreprocessorConfig> = {}\n): string {\n if (lang === 'en') return src;\n return preprocessToEnglish(src, lang, config);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/language-resolver.ts","../src/preprocessor.ts","../src/plugin.ts"],"sourcesContent":["/**\n * @lokascript/hyperscript-adapter\n *\n * Multilingual adapter plugin for the original _hyperscript.\n * Enables writing hyperscript in any of 24 supported languages.\n *\n * @example\n * import { hyperscriptI18n } from '@lokascript/hyperscript-adapter';\n * _hyperscript.use(hyperscriptI18n());\n *\n * @example\n * // With options\n * _hyperscript.use(hyperscriptI18n({\n * defaultLanguage: 'ja',\n * debug: true,\n * }));\n *\n * @example\n * // Standalone preprocessing (for _hyperscript.evaluate() calls)\n * import { preprocess } from '@lokascript/hyperscript-adapter';\n * const english = preprocess('トグル .active', 'ja');\n * _hyperscript(english);\n */\n\nexport { hyperscriptI18n, preprocess, type PluginOptions } from './plugin';\nexport { preprocessToEnglish, type PreprocessorConfig } from './preprocessor';\nexport { resolveLanguage } from './language-resolver';\n","/**\n * Language Resolver\n *\n * Determines the language of hyperscript code on a given element\n * using a cascading resolution strategy.\n */\n\n/**\n * Resolve the language for a hyperscript attribute on an element.\n *\n * Resolution order:\n * 1. `data-lang` attribute on the element itself\n * 2. `data-hyperscript-lang` attribute on the element or closest ancestor\n * 3. `lang` attribute on `<html>` element\n * 4. null (assume English, no preprocessing needed)\n */\nexport function resolveLanguage(elt: Element): string | null {\n // 1. Explicit per-element\n const dataLang = elt.getAttribute('data-lang');\n if (dataLang) return normalizeLangCode(dataLang);\n\n // 2. Inherited from ancestor\n const hsLang =\n elt.getAttribute('data-hyperscript-lang') ??\n elt.closest?.('[data-hyperscript-lang]')?.getAttribute('data-hyperscript-lang');\n if (hsLang) return normalizeLangCode(hsLang);\n\n // 3. Document-level lang\n const htmlLang = typeof document !== 'undefined' ? document.documentElement?.lang : null;\n if (htmlLang && htmlLang !== 'en') return normalizeLangCode(htmlLang);\n\n return null;\n}\n\n/**\n * Normalize a BCP-47 language tag to an ISO 639-1 code.\n * e.g., \"ja-JP\" → \"ja\", \"zh-Hans\" → \"zh\", \"pt-BR\" → \"pt\"\n */\nfunction normalizeLangCode(lang: string): string {\n return lang.split('-')[0].toLowerCase();\n}\n","/**\n * Preprocessor\n *\n * Translates non-English hyperscript to English using the semantic parser\n * (with optional i18n fallback), so the original _hyperscript can parse it.\n */\n\nimport {\n translate,\n render,\n parseSemantic,\n isLanguageRegistered,\n tryGetProfile,\n} from '@lokascript/semantic';\n\nexport interface PreprocessorConfig {\n /**\n * Minimum confidence threshold for semantic parsing (0-1). Default: 0.5.\n * Can be a single number (applies to all languages) or a per-language map.\n * Per-language thresholds are useful because SOV languages (ja, ko, tr) produce\n * inherently lower confidence scores than SVO languages (es, fr, de).\n *\n * @example\n * // Single threshold\n * { confidenceThreshold: 0.5 }\n *\n * @example\n * // Per-language thresholds\n * { confidenceThreshold: { es: 0.7, ja: 0.1, ko: 0.05, '*': 0.5 } }\n */\n confidenceThreshold: number | Record<string, number>;\n /** Strategy: 'semantic' (default), 'i18n', or 'auto' (semantic then i18n) */\n strategy: 'semantic' | 'i18n' | 'auto';\n /** Whether to return original text on failure. Default: true */\n fallbackToOriginal: boolean;\n /** Optional i18n toEnglish function (loaded dynamically if available) */\n i18nToEnglish?: (input: string, locale: string) => string;\n}\n\nconst DEFAULT_THRESHOLD = 0.5;\n\nconst DEFAULT_CONFIG: PreprocessorConfig = {\n confidenceThreshold: DEFAULT_THRESHOLD,\n strategy: 'semantic',\n fallbackToOriginal: true,\n};\n\n/**\n * Resolve the confidence threshold for a specific language.\n * Supports both a single number and a per-language map with '*' as default.\n */\nfunction resolveThreshold(threshold: number | Record<string, number>, lang: string): number {\n if (typeof threshold === 'number') return threshold;\n return threshold[lang] ?? threshold['*'] ?? DEFAULT_THRESHOLD;\n}\n\n/**\n * Preprocess non-English hyperscript into English.\n *\n * Uses the semantic parser to parse the input in the source language,\n * then renders back to English. Falls through to i18n if configured.\n *\n * Handles _hyperscript feature prefixes (e.g. \"on click\", \"on every keyup\")\n * by stripping them, translating only the command portion, then reassembling.\n */\nexport function preprocessToEnglish(\n src: string,\n lang: string,\n config: Partial<PreprocessorConfig> = {}\n): string {\n // English→English is identity; skip semantic parsing which may mangle the input\n if (lang === 'en') return src;\n\n const cfg = { ...DEFAULT_CONFIG, ...config };\n\n // Try translating the full string first\n const fullResult = tryTranslateWithStrategies(src, lang, cfg);\n if (fullResult !== null) return fullResult;\n\n // If full translation failed, try stripping event/feature prefix.\n // _hyperscript attributes often contain \"on <event> <commands>\" — the semantic\n // parser only understands command syntax, not event declarations.\n const stripped = stripEventPrefix(src);\n if (stripped) {\n const translated = tryTranslateWithStrategies(stripped.commands, lang, cfg);\n if (translated !== null) return stripped.prefix + translated;\n }\n\n // Fallback: return original\n return cfg.fallbackToOriginal ? src : src;\n}\n\n/**\n * Try all configured translation strategies on the input.\n * Returns null if none succeed.\n */\nfunction tryTranslateWithStrategies(\n src: string,\n lang: string,\n cfg: PreprocessorConfig\n): string | null {\n // Strategy: semantic-only\n if (cfg.strategy === 'semantic' || cfg.strategy === 'auto') {\n const threshold = resolveThreshold(cfg.confidenceThreshold, lang);\n const result = trySemanticTranslation(src, lang, threshold);\n if (result !== null) return result;\n }\n\n // Strategy: i18n fallback (auto mode or i18n-only)\n if ((cfg.strategy === 'auto' || cfg.strategy === 'i18n') && cfg.i18nToEnglish) {\n const result = tryI18nTranslation(src, lang, cfg.i18nToEnglish);\n if (result !== null) return result;\n }\n\n return null;\n}\n\n/**\n * Match _hyperscript event handler prefix: \"on [every] <event>[filter][.modifiers] \"\n *\n * Examples:\n * \"on click toggle .active\" → prefix: \"on click \", commands: \"toggle .active\"\n * \"on every click toggle .active\" → prefix: \"on every click \", commands: \"toggle .active\"\n * \"on click.debounce(300) toggle .x\" → prefix: \"on click.debounce(300) \", commands: \"toggle .x\"\n * \"on keyup[key=='Enter'] set x to 1\" → prefix: \"on keyup[key=='Enter'] \", commands: \"set x to 1\"\n * \"on click from body toggle .active\" → prefix: \"on click from body \", commands: \"toggle .active\"\n */\nconst EVENT_PREFIX_RE =\n /^(on\\s+(?:every\\s+)?[\\w-]+(?:\\[.*?\\])?(?:\\.[\\w-]+(?:\\([^)]*\\))?)*(?:\\s+from\\s+\\S+)?(?:\\s+queue\\s+\\w+)?\\s+)/;\n\nfunction stripEventPrefix(src: string): { prefix: string; commands: string } | null {\n const match = src.match(EVENT_PREFIX_RE);\n if (!match) return null;\n const prefix = match[1];\n const commands = src.slice(prefix.length);\n if (!commands) return null;\n return { prefix, commands };\n}\n\n/**\n * Try semantic translation: parse in source language, render to English.\n * Returns null if confidence is below threshold.\n */\nfunction trySemanticTranslation(src: string, lang: string, threshold: number): string | null {\n try {\n // Handle compound statements (split on \"then\" boundaries and newlines)\n const statements = splitStatements(src, lang);\n if (statements.length > 1) {\n return translateCompound(statements, lang, threshold);\n }\n\n // Check if the semantic parser can handle this\n if (!isLanguageRegistered(lang)) return null;\n\n const result = parseSemantic(src, lang);\n if (result.confidence < threshold || !result.node) {\n // If we got SOME confidence but no node, fall back to translate()\n if (result.confidence >= threshold) {\n return translate(src, lang, 'en');\n }\n return null;\n }\n\n // Render the semantic node to English\n return render(result.node, 'en');\n } catch {\n return null;\n }\n}\n\n/**\n * Try i18n grammar transformation to English.\n * Returns null if the result is identical to input (no translation happened).\n */\nfunction tryI18nTranslation(\n src: string,\n lang: string,\n toEnglish: (input: string, locale: string) => string\n): string | null {\n try {\n const result = toEnglish(src, lang);\n return result !== src ? result : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Get all \"then\" keyword forms for a language (primary + alternatives).\n * Falls back to English \"then\" if profile is unavailable.\n */\nfunction getThenKeywords(lang: string): string[] {\n const keywords = ['then']; // Always include English\n const profile = tryGetProfile(lang);\n if (profile?.keywords?.then) {\n const kw = profile.keywords.then;\n if (kw.primary && kw.primary !== 'then') keywords.push(kw.primary);\n if (kw.alternatives) {\n for (const alt of kw.alternatives) {\n if (!keywords.includes(alt)) keywords.push(alt);\n }\n }\n }\n return keywords;\n}\n\n/**\n * Split a hyperscript source string into individual statements.\n * Splits on language-specific \"then\" keywords and newlines.\n */\nfunction splitStatements(src: string, lang: string): string[] {\n const thenWords = getThenKeywords(lang);\n // Escape regex special chars and build pattern\n const escaped = thenWords.map(w => w.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'));\n const pattern = new RegExp(`\\\\s+(?:${escaped.join('|')})\\\\s+`, 'i');\n\n // Split on newlines first\n const lines = src\n .split('\\n')\n .map(l => l.trim())\n .filter(l => l.length > 0);\n const result: string[] = [];\n for (const line of lines) {\n const parts = line.split(pattern);\n result.push(...parts);\n }\n return result;\n}\n\n/**\n * Translate compound statements individually and rejoin with \" then \".\n */\nfunction translateCompound(statements: string[], lang: string, threshold: number): string | null {\n const translated: string[] = [];\n for (const stmt of statements) {\n const result = trySemanticTranslation(stmt.trim(), lang, threshold);\n if (result === null) return null; // If any part fails, fail the whole compound\n translated.push(result);\n }\n return translated.join(' then ');\n}\n","/**\n * _hyperscript Plugin\n *\n * Registers with _hyperscript.use() to intercept getScript() and\n * preprocess non-English hyperscript into English before parsing.\n */\n\nimport { resolveLanguage } from './language-resolver';\nimport { preprocessToEnglish, type PreprocessorConfig } from './preprocessor';\n\n/**\n * The _hyperscript global object shape (minimal subset we need).\n */\ninterface HyperscriptGlobal {\n internals: {\n runtime: HyperscriptRuntime;\n };\n config: Record<string, unknown>;\n}\n\ninterface HyperscriptRuntime {\n getScript(elt: Element): string | null;\n}\n\nexport interface PluginOptions extends Partial<PreprocessorConfig> {\n /** Default language for all elements (overridable per-element). */\n defaultLanguage?: string;\n /** Custom attribute name for per-element language. Default: \"data-lang\" */\n languageAttribute?: string;\n /** Enable debug logging to console. Default: false */\n debug?: boolean;\n}\n\n/**\n * Create a _hyperscript plugin that enables multilingual hyperscript.\n *\n * @example\n * // Basic usage\n * _hyperscript.use(hyperscriptI18n());\n *\n * @example\n * // With options\n * _hyperscript.use(hyperscriptI18n({\n * defaultLanguage: 'ja',\n * confidenceThreshold: 0.6,\n * debug: true,\n * }));\n */\nexport function hyperscriptI18n(options: PluginOptions = {}) {\n return function plugin(hs: unknown): void {\n const { internals } = hs as HyperscriptGlobal;\n const runtime = internals.runtime;\n const originalGetScript = runtime.getScript.bind(runtime);\n\n runtime.getScript = function (elt: Element): string | null {\n const src = originalGetScript(elt);\n if (!src) return null;\n\n // Resolve language\n const lang = resolveLanguageWithOptions(elt, options);\n\n // If English or no language detected, pass through\n if (!lang || lang === 'en') return src;\n\n // Preprocess to English\n const english = preprocessToEnglish(src, lang, options);\n\n if (english !== src) {\n if (options.debug) {\n console.log(`[hyperscript-i18n] ${lang}: \"${src}\" → \"${english}\"`);\n }\n } else {\n // Translation produced no change — likely a failure\n console.warn(\n `[hyperscript-i18n] Translation unchanged for lang=\"${lang}\": \"${src.length > 60 ? src.slice(0, 60) + '…' : src}\". ` +\n 'The input may not match any known pattern, or the language may not be registered. ' +\n 'Original text will be passed to _hyperscript as-is.'\n );\n }\n\n return english;\n };\n };\n}\n\n/**\n * Resolve language with plugin options factored in.\n */\nfunction resolveLanguageWithOptions(elt: Element, options: PluginOptions): string | null {\n // Check custom attribute name\n if (options.languageAttribute) {\n const custom = elt.getAttribute(options.languageAttribute);\n if (custom) return custom.split('-')[0].toLowerCase();\n }\n\n // Standard resolution\n const resolved = resolveLanguage(elt);\n if (resolved) return resolved;\n\n // Fall back to default language\n return options.defaultLanguage ?? null;\n}\n\n/**\n * Standalone preprocessing function for programmatic use.\n * Call this when using _hyperscript.evaluate() or _hyperscript(\"code\") directly.\n *\n * @example\n * const english = preprocess(\"トグル .active\", \"ja\");\n * _hyperscript(english);\n */\nexport function preprocess(\n src: string,\n lang: string,\n config: Partial<PreprocessorConfig> = {}\n): string {\n if (lang === 'en') return src;\n return preprocessToEnglish(src, lang, config);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACgBO,SAAS,gBAAgB,KAA6B;AAE3D,QAAM,WAAW,IAAI,aAAa,WAAW;AAC7C,MAAI,SAAU,QAAO,kBAAkB,QAAQ;AAG/C,QAAM,SACJ,IAAI,aAAa,uBAAuB,KACxC,IAAI,UAAU,yBAAyB,GAAG,aAAa,uBAAuB;AAChF,MAAI,OAAQ,QAAO,kBAAkB,MAAM;AAG3C,QAAM,WAAW,OAAO,aAAa,cAAc,SAAS,iBAAiB,OAAO;AACpF,MAAI,YAAY,aAAa,KAAM,QAAO,kBAAkB,QAAQ;AAEpE,SAAO;AACT;AAMA,SAAS,kBAAkB,MAAsB;AAC/C,SAAO,KAAK,MAAM,GAAG,EAAE,CAAC,EAAE,YAAY;AACxC;;;ACjCA,sBAMO;AA0BP,IAAM,oBAAoB;AAE1B,IAAM,iBAAqC;AAAA,EACzC,qBAAqB;AAAA,EACrB,UAAU;AAAA,EACV,oBAAoB;AACtB;AAMA,SAAS,iBAAiB,WAA4C,MAAsB;AAC1F,MAAI,OAAO,cAAc,SAAU,QAAO;AAC1C,SAAO,UAAU,IAAI,KAAK,UAAU,GAAG,KAAK;AAC9C;AAWO,SAAS,oBACd,KACA,MACA,SAAsC,CAAC,GAC/B;AAER,MAAI,SAAS,KAAM,QAAO;AAE1B,QAAM,MAAM,EAAE,GAAG,gBAAgB,GAAG,OAAO;AAG3C,QAAM,aAAa,2BAA2B,KAAK,MAAM,GAAG;AAC5D,MAAI,eAAe,KAAM,QAAO;AAKhC,QAAM,WAAW,iBAAiB,GAAG;AACrC,MAAI,UAAU;AACZ,UAAM,aAAa,2BAA2B,SAAS,UAAU,MAAM,GAAG;AAC1E,QAAI,eAAe,KAAM,QAAO,SAAS,SAAS;AAAA,EACpD;AAGA,SAAO,IAAI,qBAAqB,MAAM;AACxC;AAMA,SAAS,2BACP,KACA,MACA,KACe;AAEf,MAAI,IAAI,aAAa,cAAc,IAAI,aAAa,QAAQ;AAC1D,UAAM,YAAY,iBAAiB,IAAI,qBAAqB,IAAI;AAChE,UAAM,SAAS,uBAAuB,KAAK,MAAM,SAAS;AAC1D,QAAI,WAAW,KAAM,QAAO;AAAA,EAC9B;AAGA,OAAK,IAAI,aAAa,UAAU,IAAI,aAAa,WAAW,IAAI,eAAe;AAC7E,UAAM,SAAS,mBAAmB,KAAK,MAAM,IAAI,aAAa;AAC9D,QAAI,WAAW,KAAM,QAAO;AAAA,EAC9B;AAEA,SAAO;AACT;AAYA,IAAM,kBACJ;AAEF,SAAS,iBAAiB,KAA0D;AAClF,QAAM,QAAQ,IAAI,MAAM,eAAe;AACvC,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,SAAS,MAAM,CAAC;AACtB,QAAM,WAAW,IAAI,MAAM,OAAO,MAAM;AACxC,MAAI,CAAC,SAAU,QAAO;AACtB,SAAO,EAAE,QAAQ,SAAS;AAC5B;AAMA,SAAS,uBAAuB,KAAa,MAAc,WAAkC;AAC3F,MAAI;AAEF,UAAM,aAAa,gBAAgB,KAAK,IAAI;AAC5C,QAAI,WAAW,SAAS,GAAG;AACzB,aAAO,kBAAkB,YAAY,MAAM,SAAS;AAAA,IACtD;AAGA,QAAI,KAAC,sCAAqB,IAAI,EAAG,QAAO;AAExC,UAAM,aAAS,+BAAc,KAAK,IAAI;AACtC,QAAI,OAAO,aAAa,aAAa,CAAC,OAAO,MAAM;AAEjD,UAAI,OAAO,cAAc,WAAW;AAClC,mBAAO,2BAAU,KAAK,MAAM,IAAI;AAAA,MAClC;AACA,aAAO;AAAA,IACT;AAGA,eAAO,wBAAO,OAAO,MAAM,IAAI;AAAA,EACjC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMA,SAAS,mBACP,KACA,MACA,WACe;AACf,MAAI;AACF,UAAM,SAAS,UAAU,KAAK,IAAI;AAClC,WAAO,WAAW,MAAM,SAAS;AAAA,EACnC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMA,SAAS,gBAAgB,MAAwB;AAC/C,QAAM,WAAW,CAAC,MAAM;AACxB,QAAM,cAAU,+BAAc,IAAI;AAClC,MAAI,SAAS,UAAU,MAAM;AAC3B,UAAM,KAAK,QAAQ,SAAS;AAC5B,QAAI,GAAG,WAAW,GAAG,YAAY,OAAQ,UAAS,KAAK,GAAG,OAAO;AACjE,QAAI,GAAG,cAAc;AACnB,iBAAW,OAAO,GAAG,cAAc;AACjC,YAAI,CAAC,SAAS,SAAS,GAAG,EAAG,UAAS,KAAK,GAAG;AAAA,MAChD;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAMA,SAAS,gBAAgB,KAAa,MAAwB;AAC5D,QAAM,YAAY,gBAAgB,IAAI;AAEtC,QAAM,UAAU,UAAU,IAAI,OAAK,EAAE,QAAQ,uBAAuB,MAAM,CAAC;AAC3E,QAAM,UAAU,IAAI,OAAO,UAAU,QAAQ,KAAK,GAAG,CAAC,SAAS,GAAG;AAGlE,QAAM,QAAQ,IACX,MAAM,IAAI,EACV,IAAI,OAAK,EAAE,KAAK,CAAC,EACjB,OAAO,OAAK,EAAE,SAAS,CAAC;AAC3B,QAAM,SAAmB,CAAC;AAC1B,aAAW,QAAQ,OAAO;AACxB,UAAM,QAAQ,KAAK,MAAM,OAAO;AAChC,WAAO,KAAK,GAAG,KAAK;AAAA,EACtB;AACA,SAAO;AACT;AAKA,SAAS,kBAAkB,YAAsB,MAAc,WAAkC;AAC/F,QAAM,aAAuB,CAAC;AAC9B,aAAW,QAAQ,YAAY;AAC7B,UAAM,SAAS,uBAAuB,KAAK,KAAK,GAAG,MAAM,SAAS;AAClE,QAAI,WAAW,KAAM,QAAO;AAC5B,eAAW,KAAK,MAAM;AAAA,EACxB;AACA,SAAO,WAAW,KAAK,QAAQ;AACjC;;;AChMO,SAAS,gBAAgB,UAAyB,CAAC,GAAG;AAC3D,SAAO,SAAS,OAAO,IAAmB;AACxC,UAAM,EAAE,UAAU,IAAI;AACtB,UAAM,UAAU,UAAU;AAC1B,UAAM,oBAAoB,QAAQ,UAAU,KAAK,OAAO;AAExD,YAAQ,YAAY,SAAU,KAA6B;AACzD,YAAM,MAAM,kBAAkB,GAAG;AACjC,UAAI,CAAC,IAAK,QAAO;AAGjB,YAAM,OAAO,2BAA2B,KAAK,OAAO;AAGpD,UAAI,CAAC,QAAQ,SAAS,KAAM,QAAO;AAGnC,YAAM,UAAU,oBAAoB,KAAK,MAAM,OAAO;AAEtD,UAAI,YAAY,KAAK;AACnB,YAAI,QAAQ,OAAO;AACjB,kBAAQ,IAAI,sBAAsB,IAAI,MAAM,GAAG,aAAQ,OAAO,GAAG;AAAA,QACnE;AAAA,MACF,OAAO;AAEL,gBAAQ;AAAA,UACN,sDAAsD,IAAI,OAAO,IAAI,SAAS,KAAK,IAAI,MAAM,GAAG,EAAE,IAAI,WAAM,GAAG;AAAA,QAGjH;AAAA,MACF;AAEA,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAKA,SAAS,2BAA2B,KAAc,SAAuC;AAEvF,MAAI,QAAQ,mBAAmB;AAC7B,UAAM,SAAS,IAAI,aAAa,QAAQ,iBAAiB;AACzD,QAAI,OAAQ,QAAO,OAAO,MAAM,GAAG,EAAE,CAAC,EAAE,YAAY;AAAA,EACtD;AAGA,QAAM,WAAW,gBAAgB,GAAG;AACpC,MAAI,SAAU,QAAO;AAGrB,SAAO,QAAQ,mBAAmB;AACpC;AAUO,SAAS,WACd,KACA,MACA,SAAsC,CAAC,GAC/B;AACR,MAAI,SAAS,KAAM,QAAO;AAC1B,SAAO,oBAAoB,KAAK,MAAM,MAAM;AAC9C;","names":[]}
package/dist/index.js CHANGED
@@ -16,8 +16,8 @@ function normalizeLangCode(lang) {
16
16
  import {
17
17
  translate,
18
18
  render,
19
- createSemanticAnalyzer,
20
- shouldUseSemanticResult,
19
+ parseSemantic,
20
+ isLanguageRegistered,
21
21
  tryGetProfile
22
22
  } from "@lokascript/semantic";
23
23
  var DEFAULT_THRESHOLD = 0.5;
@@ -30,13 +30,6 @@ function resolveThreshold(threshold, lang) {
30
30
  if (typeof threshold === "number") return threshold;
31
31
  return threshold[lang] ?? threshold["*"] ?? DEFAULT_THRESHOLD;
32
32
  }
33
- var analyzer = null;
34
- function getAnalyzer() {
35
- if (!analyzer) {
36
- analyzer = createSemanticAnalyzer();
37
- }
38
- return analyzer;
39
- }
40
33
  function preprocessToEnglish(src, lang, config = {}) {
41
34
  if (lang === "en") return src;
42
35
  const cfg = { ...DEFAULT_CONFIG, ...config };
@@ -76,14 +69,15 @@ function trySemanticTranslation(src, lang, threshold) {
76
69
  if (statements.length > 1) {
77
70
  return translateCompound(statements, lang, threshold);
78
71
  }
79
- const sem = getAnalyzer();
80
- if (!sem.supportsLanguage(lang)) return null;
81
- const result = sem.analyze(src, lang);
82
- if (!shouldUseSemanticResult(result, threshold)) return null;
83
- if (result.node) {
84
- return render(result.node, "en");
72
+ if (!isLanguageRegistered(lang)) return null;
73
+ const result = parseSemantic(src, lang);
74
+ if (result.confidence < threshold || !result.node) {
75
+ if (result.confidence >= threshold) {
76
+ return translate(src, lang, "en");
77
+ }
78
+ return null;
85
79
  }
86
- return translate(src, lang, "en");
80
+ return render(result.node, "en");
87
81
  } catch {
88
82
  return null;
89
83
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/language-resolver.ts","../src/preprocessor.ts","../src/plugin.ts"],"sourcesContent":["/**\n * Language Resolver\n *\n * Determines the language of hyperscript code on a given element\n * using a cascading resolution strategy.\n */\n\n/**\n * Resolve the language for a hyperscript attribute on an element.\n *\n * Resolution order:\n * 1. `data-lang` attribute on the element itself\n * 2. `data-hyperscript-lang` attribute on the element or closest ancestor\n * 3. `lang` attribute on `<html>` element\n * 4. null (assume English, no preprocessing needed)\n */\nexport function resolveLanguage(elt: Element): string | null {\n // 1. Explicit per-element\n const dataLang = elt.getAttribute('data-lang');\n if (dataLang) return normalizeLangCode(dataLang);\n\n // 2. Inherited from ancestor\n const hsLang =\n elt.getAttribute('data-hyperscript-lang') ??\n elt.closest?.('[data-hyperscript-lang]')?.getAttribute('data-hyperscript-lang');\n if (hsLang) return normalizeLangCode(hsLang);\n\n // 3. Document-level lang\n const htmlLang = typeof document !== 'undefined' ? document.documentElement?.lang : null;\n if (htmlLang && htmlLang !== 'en') return normalizeLangCode(htmlLang);\n\n return null;\n}\n\n/**\n * Normalize a BCP-47 language tag to an ISO 639-1 code.\n * e.g., \"ja-JP\" → \"ja\", \"zh-Hans\" → \"zh\", \"pt-BR\" → \"pt\"\n */\nfunction normalizeLangCode(lang: string): string {\n return lang.split('-')[0].toLowerCase();\n}\n","/**\n * Preprocessor\n *\n * Translates non-English hyperscript to English using the semantic parser\n * (with optional i18n fallback), so the original _hyperscript can parse it.\n */\n\nimport {\n translate,\n render,\n createSemanticAnalyzer,\n shouldUseSemanticResult,\n tryGetProfile,\n type SemanticAnalyzer,\n} from '@lokascript/semantic';\n\nexport interface PreprocessorConfig {\n /**\n * Minimum confidence threshold for semantic parsing (0-1). Default: 0.5.\n * Can be a single number (applies to all languages) or a per-language map.\n * Per-language thresholds are useful because SOV languages (ja, ko, tr) produce\n * inherently lower confidence scores than SVO languages (es, fr, de).\n *\n * @example\n * // Single threshold\n * { confidenceThreshold: 0.5 }\n *\n * @example\n * // Per-language thresholds\n * { confidenceThreshold: { es: 0.7, ja: 0.1, ko: 0.05, '*': 0.5 } }\n */\n confidenceThreshold: number | Record<string, number>;\n /** Strategy: 'semantic' (default), 'i18n', or 'auto' (semantic then i18n) */\n strategy: 'semantic' | 'i18n' | 'auto';\n /** Whether to return original text on failure. Default: true */\n fallbackToOriginal: boolean;\n /** Optional i18n toEnglish function (loaded dynamically if available) */\n i18nToEnglish?: (input: string, locale: string) => string;\n}\n\nconst DEFAULT_THRESHOLD = 0.5;\n\nconst DEFAULT_CONFIG: PreprocessorConfig = {\n confidenceThreshold: DEFAULT_THRESHOLD,\n strategy: 'semantic',\n fallbackToOriginal: true,\n};\n\n/**\n * Resolve the confidence threshold for a specific language.\n * Supports both a single number and a per-language map with '*' as default.\n */\nfunction resolveThreshold(threshold: number | Record<string, number>, lang: string): number {\n if (typeof threshold === 'number') return threshold;\n return threshold[lang] ?? threshold['*'] ?? DEFAULT_THRESHOLD;\n}\n\nlet analyzer: SemanticAnalyzer | null = null;\n\nfunction getAnalyzer(): SemanticAnalyzer {\n if (!analyzer) {\n analyzer = createSemanticAnalyzer();\n }\n return analyzer;\n}\n\n/**\n * Preprocess non-English hyperscript into English.\n *\n * Uses the semantic parser to parse the input in the source language,\n * then renders back to English. Falls through to i18n if configured.\n *\n * Handles _hyperscript feature prefixes (e.g. \"on click\", \"on every keyup\")\n * by stripping them, translating only the command portion, then reassembling.\n */\nexport function preprocessToEnglish(\n src: string,\n lang: string,\n config: Partial<PreprocessorConfig> = {}\n): string {\n // English→English is identity; skip semantic parsing which may mangle the input\n if (lang === 'en') return src;\n\n const cfg = { ...DEFAULT_CONFIG, ...config };\n\n // Try translating the full string first\n const fullResult = tryTranslateWithStrategies(src, lang, cfg);\n if (fullResult !== null) return fullResult;\n\n // If full translation failed, try stripping event/feature prefix.\n // _hyperscript attributes often contain \"on <event> <commands>\" — the semantic\n // parser only understands command syntax, not event declarations.\n const stripped = stripEventPrefix(src);\n if (stripped) {\n const translated = tryTranslateWithStrategies(stripped.commands, lang, cfg);\n if (translated !== null) return stripped.prefix + translated;\n }\n\n // Fallback: return original\n return cfg.fallbackToOriginal ? src : src;\n}\n\n/**\n * Try all configured translation strategies on the input.\n * Returns null if none succeed.\n */\nfunction tryTranslateWithStrategies(\n src: string,\n lang: string,\n cfg: PreprocessorConfig\n): string | null {\n // Strategy: semantic-only\n if (cfg.strategy === 'semantic' || cfg.strategy === 'auto') {\n const threshold = resolveThreshold(cfg.confidenceThreshold, lang);\n const result = trySemanticTranslation(src, lang, threshold);\n if (result !== null) return result;\n }\n\n // Strategy: i18n fallback (auto mode or i18n-only)\n if ((cfg.strategy === 'auto' || cfg.strategy === 'i18n') && cfg.i18nToEnglish) {\n const result = tryI18nTranslation(src, lang, cfg.i18nToEnglish);\n if (result !== null) return result;\n }\n\n return null;\n}\n\n/**\n * Match _hyperscript event handler prefix: \"on [every] <event>[filter][.modifiers] \"\n *\n * Examples:\n * \"on click toggle .active\" → prefix: \"on click \", commands: \"toggle .active\"\n * \"on every click toggle .active\" → prefix: \"on every click \", commands: \"toggle .active\"\n * \"on click.debounce(300) toggle .x\" → prefix: \"on click.debounce(300) \", commands: \"toggle .x\"\n * \"on keyup[key=='Enter'] set x to 1\" → prefix: \"on keyup[key=='Enter'] \", commands: \"set x to 1\"\n * \"on click from body toggle .active\" → prefix: \"on click from body \", commands: \"toggle .active\"\n */\nconst EVENT_PREFIX_RE =\n /^(on\\s+(?:every\\s+)?[\\w-]+(?:\\[.*?\\])?(?:\\.[\\w-]+(?:\\([^)]*\\))?)*(?:\\s+from\\s+\\S+)?(?:\\s+queue\\s+\\w+)?\\s+)/;\n\nfunction stripEventPrefix(src: string): { prefix: string; commands: string } | null {\n const match = src.match(EVENT_PREFIX_RE);\n if (!match) return null;\n const prefix = match[1];\n const commands = src.slice(prefix.length);\n if (!commands) return null;\n return { prefix, commands };\n}\n\n/**\n * Try semantic translation: parse in source language, render to English.\n * Returns null if confidence is below threshold.\n */\nfunction trySemanticTranslation(src: string, lang: string, threshold: number): string | null {\n try {\n // Handle compound statements (split on \"then\" boundaries and newlines)\n const statements = splitStatements(src, lang);\n if (statements.length > 1) {\n return translateCompound(statements, lang, threshold);\n }\n\n // Check if the semantic parser can handle this\n const sem = getAnalyzer();\n if (!sem.supportsLanguage(lang)) return null;\n\n const result = sem.analyze(src, lang);\n if (!shouldUseSemanticResult(result, threshold)) return null;\n\n // Render the semantic node to English\n if (result.node) {\n return render(result.node, 'en');\n }\n\n // Fallback: use translate() which combines parse+render\n return translate(src, lang, 'en');\n } catch {\n return null;\n }\n}\n\n/**\n * Try i18n grammar transformation to English.\n * Returns null if the result is identical to input (no translation happened).\n */\nfunction tryI18nTranslation(\n src: string,\n lang: string,\n toEnglish: (input: string, locale: string) => string\n): string | null {\n try {\n const result = toEnglish(src, lang);\n return result !== src ? result : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Get all \"then\" keyword forms for a language (primary + alternatives).\n * Falls back to English \"then\" if profile is unavailable.\n */\nfunction getThenKeywords(lang: string): string[] {\n const keywords = ['then']; // Always include English\n const profile = tryGetProfile(lang);\n if (profile?.keywords?.then) {\n const kw = profile.keywords.then;\n if (kw.primary && kw.primary !== 'then') keywords.push(kw.primary);\n if (kw.alternatives) {\n for (const alt of kw.alternatives) {\n if (!keywords.includes(alt)) keywords.push(alt);\n }\n }\n }\n return keywords;\n}\n\n/**\n * Split a hyperscript source string into individual statements.\n * Splits on language-specific \"then\" keywords and newlines.\n */\nfunction splitStatements(src: string, lang: string): string[] {\n const thenWords = getThenKeywords(lang);\n // Escape regex special chars and build pattern\n const escaped = thenWords.map(w => w.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'));\n const pattern = new RegExp(`\\\\s+(?:${escaped.join('|')})\\\\s+`, 'i');\n\n // Split on newlines first\n const lines = src\n .split('\\n')\n .map(l => l.trim())\n .filter(l => l.length > 0);\n const result: string[] = [];\n for (const line of lines) {\n const parts = line.split(pattern);\n result.push(...parts);\n }\n return result;\n}\n\n/**\n * Translate compound statements individually and rejoin with \" then \".\n */\nfunction translateCompound(statements: string[], lang: string, threshold: number): string | null {\n const translated: string[] = [];\n for (const stmt of statements) {\n const result = trySemanticTranslation(stmt.trim(), lang, threshold);\n if (result === null) return null; // If any part fails, fail the whole compound\n translated.push(result);\n }\n return translated.join(' then ');\n}\n","/**\n * _hyperscript Plugin\n *\n * Registers with _hyperscript.use() to intercept getScript() and\n * preprocess non-English hyperscript into English before parsing.\n */\n\nimport { resolveLanguage } from './language-resolver';\nimport { preprocessToEnglish, type PreprocessorConfig } from './preprocessor';\n\n/**\n * The _hyperscript global object shape (minimal subset we need).\n */\ninterface HyperscriptGlobal {\n internals: {\n runtime: HyperscriptRuntime;\n };\n config: Record<string, unknown>;\n}\n\ninterface HyperscriptRuntime {\n getScript(elt: Element): string | null;\n}\n\nexport interface PluginOptions extends Partial<PreprocessorConfig> {\n /** Default language for all elements (overridable per-element). */\n defaultLanguage?: string;\n /** Custom attribute name for per-element language. Default: \"data-lang\" */\n languageAttribute?: string;\n /** Enable debug logging to console. Default: false */\n debug?: boolean;\n}\n\n/**\n * Create a _hyperscript plugin that enables multilingual hyperscript.\n *\n * @example\n * // Basic usage\n * _hyperscript.use(hyperscriptI18n());\n *\n * @example\n * // With options\n * _hyperscript.use(hyperscriptI18n({\n * defaultLanguage: 'ja',\n * confidenceThreshold: 0.6,\n * debug: true,\n * }));\n */\nexport function hyperscriptI18n(options: PluginOptions = {}) {\n return function plugin(hs: unknown): void {\n const { internals } = hs as HyperscriptGlobal;\n const runtime = internals.runtime;\n const originalGetScript = runtime.getScript.bind(runtime);\n\n runtime.getScript = function (elt: Element): string | null {\n const src = originalGetScript(elt);\n if (!src) return null;\n\n // Resolve language\n const lang = resolveLanguageWithOptions(elt, options);\n\n // If English or no language detected, pass through\n if (!lang || lang === 'en') return src;\n\n // Preprocess to English\n const english = preprocessToEnglish(src, lang, options);\n\n if (english !== src) {\n if (options.debug) {\n console.log(`[hyperscript-i18n] ${lang}: \"${src}\" → \"${english}\"`);\n }\n } else {\n // Translation produced no change — likely a failure\n console.warn(\n `[hyperscript-i18n] Translation unchanged for lang=\"${lang}\": \"${src.length > 60 ? src.slice(0, 60) + '…' : src}\". ` +\n 'The input may not match any known pattern, or the language may not be registered. ' +\n 'Original text will be passed to _hyperscript as-is.'\n );\n }\n\n return english;\n };\n };\n}\n\n/**\n * Resolve language with plugin options factored in.\n */\nfunction resolveLanguageWithOptions(elt: Element, options: PluginOptions): string | null {\n // Check custom attribute name\n if (options.languageAttribute) {\n const custom = elt.getAttribute(options.languageAttribute);\n if (custom) return custom.split('-')[0].toLowerCase();\n }\n\n // Standard resolution\n const resolved = resolveLanguage(elt);\n if (resolved) return resolved;\n\n // Fall back to default language\n return options.defaultLanguage ?? null;\n}\n\n/**\n * Standalone preprocessing function for programmatic use.\n * Call this when using _hyperscript.evaluate() or _hyperscript(\"code\") directly.\n *\n * @example\n * const english = preprocess(\"トグル .active\", \"ja\");\n * _hyperscript(english);\n */\nexport function preprocess(\n src: string,\n lang: string,\n config: Partial<PreprocessorConfig> = {}\n): string {\n if (lang === 'en') return src;\n return preprocessToEnglish(src, lang, config);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/language-resolver.ts","../src/preprocessor.ts","../src/plugin.ts"],"sourcesContent":["/**\n * Language Resolver\n *\n * Determines the language of hyperscript code on a given element\n * using a cascading resolution strategy.\n */\n\n/**\n * Resolve the language for a hyperscript attribute on an element.\n *\n * Resolution order:\n * 1. `data-lang` attribute on the element itself\n * 2. `data-hyperscript-lang` attribute on the element or closest ancestor\n * 3. `lang` attribute on `<html>` element\n * 4. null (assume English, no preprocessing needed)\n */\nexport function resolveLanguage(elt: Element): string | null {\n // 1. Explicit per-element\n const dataLang = elt.getAttribute('data-lang');\n if (dataLang) return normalizeLangCode(dataLang);\n\n // 2. Inherited from ancestor\n const hsLang =\n elt.getAttribute('data-hyperscript-lang') ??\n elt.closest?.('[data-hyperscript-lang]')?.getAttribute('data-hyperscript-lang');\n if (hsLang) return normalizeLangCode(hsLang);\n\n // 3. Document-level lang\n const htmlLang = typeof document !== 'undefined' ? document.documentElement?.lang : null;\n if (htmlLang && htmlLang !== 'en') return normalizeLangCode(htmlLang);\n\n return null;\n}\n\n/**\n * Normalize a BCP-47 language tag to an ISO 639-1 code.\n * e.g., \"ja-JP\" → \"ja\", \"zh-Hans\" → \"zh\", \"pt-BR\" → \"pt\"\n */\nfunction normalizeLangCode(lang: string): string {\n return lang.split('-')[0].toLowerCase();\n}\n","/**\n * Preprocessor\n *\n * Translates non-English hyperscript to English using the semantic parser\n * (with optional i18n fallback), so the original _hyperscript can parse it.\n */\n\nimport {\n translate,\n render,\n parseSemantic,\n isLanguageRegistered,\n tryGetProfile,\n} from '@lokascript/semantic';\n\nexport interface PreprocessorConfig {\n /**\n * Minimum confidence threshold for semantic parsing (0-1). Default: 0.5.\n * Can be a single number (applies to all languages) or a per-language map.\n * Per-language thresholds are useful because SOV languages (ja, ko, tr) produce\n * inherently lower confidence scores than SVO languages (es, fr, de).\n *\n * @example\n * // Single threshold\n * { confidenceThreshold: 0.5 }\n *\n * @example\n * // Per-language thresholds\n * { confidenceThreshold: { es: 0.7, ja: 0.1, ko: 0.05, '*': 0.5 } }\n */\n confidenceThreshold: number | Record<string, number>;\n /** Strategy: 'semantic' (default), 'i18n', or 'auto' (semantic then i18n) */\n strategy: 'semantic' | 'i18n' | 'auto';\n /** Whether to return original text on failure. Default: true */\n fallbackToOriginal: boolean;\n /** Optional i18n toEnglish function (loaded dynamically if available) */\n i18nToEnglish?: (input: string, locale: string) => string;\n}\n\nconst DEFAULT_THRESHOLD = 0.5;\n\nconst DEFAULT_CONFIG: PreprocessorConfig = {\n confidenceThreshold: DEFAULT_THRESHOLD,\n strategy: 'semantic',\n fallbackToOriginal: true,\n};\n\n/**\n * Resolve the confidence threshold for a specific language.\n * Supports both a single number and a per-language map with '*' as default.\n */\nfunction resolveThreshold(threshold: number | Record<string, number>, lang: string): number {\n if (typeof threshold === 'number') return threshold;\n return threshold[lang] ?? threshold['*'] ?? DEFAULT_THRESHOLD;\n}\n\n/**\n * Preprocess non-English hyperscript into English.\n *\n * Uses the semantic parser to parse the input in the source language,\n * then renders back to English. Falls through to i18n if configured.\n *\n * Handles _hyperscript feature prefixes (e.g. \"on click\", \"on every keyup\")\n * by stripping them, translating only the command portion, then reassembling.\n */\nexport function preprocessToEnglish(\n src: string,\n lang: string,\n config: Partial<PreprocessorConfig> = {}\n): string {\n // English→English is identity; skip semantic parsing which may mangle the input\n if (lang === 'en') return src;\n\n const cfg = { ...DEFAULT_CONFIG, ...config };\n\n // Try translating the full string first\n const fullResult = tryTranslateWithStrategies(src, lang, cfg);\n if (fullResult !== null) return fullResult;\n\n // If full translation failed, try stripping event/feature prefix.\n // _hyperscript attributes often contain \"on <event> <commands>\" — the semantic\n // parser only understands command syntax, not event declarations.\n const stripped = stripEventPrefix(src);\n if (stripped) {\n const translated = tryTranslateWithStrategies(stripped.commands, lang, cfg);\n if (translated !== null) return stripped.prefix + translated;\n }\n\n // Fallback: return original\n return cfg.fallbackToOriginal ? src : src;\n}\n\n/**\n * Try all configured translation strategies on the input.\n * Returns null if none succeed.\n */\nfunction tryTranslateWithStrategies(\n src: string,\n lang: string,\n cfg: PreprocessorConfig\n): string | null {\n // Strategy: semantic-only\n if (cfg.strategy === 'semantic' || cfg.strategy === 'auto') {\n const threshold = resolveThreshold(cfg.confidenceThreshold, lang);\n const result = trySemanticTranslation(src, lang, threshold);\n if (result !== null) return result;\n }\n\n // Strategy: i18n fallback (auto mode or i18n-only)\n if ((cfg.strategy === 'auto' || cfg.strategy === 'i18n') && cfg.i18nToEnglish) {\n const result = tryI18nTranslation(src, lang, cfg.i18nToEnglish);\n if (result !== null) return result;\n }\n\n return null;\n}\n\n/**\n * Match _hyperscript event handler prefix: \"on [every] <event>[filter][.modifiers] \"\n *\n * Examples:\n * \"on click toggle .active\" → prefix: \"on click \", commands: \"toggle .active\"\n * \"on every click toggle .active\" → prefix: \"on every click \", commands: \"toggle .active\"\n * \"on click.debounce(300) toggle .x\" → prefix: \"on click.debounce(300) \", commands: \"toggle .x\"\n * \"on keyup[key=='Enter'] set x to 1\" → prefix: \"on keyup[key=='Enter'] \", commands: \"set x to 1\"\n * \"on click from body toggle .active\" → prefix: \"on click from body \", commands: \"toggle .active\"\n */\nconst EVENT_PREFIX_RE =\n /^(on\\s+(?:every\\s+)?[\\w-]+(?:\\[.*?\\])?(?:\\.[\\w-]+(?:\\([^)]*\\))?)*(?:\\s+from\\s+\\S+)?(?:\\s+queue\\s+\\w+)?\\s+)/;\n\nfunction stripEventPrefix(src: string): { prefix: string; commands: string } | null {\n const match = src.match(EVENT_PREFIX_RE);\n if (!match) return null;\n const prefix = match[1];\n const commands = src.slice(prefix.length);\n if (!commands) return null;\n return { prefix, commands };\n}\n\n/**\n * Try semantic translation: parse in source language, render to English.\n * Returns null if confidence is below threshold.\n */\nfunction trySemanticTranslation(src: string, lang: string, threshold: number): string | null {\n try {\n // Handle compound statements (split on \"then\" boundaries and newlines)\n const statements = splitStatements(src, lang);\n if (statements.length > 1) {\n return translateCompound(statements, lang, threshold);\n }\n\n // Check if the semantic parser can handle this\n if (!isLanguageRegistered(lang)) return null;\n\n const result = parseSemantic(src, lang);\n if (result.confidence < threshold || !result.node) {\n // If we got SOME confidence but no node, fall back to translate()\n if (result.confidence >= threshold) {\n return translate(src, lang, 'en');\n }\n return null;\n }\n\n // Render the semantic node to English\n return render(result.node, 'en');\n } catch {\n return null;\n }\n}\n\n/**\n * Try i18n grammar transformation to English.\n * Returns null if the result is identical to input (no translation happened).\n */\nfunction tryI18nTranslation(\n src: string,\n lang: string,\n toEnglish: (input: string, locale: string) => string\n): string | null {\n try {\n const result = toEnglish(src, lang);\n return result !== src ? result : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Get all \"then\" keyword forms for a language (primary + alternatives).\n * Falls back to English \"then\" if profile is unavailable.\n */\nfunction getThenKeywords(lang: string): string[] {\n const keywords = ['then']; // Always include English\n const profile = tryGetProfile(lang);\n if (profile?.keywords?.then) {\n const kw = profile.keywords.then;\n if (kw.primary && kw.primary !== 'then') keywords.push(kw.primary);\n if (kw.alternatives) {\n for (const alt of kw.alternatives) {\n if (!keywords.includes(alt)) keywords.push(alt);\n }\n }\n }\n return keywords;\n}\n\n/**\n * Split a hyperscript source string into individual statements.\n * Splits on language-specific \"then\" keywords and newlines.\n */\nfunction splitStatements(src: string, lang: string): string[] {\n const thenWords = getThenKeywords(lang);\n // Escape regex special chars and build pattern\n const escaped = thenWords.map(w => w.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'));\n const pattern = new RegExp(`\\\\s+(?:${escaped.join('|')})\\\\s+`, 'i');\n\n // Split on newlines first\n const lines = src\n .split('\\n')\n .map(l => l.trim())\n .filter(l => l.length > 0);\n const result: string[] = [];\n for (const line of lines) {\n const parts = line.split(pattern);\n result.push(...parts);\n }\n return result;\n}\n\n/**\n * Translate compound statements individually and rejoin with \" then \".\n */\nfunction translateCompound(statements: string[], lang: string, threshold: number): string | null {\n const translated: string[] = [];\n for (const stmt of statements) {\n const result = trySemanticTranslation(stmt.trim(), lang, threshold);\n if (result === null) return null; // If any part fails, fail the whole compound\n translated.push(result);\n }\n return translated.join(' then ');\n}\n","/**\n * _hyperscript Plugin\n *\n * Registers with _hyperscript.use() to intercept getScript() and\n * preprocess non-English hyperscript into English before parsing.\n */\n\nimport { resolveLanguage } from './language-resolver';\nimport { preprocessToEnglish, type PreprocessorConfig } from './preprocessor';\n\n/**\n * The _hyperscript global object shape (minimal subset we need).\n */\ninterface HyperscriptGlobal {\n internals: {\n runtime: HyperscriptRuntime;\n };\n config: Record<string, unknown>;\n}\n\ninterface HyperscriptRuntime {\n getScript(elt: Element): string | null;\n}\n\nexport interface PluginOptions extends Partial<PreprocessorConfig> {\n /** Default language for all elements (overridable per-element). */\n defaultLanguage?: string;\n /** Custom attribute name for per-element language. Default: \"data-lang\" */\n languageAttribute?: string;\n /** Enable debug logging to console. Default: false */\n debug?: boolean;\n}\n\n/**\n * Create a _hyperscript plugin that enables multilingual hyperscript.\n *\n * @example\n * // Basic usage\n * _hyperscript.use(hyperscriptI18n());\n *\n * @example\n * // With options\n * _hyperscript.use(hyperscriptI18n({\n * defaultLanguage: 'ja',\n * confidenceThreshold: 0.6,\n * debug: true,\n * }));\n */\nexport function hyperscriptI18n(options: PluginOptions = {}) {\n return function plugin(hs: unknown): void {\n const { internals } = hs as HyperscriptGlobal;\n const runtime = internals.runtime;\n const originalGetScript = runtime.getScript.bind(runtime);\n\n runtime.getScript = function (elt: Element): string | null {\n const src = originalGetScript(elt);\n if (!src) return null;\n\n // Resolve language\n const lang = resolveLanguageWithOptions(elt, options);\n\n // If English or no language detected, pass through\n if (!lang || lang === 'en') return src;\n\n // Preprocess to English\n const english = preprocessToEnglish(src, lang, options);\n\n if (english !== src) {\n if (options.debug) {\n console.log(`[hyperscript-i18n] ${lang}: \"${src}\" → \"${english}\"`);\n }\n } else {\n // Translation produced no change — likely a failure\n console.warn(\n `[hyperscript-i18n] Translation unchanged for lang=\"${lang}\": \"${src.length > 60 ? src.slice(0, 60) + '…' : src}\". ` +\n 'The input may not match any known pattern, or the language may not be registered. ' +\n 'Original text will be passed to _hyperscript as-is.'\n );\n }\n\n return english;\n };\n };\n}\n\n/**\n * Resolve language with plugin options factored in.\n */\nfunction resolveLanguageWithOptions(elt: Element, options: PluginOptions): string | null {\n // Check custom attribute name\n if (options.languageAttribute) {\n const custom = elt.getAttribute(options.languageAttribute);\n if (custom) return custom.split('-')[0].toLowerCase();\n }\n\n // Standard resolution\n const resolved = resolveLanguage(elt);\n if (resolved) return resolved;\n\n // Fall back to default language\n return options.defaultLanguage ?? null;\n}\n\n/**\n * Standalone preprocessing function for programmatic use.\n * Call this when using _hyperscript.evaluate() or _hyperscript(\"code\") directly.\n *\n * @example\n * const english = preprocess(\"トグル .active\", \"ja\");\n * _hyperscript(english);\n */\nexport function preprocess(\n src: string,\n lang: string,\n config: Partial<PreprocessorConfig> = {}\n): string {\n if (lang === 'en') return src;\n return preprocessToEnglish(src, lang, config);\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lokascript/hyperscript-adapter",
3
- "version": "2.3.1",
3
+ "version": "2.4.0",
4
4
  "description": "Multilingual adapter plugin for original _hyperscript — write hyperscript in 24 languages",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -151,7 +151,7 @@
151
151
  "@types/node": "^20.0.0",
152
152
  "tsup": "^8.0.0",
153
153
  "typescript": "^5.0.0",
154
- "vitest": "^4.0.0"
154
+ "vitest": "^4.1.5"
155
155
  },
156
156
  "files": [
157
157
  "dist",
@@ -11,14 +11,18 @@ export const SYNTAX: Record<string, readonly [string, string][]> = {
11
11
  behavior: [['patient', '']],
12
12
  blur: [['patient', '']],
13
13
  break: [],
14
+ breakpoint: [],
14
15
  call: [['patient', '']],
16
+ clear: [['patient', '']],
15
17
  clone: [['patient', ''], ['destination', 'into']],
18
+ close: [['patient', '']],
16
19
  compound: [],
17
20
  continue: [],
18
21
  copy: [['patient', '']],
19
22
  decrement: [['patient', ''], ['quantity', 'by']],
20
23
  default: [['destination', ''], ['patient', 'to']],
21
24
  else: [],
25
+ empty: [['patient', '']],
22
26
  exit: [],
23
27
  fetch: [['source', ''], ['responseType', 'as'], ['method', 'via'], ['destination', 'on']],
24
28
  focus: [['patient', '']],
@@ -37,13 +41,20 @@ export const SYNTAX: Record<string, readonly [string, string][]> = {
37
41
  measure: [['patient', ''], ['source', 'of']],
38
42
  morph: [['destination', ''], ['patient', 'to']],
39
43
  on: [['event', ''], ['source', 'from']],
44
+ open: [['style', 'as'], ['patient', '']],
40
45
  pick: [['patient', ''], ['source', 'from']],
41
46
  prepend: [['patient', ''], ['destination', 'to']],
47
+ process: [['patient', 'partials in']],
48
+ push: [['patient', 'url']],
42
49
  put: [['patient', ''], ['destination', 'into']],
43
50
  remove: [['patient', ''], ['source', 'from']],
44
51
  render: [['patient', ''], ['style', 'with']],
45
52
  repeat: [['quantity', ''], ['condition', 'until']],
53
+ replace: [['patient', 'url']],
54
+ reset: [['patient', '']],
46
55
  return: [['patient', '']],
56
+ scroll: [['destination', 'to']],
57
+ select: [['patient', '']],
47
58
  send: [['event', ''], ['destination', 'to']],
48
59
  set: [['destination', ''], ['patient', 'to']],
49
60
  settle: [['patient', '']],
@@ -56,6 +67,6 @@ export const SYNTAX: Record<string, readonly [string, string][]> = {
56
67
  transition: [['patient', ''], ['goal', 'to'], ['destination', 'on'], ['duration', 'over'], ['style', 'with']],
57
68
  trigger: [['event', ''], ['destination', 'on']],
58
69
  unless: [['condition', '']],
59
- wait: [['patient', '']],
70
+ wait: [['duration', '']],
60
71
  while: [['condition', '']],
61
72
  };
@@ -8,10 +8,9 @@
8
8
  import {
9
9
  translate,
10
10
  render,
11
- createSemanticAnalyzer,
12
- shouldUseSemanticResult,
11
+ parseSemantic,
12
+ isLanguageRegistered,
13
13
  tryGetProfile,
14
- type SemanticAnalyzer,
15
14
  } from '@lokascript/semantic';
16
15
 
17
16
  export interface PreprocessorConfig {
@@ -55,15 +54,6 @@ function resolveThreshold(threshold: number | Record<string, number>, lang: stri
55
54
  return threshold[lang] ?? threshold['*'] ?? DEFAULT_THRESHOLD;
56
55
  }
57
56
 
58
- let analyzer: SemanticAnalyzer | null = null;
59
-
60
- function getAnalyzer(): SemanticAnalyzer {
61
- if (!analyzer) {
62
- analyzer = createSemanticAnalyzer();
63
- }
64
- return analyzer;
65
- }
66
-
67
57
  /**
68
58
  * Preprocess non-English hyperscript into English.
69
59
  *
@@ -160,19 +150,19 @@ function trySemanticTranslation(src: string, lang: string, threshold: number): s
160
150
  }
161
151
 
162
152
  // Check if the semantic parser can handle this
163
- const sem = getAnalyzer();
164
- if (!sem.supportsLanguage(lang)) return null;
153
+ if (!isLanguageRegistered(lang)) return null;
165
154
 
166
- const result = sem.analyze(src, lang);
167
- if (!shouldUseSemanticResult(result, threshold)) return null;
168
-
169
- // Render the semantic node to English
170
- if (result.node) {
171
- return render(result.node, 'en');
155
+ const result = parseSemantic(src, lang);
156
+ if (result.confidence < threshold || !result.node) {
157
+ // If we got SOME confidence but no node, fall back to translate()
158
+ if (result.confidence >= threshold) {
159
+ return translate(src, lang, 'en');
160
+ }
161
+ return null;
172
162
  }
173
163
 
174
- // Fallback: use translate() which combines parse+render
175
- return translate(src, lang, 'en');
164
+ // Render the semantic node to English
165
+ return render(result.node, 'en');
176
166
  } catch {
177
167
  return null;
178
168
  }
@@ -12,11 +12,9 @@
12
12
  // Import from /core — does NOT trigger all-language registration.
13
13
  // Languages are registered separately via side-effect imports in bundle entries.
14
14
  import {
15
- createSemanticAnalyzer,
16
- shouldUseSemanticResult,
17
- parse,
15
+ parseWithConfidence,
16
+ isLanguageRegistered,
18
17
  tryGetProfile,
19
- type SemanticAnalyzer,
20
18
  } from '@lokascript/semantic/core';
21
19
 
22
20
  import { renderToHyperscript } from './hyperscript-renderer';
@@ -35,15 +33,6 @@ function resolveThreshold(threshold: number | Record<string, number>, lang: stri
35
33
  return threshold[lang] ?? threshold['*'] ?? DEFAULT_THRESHOLD;
36
34
  }
37
35
 
38
- let analyzer: SemanticAnalyzer | null = null;
39
-
40
- function getAnalyzer(): SemanticAnalyzer {
41
- if (!analyzer) {
42
- analyzer = createSemanticAnalyzer();
43
- }
44
- return analyzer;
45
- }
46
-
47
36
  /**
48
37
  * Preprocess non-English hyperscript into English.
49
38
  * Identical to preprocessToEnglish in preprocessor.ts but with slim imports.
@@ -111,19 +100,12 @@ function trySemanticTranslation(src: string, lang: string, threshold: number): s
111
100
  return translateCompound(statements, lang, threshold);
112
101
  }
113
102
 
114
- const sem = getAnalyzer();
115
- if (!sem.supportsLanguage(lang)) return null;
116
-
117
- const result = sem.analyze(src, lang);
118
- if (!shouldUseSemanticResult(result, threshold)) return null;
103
+ if (!isLanguageRegistered(lang)) return null;
119
104
 
120
- if (result.node) {
121
- return renderToHyperscript(result.node);
122
- }
105
+ const result = parseWithConfidence(src, lang);
106
+ if (result.confidence < threshold || !result.node) return null;
123
107
 
124
- // Fallback: parse then render via custom renderer
125
- const node = parse(src, lang);
126
- return renderToHyperscript(node);
108
+ return renderToHyperscript(result.node);
127
109
  } catch {
128
110
  return null;
129
111
  }