glotfile 0.5.1 → 0.5.2

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.
@@ -29,7 +29,7 @@ var init_dictionary_en = __esm({
29
29
  import { Hono as Hono2 } from "hono";
30
30
  import { serve } from "@hono/node-server";
31
31
  import { fileURLToPath } from "url";
32
- import { dirname as dirname4, join as join9, resolve as resolve10, extname as extname3, sep as sep3 } from "path";
32
+ import { dirname as dirname4, join as join10, resolve as resolve10, extname as extname3, sep as sep3 } from "path";
33
33
  import { readFile, stat } from "fs/promises";
34
34
  import { createServer } from "net";
35
35
  import open from "open";
@@ -916,7 +916,11 @@ var PATTERNS = {
916
916
  apple: [
917
917
  /NSLocalizedString\s*\(\s*@?"([^"]+)"/g,
918
918
  /String\s*\(\s*localized:\s*"([^"]+)"/g,
919
- /localizedString\s*\(\s*forKey:\s*"([^"]+)"/g
919
+ /localizedString\s*\(\s*forKey:\s*"([^"]+)"/g,
920
+ // The "key".localized / "key".localised String-extension idiom, where the
921
+ // literal IS the key (common when keys are natural-language source text).
922
+ /"([^"]+)"\s*\.\s*localized\b/g,
923
+ /"([^"]+)"\s*\.\s*localised\b/g
920
924
  ]
921
925
  };
922
926
  var PREFIX_PATTERNS = {
@@ -934,7 +938,7 @@ var PREFIX_PATTERNS = {
934
938
  /(?<!\.)(?<![a-zA-Z0-9_$])\bt\s*\(\s*`([^`$]*)\$\{/g
935
939
  ]
936
940
  };
937
- var CACHE_VERSION = 4;
941
+ var CACHE_VERSION = 5;
938
942
  var EXT_SCANNER = {
939
943
  ".php": "laravel",
940
944
  ".vue": "js-i18n",
@@ -2737,8 +2741,50 @@ var appleStringsdict = {
2737
2741
  }
2738
2742
  };
2739
2743
 
2744
+ // src/server/adapters/apple-strings.ts
2745
+ var DEFAULT_LOCALE_CASE6 = "bcp47-hyphen";
2746
+ function escape(s) {
2747
+ return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\t/g, "\\t").replace(/\r/g, "\\r");
2748
+ }
2749
+ var appleStrings = {
2750
+ name: "apple-strings",
2751
+ capabilities: {
2752
+ // Plurals belong in .stringsdict (apple-stringsdict), not the scalar table.
2753
+ plural: "none",
2754
+ select: "none",
2755
+ nesting: "flat",
2756
+ metadata: false,
2757
+ placeholderStyle: "printf",
2758
+ fileGrouping: "per-locale"
2759
+ },
2760
+ defaultLocaleCase: DEFAULT_LOCALE_CASE6,
2761
+ export(state, output) {
2762
+ const files = [];
2763
+ const warnings = [];
2764
+ warnings.push(...localeCollisionWarnings(output, state.config.locales, DEFAULT_LOCALE_CASE6));
2765
+ const emptyAs = resolveEmptyAs(output, "source");
2766
+ const keys = Object.keys(state.keys).sort();
2767
+ for (const locale of state.config.locales) {
2768
+ const lines = [];
2769
+ for (const key of keys) {
2770
+ const entry = state.keys[key];
2771
+ if (entry.plural) continue;
2772
+ const value = resolveScalar(entry, locale, state.config.sourceLocale, emptyAs);
2773
+ if (value === null) continue;
2774
+ lines.push(`"${escape(key)}" = "${escape(value)}";`);
2775
+ }
2776
+ const contents = lines.length ? lines.join("\n") + "\n" : "";
2777
+ files.push({
2778
+ path: resolvePath(output.path, resolveLocaleToken(output, locale, DEFAULT_LOCALE_CASE6)),
2779
+ contents
2780
+ });
2781
+ }
2782
+ return { files, warnings };
2783
+ }
2784
+ };
2785
+
2740
2786
  // src/server/adapters/vue-i18n-json.ts
