{
  "always_run_in_app": false,
  "icon": {
    "color": "deep-green",
    "glyph": "key"
  },
  "name": "Nicely Done Password",
  "script": "// Nicely Done — Password widget for Scriptable (https://scriptable.app)\n// https://nicelydone.qd.je/widgets/\n//\n// Tap the widget: a strong password is generated on your iPhone and copied to the\n// clipboard. The widget itself never shows a password.\n// Widget parameter (optional): a length from 12 to 64 (default 20), e.g. \"24\".\n//\n// Randomness: iOS UUIDs (UUID.string()) come from the system's cryptographic random\n// generator. We keep only their 120 fully random bits each and pick characters with\n// rejection sampling, so every character is equally likely.\n\nconst AR = String(Device.language() || \"\").toLowerCase().startsWith(\"ar\");\nconst C = {\n  bg: Color.dynamic(new Color(\"#f5f3ee\"), new Color(\"#0b0c0b\")),\n  text: Color.dynamic(new Color(\"#161613\"), new Color(\"#ecebe6\")),\n  muted: Color.dynamic(new Color(\"#5b5a54\"), new Color(\"#a6a59f\")),\n  accent: Color.dynamic(new Color(\"#0f8a5c\"), new Color(\"#7ee2b0\")),\n  chip: Color.dynamic(new Color(\"#e3efe8\"), new Color(\"#15271f\")),\n};\nconst CHARSET = \"ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789!@#$%&*-_=+?\";\n\nfunction randomBytes(n) {\n  const out = [];\n  while (out.length < n) {\n    const hex = UUID.string().replace(/-/g, \"\").toLowerCase();\n    // drop the version nibble (index 12) and the variant nibble (index 16)\n    const nib = hex.split(\"\").filter((_, i) => i !== 12 && i !== 16).map((c) => parseInt(c, 16));\n    for (let i = 0; i + 1 < nib.length; i += 2) out.push(nib[i] * 16 + nib[i + 1]);\n  }\n  return out.slice(0, n);\n}\nfunction randInt(max) {\n  const limit = Math.floor(256 / max) * max;\n  for (;;) { const b = randomBytes(1)[0]; if (b < limit) return b % max; }\n}\nfunction password(len) {\n  const sets = [\"ABCDEFGHJKLMNPQRSTUVWXYZ\", \"abcdefghijkmnopqrstuvwxyz\", \"23456789\", \"!@#$%&*-_=+?\"];\n  const chars = sets.map((s) => s[randInt(s.length)]);           // at least one of each\n  while (chars.length < len) chars.push(CHARSET[randInt(CHARSET.length)]);\n  for (let i = chars.length - 1; i > 0; i--) { const j = randInt(i + 1); [chars[i], chars[j]] = [chars[j], chars[i]]; }\n  return chars.join(\"\");\n}\n\nconst len = Math.min(64, Math.max(12, parseInt(args.widgetParameter || args.queryParameters.length || \"20\", 10) || 20));\n\nif (config.runsInWidget) {\n  const w = new ListWidget();\n  w.backgroundColor = C.bg;\n  w.setPadding(14, 16, 14, 16);\n  w.url = \"scriptable:///run/\" + encodeURIComponent(Script.name()) + \"?length=\" + len;\n  const fam = config.widgetFamily;\n  if (fam && fam.startsWith(\"accessory\")) {\n    w.addAccessoryWidgetBackground = true;\n    const img = w.addImage(SFSymbol.named(\"key.fill\").image); img.imageSize = new Size(18, 18); img.centerAlignImage();\n    if (fam !== \"accessoryCircular\") { const t = w.addText(AR ? \"كلمة مرور جديدة\" : \"New password\"); t.font = Font.semiboldSystemFont(12); t.centerAlignText(); }\n  } else {\n    const top = w.addStack(); top.layoutHorizontally(); top.centerAlignContent();\n    const badge = top.addStack(); badge.backgroundColor = C.chip; badge.cornerRadius = 9; badge.setPadding(6, 6, 6, 6);\n    const img = badge.addImage(SFSymbol.named(\"key.fill\").image); img.imageSize = new Size(16, 16); img.tintColor = C.accent;\n    w.addSpacer();\n    const t = w.addText(AR ? \"كلمة مرور جديدة\" : \"New password\"); t.font = new Font(\"Georgia\", 20); t.textColor = C.text; t.minimumScaleFactor = 0.7;\n    w.addSpacer(2);\n    const s = w.addText(AR ? \"اضغط لنسخ كلمة قوية (\" + len + \" حرفًا)\" : \"Tap to copy a strong one · \" + len + \" chars\");\n    s.font = Font.systemFont(11); s.textColor = C.muted;\n    if (AR) { t.rightAlignText(); s.rightAlignText(); }\n  }\n  Script.setWidget(w);\n} else {\n  const pw = password(len);\n  Pasteboard.copyString(pw);\n  const a = new Alert();\n  a.title = AR ? \"تم نسخ كلمة المرور\" : \"Password copied\";\n  a.message = pw + \"\\n\\n\" + (AR ? \"الصقها في مدير كلمات المرور.\" : \"Paste it into your password manager.\");\n  a.addAction(AR ? \"تم\" : \"Done\");\n  await a.presentAlert();\n}\nScript.complete();\n",
  "share_sheet_inputs": []
}