2741
- var DEFAULT_LOCALE_CASE6 = "lower-hyphen";
2787
+ var DEFAULT_LOCALE_CASE7 = "lower-hyphen";
2742
2788
  var vueI18nJson = {
2743
2789
  name: "vue-i18n-json",
2744
2790
  capabilities: {
@@ -2749,11 +2795,11 @@ var vueI18nJson = {
2749
2795
  placeholderStyle: "named",
2750
2796
  fileGrouping: "per-locale"
2751
2797
  },
2752
- defaultLocaleCase: DEFAULT_LOCALE_CASE6,
2798
+ defaultLocaleCase: DEFAULT_LOCALE_CASE7,
2753
2799
  export(state, output) {
2754
2800
  const files = [];
2755
2801
  const warnings = [];
2756
- warnings.push(...localeCollisionWarnings(output, state.config.locales, DEFAULT_LOCALE_CASE6));
2802
+ warnings.push(...localeCollisionWarnings(output, state.config.locales, DEFAULT_LOCALE_CASE7));
2757
2803
  const { indent, finalNewline } = resolveFormat(state, output);
2758
2804
  const fmt = { indent, sortKeys: true, finalNewline };
2759
2805
  const emptyAs = resolveEmptyAs(output, "omit");
@@ -2793,7 +2839,7 @@ var vueI18nJson = {
2793
2839
  }
2794
2840
  payload = tree;
2795
2841
  }
2796
- files.push({ path: resolvePath(output.path, resolveLocaleToken(output, locale, DEFAULT_LOCALE_CASE6)), contents: serializeJson(payload, fmt) });
2842
+ files.push({ path: resolvePath(output.path, resolveLocaleToken(output, locale, DEFAULT_LOCALE_CASE7)), contents: serializeJson(payload, fmt) });
2797
2843
  }
2798
2844
  files.sort((a, b) => a.path.localeCompare(b.path));
2799
2845
  return { files, warnings };
@@ -2837,7 +2883,7 @@ function renderEmbeddedIcu(value) {
2837
2883
  function renderScalar(value, ids) {
2838
2884
  return isIcuPluralOrSelect(value) ? renderEmbeddedIcu(value) : renderInterpolations(value, ids);
2839
2885
  }
2840
- var DEFAULT_LOCALE_CASE7 = "bcp47-hyphen";
2886
+ var DEFAULT_LOCALE_CASE8 = "bcp47-hyphen";
2841
2887
  var angularXliff = {
2842
2888
  name: "angular-xliff",
2843
2889
  capabilities: {
@@ -2848,17 +2894,17 @@ var angularXliff = {
2848
2894
  placeholderStyle: "icu",
2849
2895
  fileGrouping: "per-locale"
2850
2896
  },
2851
- defaultLocaleCase: DEFAULT_LOCALE_CASE7,
2897
+ defaultLocaleCase: DEFAULT_LOCALE_CASE8,
2852
2898
  export(state, output) {
2853
2899
  const files = [];
2854
2900
  const warnings = [];
2855
- warnings.push(...localeCollisionWarnings(output, state.config.locales, DEFAULT_LOCALE_CASE7));
2901
+ warnings.push(...localeCollisionWarnings(output, state.config.locales, DEFAULT_LOCALE_CASE8));
2856
2902
  const sourceLocale = state.config.sourceLocale;
2857
- const sourceToken = resolveLocaleToken(output, sourceLocale, DEFAULT_LOCALE_CASE7);
2903
+ const sourceToken = resolveLocaleToken(output, sourceLocale, DEFAULT_LOCALE_CASE8);
2858
2904
  const emptyAs = resolveEmptyAs(output, "source");
2859
2905
  const keys = Object.keys(state.keys).sort();
2860
2906
  for (const locale of state.config.locales) {
2861
- const token = resolveLocaleToken(output, locale, DEFAULT_LOCALE_CASE7);
2907
+ const token = resolveLocaleToken(output, locale, DEFAULT_LOCALE_CASE8);
2862
2908
  const units = [];
2863
2909
  for (const key of keys) {
2864
2910
  const entry = state.keys[key];
@@ -2921,7 +2967,7 @@ function yamlMap(node, indent, level) {
2921
2967
  }
2922
2968
  return lines;
2923
2969
  }
2924
- var DEFAULT_LOCALE_CASE8 = "bcp47-hyphen";
2970
+ var DEFAULT_LOCALE_CASE9 = "bcp47-hyphen";
2925
2971
  var railsYaml = {
2926
2972
  name: "rails-yaml",
2927
2973
  capabilities: {
@@ -2932,10 +2978,10 @@ var railsYaml = {
2932
2978
  placeholderStyle: "named",
2933
2979
  fileGrouping: "per-locale"
2934
2980
  },
2935
- defaultLocaleCase: DEFAULT_LOCALE_CASE8,
2981
+ defaultLocaleCase: DEFAULT_LOCALE_CASE9,
2936
2982
  export(state, output) {
2937
2983
  const warnings = [];
2938
- warnings.push(...localeCollisionWarnings(output, state.config.locales, DEFAULT_LOCALE_CASE8));
2984
+ warnings.push(...localeCollisionWarnings(output, state.config.locales, DEFAULT_LOCALE_CASE9));
2939
2985
  const { indent, finalNewline } = resolveFormat(state, output);
2940
2986
  const emptyAs = resolveEmptyAs(output, "omit");
2941
2987
  const files = [];
@@ -2967,7 +3013,7 @@ var railsYaml = {
2967
3013
  for (const c of collisions) {
2968
3014
  warnings.push({ code: "key-collision", key: c, locale, message: "key is both a leaf and a parent; dropped from nested output" });
2969
3015
  }
2970
- const token = resolveLocaleToken(output, locale, DEFAULT_LOCALE_CASE8);
3016
+ const token = resolveLocaleToken(output, locale, DEFAULT_LOCALE_CASE9);
2971
3017
  const body = [`${yamlKey(token)}:`, ...yamlMap(nested, indent, 1)].join("\n");
2972
3018
  files.push({ path: resolvePath(output.path, token), contents: finalNewline ? body + "\n" : body });
2973
3019
  }
@@ -3008,6 +3054,7 @@ function getRegistry() {
3008
3054
  [i18nextJson.name]: i18nextJson,
3009
3055
  [gettextPo.name]: gettextPo,
3010
3056
  [appleStringsdict.name]: appleStringsdict,
3057
+ [appleStrings.name]: appleStrings,
3011
3058
  [vueI18nJson.name]: vueI18nJson,
3012
3059
  [angularXliff.name]: angularXliff,
3013
3060
  [railsYaml.name]: railsYaml
@@ -3038,8 +3085,8 @@ function checkOutputs(state, root) {
3038
3085
  }
3039
3086
 
3040
3087
  // src/server/api.ts
3041
- import { readFileSync as readFileSync14, existsSync as existsSync11, readdirSync as readdirSync8, statSync as statSync5, rmSync as rmSync4 } from "fs";
3042
- import { dirname as dirname3, resolve as resolve9, basename, relative as relative3, sep as sep2 } from "path";
3088
+ import { readFileSync as readFileSync15, existsSync as existsSync11, readdirSync as readdirSync9, statSync as statSync6, rmSync as rmSync4 } from "fs";
3089
+ import { dirname as dirname3, resolve as resolve9, basename, relative as relative4, sep as sep2 } from "path";
3043
3090
 
3044
3091
  // src/server/ai/anthropic.ts
3045
3092
  import Anthropic from "@anthropic-ai/sdk";
@@ -3685,6 +3732,9 @@ function readLog(projectRoot, limit = 100) {
3685
3732
  return entries.reverse().slice(0, limit);
3686
3733
  }
3687
3734
 
3735
+ // src/server/import/run.ts
3736
+ import { relative as relative3 } from "path";
3737
+
3688
3738
  // src/server/import/detect.ts
3689
3739
  import { existsSync as existsSync9, readdirSync as readdirSync3, statSync as statSync2 } from "fs";
3690
3740
  import { join as join4 } from "path";
@@ -3749,11 +3799,38 @@ function detectArb(root) {
3749
3799
  }
3750
3800
  return null;
3751
3801
  }
3752
- var DETECTORS = [detectLaravel, detectVue, detectArb];
3802
+ function lprojLocales(dir) {
3803
+ return listDirs(dir).map((d) => d.match(/^(.+)\.lproj$/)?.[1]).filter((l) => !!l && LOCALE_RE.test(l) && existsSync9(join4(dir, `${l}.lproj`, "Localizable.strings")));
3804
+ }
3805
+ function detectApple(root) {
3806
+ const candidates = [root, ...listDirs(root).map((d) => join4(root, d))];
3807
+ let best = null;
3808
+ for (const dir of candidates) {
3809
+ const locales = lprojLocales(dir);
3810
+ if (locales.length === 0) continue;
3811
+ if (!best || locales.length > best.locales.length) {
3812
+ best = {
3813
+ format: "apple-strings",
3814
+ localeRoot: dir,
3815
+ locales,
3816
+ sourceLocale: pickSource(locales, (loc) => {
3817
+ try {
3818
+ return statSync2(join4(dir, `${loc}.lproj`, "Localizable.strings")).size;
3819
+ } catch {
3820
+ return 0;
3821
+ }
3822
+ })
3823
+ };
3824
+ }
3825
+ }
3826
+ return best;
3827
+ }
3828
+ var DETECTORS = [detectLaravel, detectVue, detectArb, detectApple];
3753
3829
  var BY_FORMAT = {
3754
3830
  "laravel-php": detectLaravel,
3755
3831
  "vue-i18n-json": (root) => detectVue(root, true),
3756
- "flutter-arb": detectArb
3832
+ "flutter-arb": detectArb,
3833
+ "apple-strings": detectApple
3757
3834
  };
3758
3835
  function detect(root, formatOverride) {
3759
3836
  if (!existsSync9(root)) return null;
@@ -3963,11 +4040,139 @@ var flutterArb2 = {
3963
4040
  }
3964
4041
  };
3965
4042
 
4043
+ // src/server/import/parsers/apple-strings.ts
4044
+ import { readdirSync as readdirSync7, readFileSync as readFileSync11, statSync as statSync4 } from "fs";
4045
+ import { join as join8 } from "path";
4046
+ var LOCALE_RE4 = /^[a-z]{2,3}([_-][A-Za-z]{2,4}){0,2}$/;
4047
+ var TABLE = "Localizable.strings";
4048
+ function localeFromLproj(dir) {
4049
+ const m = dir.match(/^(.+)\.lproj$/);
4050
+ if (!m) return null;
4051
+ return LOCALE_RE4.test(m[1]) ? m[1] : null;
4052
+ }
4053
+ function unescape(body) {
4054
+ return body.replace(/\\(U[0-9a-fA-F]{4}|u[0-9a-fA-F]{4}|.)/g, (_m, esc) => {
4055
+ const c = esc[0];
4056
+ if (c === "U" || c === "u") return String.fromCharCode(parseInt(esc.slice(1), 16));
4057
+ if (c === "n") return "\n";
4058
+ if (c === "t") return " ";
4059
+ if (c === "r") return "\r";
4060
+ return esc;
4061
+ });
4062
+ }
4063
+ function parseStrings(text, file, warnings) {
4064
+ const pairs = [];
4065
+ let i = 0;
4066
+ const n = text.length;
4067
+ const skipTrivia = () => {
4068
+ while (i < n) {
4069
+ const c = text[i];
4070
+ if (c === " " || c === " " || c === "\n" || c === "\r") {
4071
+ i++;
4072
+ continue;
4073
+ }
4074
+ if (c === "/" && text[i + 1] === "/") {
4075
+ i += 2;
4076
+ while (i < n && text[i] !== "\n") i++;
4077
+ continue;
4078
+ }
4079
+ if (c === "/" && text[i + 1] === "*") {
4080
+ i += 2;
4081
+ while (i < n && !(text[i] === "*" && text[i + 1] === "/")) i++;
4082
+ i += 2;
4083
+ continue;
4084
+ }
4085
+ break;
4086
+ }
4087
+ };
4088
+ const readToken = () => {
4089
+ if (i >= n) return null;
4090
+ if (text[i] === '"') {
4091
+ i++;
4092
+ let raw2 = "";
4093
+ while (i < n) {
4094
+ const c = text[i];
4095
+ if (c === "\\") {
4096
+ raw2 += c + (text[i + 1] ?? "");
4097
+ i += 2;
4098
+ continue;
4099
+ }
4100
+ if (c === '"') {
4101
+ i++;
4102
+ return unescape(raw2);
4103
+ }
4104
+ raw2 += c;
4105
+ i++;
4106
+ }
4107
+ return null;
4108
+ }
4109
+ let raw = "";
4110
+ while (i < n && !/[\s=;]/.test(text[i])) raw += text[i++];
4111
+ return raw.length ? raw : null;
4112
+ };
4113
+ while (true) {
4114
+ skipTrivia();
4115
+ if (i >= n) break;
4116
+ const key = readToken();
4117
+ if (key === null) {
4118
+ warnings.push(`apple-strings: malformed entry in ${file} near offset ${i}`);
4119
+ break;
4120
+ }
4121
+ skipTrivia();
4122
+ if (text[i] !== "=") {
4123
+ warnings.push(`apple-strings: expected '=' after key "${key}" in ${file}`);
4124
+ break;
4125
+ }
4126
+ i++;
4127
+ skipTrivia();
4128
+ const value = readToken();
4129
+ if (value === null) {
4130
+ warnings.push(`apple-strings: missing value for key "${key}" in ${file}`);
4131
+ break;
4132
+ }
4133
+ skipTrivia();
4134
+ if (text[i] === ";") i++;
4135
+ pairs.push({ key, value });
4136
+ }
4137
+ return pairs;
4138
+ }
4139
+ var appleStrings2 = {
4140
+ name: "apple-strings",
4141
+ parse(localeRoot, opts) {
4142
+ const warnings = [];
4143
+ const keys = {};
4144
+ const locales = [];
4145
+ for (const dir of readdirSync7(localeRoot).sort()) {
4146
+ const locale = localeFromLproj(dir);
4147
+ if (!locale) continue;
4148
+ if (opts?.locales && !opts.locales.includes(locale)) continue;
4149
+ const file = join8(localeRoot, dir, TABLE);
4150
+ let text;
4151
+ try {
4152
+ if (!statSync4(file).isFile()) continue;
4153
+ text = readFileSync11(file, "utf8");
4154
+ } catch {
4155
+ continue;
4156
+ }
4157
+ locales.push(locale);
4158
+ const others = readdirSync7(join8(localeRoot, dir)).filter((f) => f.endsWith(".strings") && f !== TABLE);
4159
+ if (others.length) {
4160
+ warnings.push(`apple-strings: ${dir} has other .strings tables (${others.join(", ")}); only ${TABLE} is imported`);
4161
+ }
4162
+ for (const { key, value } of parseStrings(text, file, warnings)) {
4163
+ (keys[key] ??= { values: {} }).values[locale] = value;
4164
+ }
4165
+ }
4166
+ return { locales, keys, warnings };
4167
+ }
4168
+ };
4169
+
3966
4170
  // src/server/import/parsers/index.ts
3967
4171
  var REGISTRY = {
3968
4172
  [vueI18nJson2.name]: vueI18nJson2,
3969
4173
  [laravelPhp2.name]: laravelPhp2,
3970
- [flutterArb2.name]: flutterArb2
4174
+ [flutterArb2.name]: flutterArb2,
4175
+ [appleStrings2.name]: appleStrings2
3971
4176
  };
3972
4177
  function getParser(name) {
3973
4178
  const p = REGISTRY[name];
@@ -3979,16 +4184,20 @@ function getParser(name) {
3979
4184
  var OUTPUT_BY_FORMAT = {
3980
4185
  "laravel-php": { adapter: "laravel-php", path: "lang/{locale}/{namespace}.php" },
3981
4186
  "vue-i18n-json": { adapter: "vue-i18n-json", path: "src/locale/{locale}.json" },
3982
- "flutter-arb": { adapter: "flutter-arb", path: "lib/l10n/app_{locale}.arb" }
4187
+ "flutter-arb": { adapter: "flutter-arb", path: "lib/l10n/app_{locale}.arb" },
4188
+ "apple-strings": { adapter: "apple-strings", path: "{locale}.lproj/Localizable.strings", rootRelative: true }
3983
4189
  };
3984
4190
  function assemble2(parsed, opts) {
3985
4191
  const warnings = [...parsed.warnings];
3986
4192
  const base = OUTPUT_BY_FORMAT[opts.format];
3987
4193
  if (!base) throw new Error(`No output mapping for format "${opts.format}"`);
4194
+ const prefix = (opts.localeRootRel ?? "").replace(/\\/g, "/").replace(/\/+$/, "");
4195
+ const path = base.rootRelative && prefix ? `${prefix}/${base.path}` : base.path;
3988
4196
  const rawLocales = [.../* @__PURE__ */ new Set([opts.sourceLocale, ...parsed.locales])];
3989
4197
  const pairs = rawLocales.map((obs) => [canonLocale(obs), obs]);
3990
4198
  const inferred = inferLocaleStyle(pairs, getAdapter(base.adapter).defaultLocaleCase);
3991
- const output = { ...base, ...inferred };
4199
+ const { rootRelative: _rootRelative, ...baseOutput } = base;
4200
+ const output = { ...baseOutput, path, ...inferred };
3992
4201
  const sourceLocale = canonLocale(opts.sourceLocale);
3993
4202
  const locales = [...new Set(rawLocales.map(canonLocale))].sort();
3994
4203
  const keys = {};
@@ -4080,7 +4289,8 @@ function runImport(opts) {
4080
4289
  const assembled = assemble2(parsed, {
4081
4290
  sourceLocale: opts.sourceLocale ?? det.sourceLocale,
4082
4291
  format: det.format,
4083
- cldr: opts.cldr
4292
+ cldr: opts.cldr,
4293
+ localeRootRel: relative3(opts.projectRoot, det.localeRoot)
4084
4294
  });
4085
4295
  const { warnings, ...rest } = assembled;
4086
4296
  const state = validate(rest);
@@ -4093,7 +4303,7 @@ function runImport(opts) {
4093
4303
  }
4094
4304
 
4095
4305
  // src/server/export-run.ts
4096
- import { existsSync as existsSync10, readFileSync as readFileSync11, readdirSync as readdirSync7, rmdirSync, statSync as statSync4, unlinkSync } from "fs";
4306
+ import { existsSync as existsSync10, readFileSync as readFileSync12, readdirSync as readdirSync8, rmdirSync, statSync as statSync5, unlinkSync } from "fs";
4097
4307
  import { dirname as dirname2, resolve as resolve7, sep } from "path";
4098
4308
  function effectiveLocales(config) {
4099
4309
  const limit = config.exportLocales;
@@ -4136,7 +4346,7 @@ function pruneStaleLocaleFiles(output, validTokens, projectRoot) {
4136
4346
  if (!segment.includes("{locale}") && !segment.includes("{namespace}")) {
4137
4347
  const next = resolve7(dir, segment);
4138
4348
  if (isLast) {
4139
- if (stale(locale) && existsSync10(next) && statSync4(next).isFile()) {
4349
+ if (stale(locale) && existsSync10(next) && statSync5(next).isFile()) {
4140
4350
  unlinkSync(next);
4141
4351
  deleted++;
4142
4352
  removeEmptyDirs(dir, root);
@@ -4149,7 +4359,7 @@ function pruneStaleLocaleFiles(output, validTokens, projectRoot) {
4149
4359
  const re = segmentRegExp(segment);
4150
4360
  let entries;
4151
4361
  try {
4152
- entries = readdirSync7(dir, { withFileTypes: true });
4362
+ entries = readdirSync8(dir, { withFileTypes: true });
4153
4363
  } catch {
4154
4364
  return;
4155
4365
  }
@@ -4192,7 +4402,7 @@ function exportToDisk(state, projectRoot, opts) {
4192
4402
  writtenPaths.add(abs);
4193
4403
  let current = null;
4194
4404
  try {
4195
- current = readFileSync11(abs, "utf8");
4405
+ current = readFileSync12(abs, "utf8");
4196
4406
  } catch {
4197
4407
  }
4198
4408
  if (current === f.contents) {
@@ -4209,17 +4419,17 @@ function exportToDisk(state, projectRoot, opts) {
4209
4419
  }
4210
4420
 
4211
4421
  // src/server/ui-prefs.ts
4212
- import { readFileSync as readFileSync12 } from "fs";
4422
+ import { readFileSync as readFileSync13 } from "fs";
4213
4423
  import { homedir } from "os";
4214
- import { join as join8 } from "path";
4424
+ import { join as join9 } from "path";
4215
4425
  var THEMES = ["system", "light", "dark"];
4216
4426
  var isThemeMode = (v) => THEMES.includes(v);
4217
4427
  var isPanelWidth = (v) => typeof v === "number" && Number.isFinite(v) && v >= 120 && v <= 1200;
4218
- var defaultUiPrefsPath = () => join8(homedir(), ".glotfile", "ui.json");
4428
+ var defaultUiPrefsPath = () => join9(homedir(), ".glotfile", "ui.json");
4219
4429
  var DEFAULTS = { theme: "system" };
4220
4430
  function readJson(path) {
4221
4431
  try {
4222
- const parsed = JSON.parse(readFileSync12(path, "utf8"));
4432
+ const parsed = JSON.parse(readFileSync13(path, "utf8"));
4223
4433
  return parsed && typeof parsed === "object" ? parsed : {};
4224
4434
  } catch {
4225
4435
  return {};
@@ -4238,7 +4448,7 @@ function saveUiPrefs(path, prefs) {
4238
4448
  }
4239
4449
 
4240
4450
  // src/server/local-settings.ts
4241
- import { readFileSync as readFileSync13 } from "fs";
4451
+ import { readFileSync as readFileSync14 } from "fs";
4242
4452
  import { resolve as resolve8 } from "path";
4243
4453
  var EDITOR_IDS = ["vscode", "zed", "phpstorm"];
4244
4454
  var isEditorId = (v) => EDITOR_IDS.includes(v);
@@ -4253,7 +4463,7 @@ var DEFAULT_EDITOR = "vscode";
4253
4463
  var settingsPath = (projectRoot) => resolve8(projectRoot, ".glotfile", "settings.json");
4254
4464
  function readJson2(path) {
4255
4465
  try {
4256
- const parsed = JSON.parse(readFileSync13(path, "utf8"));
4466
+ const parsed = JSON.parse(readFileSync14(path, "utf8"));
4257
4467
  return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
4258
4468
  } catch {
4259
4469
  return {};
@@ -4326,7 +4536,7 @@ function projectName(root) {
4326
4536
  const nameFile = resolve9(root, ".idea", ".name");
4327
4537
  if (existsSync11(nameFile)) {
4328
4538
  try {
4329
- const name = readFileSync14(nameFile, "utf8").trim();
4539
+ const name = readFileSync15(nameFile, "utf8").trim();
4330
4540
  if (name) return name;
4331
4541
  } catch {
4332
4542
  }
@@ -4441,7 +4651,7 @@ function createApi(deps) {
4441
4651
  app.get("/file", (c) => c.json({ path: deps.statePath, name: basename(deps.statePath), dir: projectRoot, project: basename(projectRoot) }));
4442
4652
  app.get("/files", (c) => {
4443
4653
  const found = /* @__PURE__ */ new Map();
4444
- const activeRel = relative3(projectRoot, deps.statePath);
4654
+ const activeRel = relative4(projectRoot, deps.statePath);
4445
4655
  found.set(deps.statePath, {
4446
4656
  name: basename(deps.statePath),
4447
4657
  path: deps.statePath,
@@ -4451,7 +4661,7 @@ function createApi(deps) {
4451
4661
  if (depth > 4) return;
4452
4662
  let entries = [];
4453
4663
  try {
4454
- entries = readdirSync8(dir);
4664
+ entries = readdirSync9(dir);
4455
4665
  } catch {
4456
4666
  return;
4457
4667
  }
@@ -4465,7 +4675,7 @@ function createApi(deps) {
4465
4675
  filePath = abs;
4466
4676
  } else {
4467
4677
  try {
4468
- if (statSync5(abs).isDirectory()) walk(abs, depth + 1);
4678
+ if (statSync6(abs).isDirectory()) walk(abs, depth + 1);
4469
4679
  } catch {
4470
4680
  }
4471
4681
  continue;
@@ -4473,7 +4683,7 @@ function createApi(deps) {
4473
4683
  if (found.has(filePath)) continue;
4474
4684
  try {
4475
4685
  loadState(filePath);
4476
- const rel = relative3(projectRoot, filePath);
4686
+ const rel = relative4(projectRoot, filePath);
4477
4687
  found.set(filePath, { name: basename(filePath), path: filePath, relDir: rel !== basename(filePath) ? dirname3(rel) : void 0 });
4478
4688
  } catch {
4479
4689
  }
@@ -4552,7 +4762,7 @@ function createApi(deps) {
4552
4762
  for (const e of Object.values(s.keys)) if (e.screenshot === screenshot) return;
4553
4763
  const root = dirname3(resolve9(deps.statePath));
4554
4764
  const abs = resolve9(root, screenshot);
4555
- const rel = relative3(root, abs);
4765
+ const rel = relative4(root, abs);
4556
4766
  const seg0 = rel.split(sep2)[0] ?? "";
4557
4767
  if (!rel.startsWith("..") && seg0.endsWith("-screenshots") && existsSync11(abs)) {
4558
4768
  try {
@@ -5246,7 +5456,7 @@ function createApi(deps) {
5246
5456
 
5247
5457
  // src/server/server.ts
5248
5458
  var here = dirname4(fileURLToPath(import.meta.url));
5249
- var DEFAULT_UI_DIR = join9(here, "..", "ui");
5459
+ var DEFAULT_UI_DIR = join10(here, "..", "ui");
5250
5460
  var MIME = {
5251
5461
  ".html": "text/html; charset=utf-8",
5252
5462
  ".js": "text/javascript; charset=utf-8",
@@ -5300,7 +5510,7 @@ function buildApp(opts) {
5300
5510
  const file = await readFileResponse(target);
5301
5511
  if (file) return file;
5302
5512
  }
5303
- const index = await readFileResponse(join9(root, "index.html"));
5513
+ const index = await readFileResponse(join10(root, "index.html"));
5304
5514
  if (index) return index;
5305
5515
  return c.notFound();
5306
5516
  });