{
  "format": "eleckoi.character-card",
  "version": 1,
  "character": {
    "name": "原生 SDK 示例角色",
    "group": "SDK 示例",
    "opening": "<!doctype html>\n<html lang=\"zh-CN\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n  <meta name=\"eleckoi-author-api\" content=\"0.1.0\">\n  <title>积分对话</title>\n  <style>\n    body { margin: 16px; font: 14px/1.6 system-ui; color: #202124; background: #fff; }\n    main { max-width: 760px; margin: auto; }\n    form { display: grid; gap: 12px; }\n    label { display: grid; gap: 4px; }\n    input, select, button { box-sizing: border-box; max-width: 100%; min-height: 40px; font: inherit; padding: 8px; }\n    .actions { display: flex; flex-wrap: wrap; gap: 8px; }\n    output, li { overflow-wrap: anywhere; white-space: pre-wrap; }\n    ol { padding-left: 24px; }\n    h1 { font-size: 20px; } h2 { font-size: 16px; }\n  </style>\n</head>\n<body>\n  <main>\n    <h1>积分对话</h1>\n    <form>\n      <label>开场白<select id=\"opening\" disabled></select></label>\n      <label>积分<input id=\"score\" type=\"number\" min=\"0\" value=\"5\" required disabled></label>\n      <label>消息<input id=\"message\" value=\"继续探索\" required disabled></label>\n      <div class=\"actions\">\n        <button type=\"button\" id=\"update\" disabled>更新积分</button>\n        <button type=\"button\" id=\"draft\" disabled>填写输入框</button>\n        <button id=\"send\" disabled>确认并发送</button>\n        <button type=\"button\" id=\"stop\" disabled>停止生成</button>\n      </div>\n      <output id=\"state\"></output>\n      <output id=\"status\" role=\"status\"></output>\n    </form>\n    <h2>已加载消息</h2>\n    <ol id=\"messages\"></ol>\n  </main>\n  <script type=\"module\">\n    const form = document.querySelector('form'), status = document.querySelector('#status');\n    const score = document.querySelector('#score'), message = document.querySelector('#message');\n    const controls = [...form.querySelectorAll('button, input, select')];\n    try {\n      const api = await window.ElecKoiReady;\n      const opening = document.querySelector('#opening');\n      const options = await api.openings.list(), current = await api.openings.current();\n      for (const item of options.items) {\n        const option = document.createElement('option'); option.value = item.id; option.textContent = item.title;\n        opening.append(option);\n      }\n      opening.value = current?.id ?? '';\n      async function refresh() {\n        document.querySelector('#state').textContent = JSON.stringify(await api.variables.getState(), null, 2);\n        const rows = await api.messages.list();\n        document.querySelector('#messages').replaceChildren(...rows.map(row => {\n          const li = document.createElement('li'); li.textContent = `${row.role}: ${row.content}`; return li;\n        }));\n      }\n      async function perform(action) {\n        controls.forEach(control => { control.disabled = true; });\n        document.querySelector('#stop').disabled = false;\n        status.textContent = '';\n        try { await action(); await refresh(); }\n        catch (error) { status.textContent = error.message; }\n        finally { controls.forEach(control => { control.disabled = false; }); }\n      }\n      const acceptScore = () => api.variables.merge({ score: Number(score.value) });\n      document.querySelector('#update').onclick = () => perform(acceptScore);\n      document.querySelector('#draft').onclick = () => perform(() => api.input.set(message.value));\n      document.querySelector('#stop').onclick = async () => {\n        try { await api.chat.stopGeneration(); } catch (error) { status.textContent = error.message; }\n      };\n      opening.onchange = () => perform(() => api.openings.select(opening.value));\n      form.onsubmit = event => {\n        event.preventDefault();\n        void perform(async () => {\n          const accepted = await acceptScore();\n          await api.chat.send(`${message.value}。当前积分 ${accepted.score}，等级 ${accepted.level}。`);\n        });\n      };\n      const stopEvents = api.events.on('messages.changed', () => { void refresh().catch(error => { status.textContent = error.message; }); });\n      window.addEventListener('pagehide', stopEvents, { once: true });\n      await refresh();\n      controls.forEach(control => { control.disabled = false; });\n    } catch (error) { status.textContent = error.message; }\n  </script>\n</body>\n</html>\n",
    "show_opening": true,
    "frontend_beauty_enabled": true
  },
  "assets": [],
  "regex_rules": [],
  "setting_library": "{\"format\":\"eleckoi.setting-library-snapshot\",\"version\":1,\"active_version_id\":\"example-settings\",\"versions\":[{\"id\":\"example-settings\",\"name\":\"示例设定\",\"entries\":[{\"id\":\"example-opening\",\"kind\":\"opening\",\"title\":\"开场白\",\"content\":\"<!doctype html>\\n<html lang=\\\"zh-CN\\\">\\n<head>\\n  <meta charset=\\\"utf-8\\\">\\n  <meta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1\\\">\\n  <meta name=\\\"eleckoi-author-api\\\" content=\\\"0.1.0\\\">\\n  <title>积分对话</title>\\n  <style>\\n    body { margin: 16px; font: 14px/1.6 system-ui; color: #202124; background: #fff; }\\n    main { max-width: 760px; margin: auto; }\\n    form { display: grid; gap: 12px; }\\n    label { display: grid; gap: 4px; }\\n    input, select, button { box-sizing: border-box; max-width: 100%; min-height: 40px; font: inherit; padding: 8px; }\\n    .actions { display: flex; flex-wrap: wrap; gap: 8px; }\\n    output, li { overflow-wrap: anywhere; white-space: pre-wrap; }\\n    ol { padding-left: 24px; }\\n    h1 { font-size: 20px; } h2 { font-size: 16px; }\\n  </style>\\n</head>\\n<body>\\n  <main>\\n    <h1>积分对话</h1>\\n    <form>\\n      <label>开场白<select id=\\\"opening\\\" disabled></select></label>\\n      <label>积分<input id=\\\"score\\\" type=\\\"number\\\" min=\\\"0\\\" value=\\\"5\\\" required disabled></label>\\n      <label>消息<input id=\\\"message\\\" value=\\\"继续探索\\\" required disabled></label>\\n      <div class=\\\"actions\\\">\\n        <button type=\\\"button\\\" id=\\\"update\\\" disabled>更新积分</button>\\n        <button type=\\\"button\\\" id=\\\"draft\\\" disabled>填写输入框</button>\\n        <button id=\\\"send\\\" disabled>确认并发送</button>\\n        <button type=\\\"button\\\" id=\\\"stop\\\" disabled>停止生成</button>\\n      </div>\\n      <output id=\\\"state\\\"></output>\\n      <output id=\\\"status\\\" role=\\\"status\\\"></output>\\n    </form>\\n    <h2>已加载消息</h2>\\n    <ol id=\\\"messages\\\"></ol>\\n  </main>\\n  <script type=\\\"module\\\">\\n    const form = document.querySelector('form'), status = document.querySelector('#status');\\n    const score = document.querySelector('#score'), message = document.querySelector('#message');\\n    const controls = [...form.querySelectorAll('button, input, select')];\\n    try {\\n      const api = await window.ElecKoiReady;\\n      const opening = document.querySelector('#opening');\\n      const options = await api.openings.list(), current = await api.openings.current();\\n      for (const item of options.items) {\\n        const option = document.createElement('option'); option.value = item.id; option.textContent = item.title;\\n        opening.append(option);\\n      }\\n      opening.value = current?.id ?? '';\\n      async function refresh() {\\n        document.querySelector('#state').textContent = JSON.stringify(await api.variables.getState(), null, 2);\\n        const rows = await api.messages.list();\\n        document.querySelector('#messages').replaceChildren(...rows.map(row => {\\n          const li = document.createElement('li'); li.textContent = `${row.role}: ${row.content}`; return li;\\n        }));\\n      }\\n      async function perform(action) {\\n        controls.forEach(control => { control.disabled = true; });\\n        document.querySelector('#stop').disabled = false;\\n        status.textContent = '';\\n        try { await action(); await refresh(); }\\n        catch (error) { status.textContent = error.message; }\\n        finally { controls.forEach(control => { control.disabled = false; }); }\\n      }\\n      const acceptScore = () => api.variables.merge({ score: Number(score.value) });\\n      document.querySelector('#update').onclick = () => perform(acceptScore);\\n      document.querySelector('#draft').onclick = () => perform(() => api.input.set(message.value));\\n      document.querySelector('#stop').onclick = async () => {\\n        try { await api.chat.stopGeneration(); } catch (error) { status.textContent = error.message; }\\n      };\\n      opening.onchange = () => perform(() => api.openings.select(opening.value));\\n      form.onsubmit = event => {\\n        event.preventDefault();\\n        void perform(async () => {\\n          const accepted = await acceptScore();\\n          await api.chat.send(`${message.value}。当前积分 ${accepted.score}，等级 ${accepted.level}。`);\\n        });\\n      };\\n      const stopEvents = api.events.on('messages.changed', () => { void refresh().catch(error => { status.textContent = error.message; }); });\\n      window.addEventListener('pagehide', stopEvents, { once: true });\\n      await refresh();\\n      controls.forEach(control => { control.disabled = false; });\\n    } catch (error) { status.textContent = error.message; }\\n  </script>\\n</body>\\n</html>\\n\",\"enabled\":true,\"trigger_mode\":null,\"position\":null,\"insert_role\":\"assistant\",\"opening_messages\":[{\"id\":\"example-start\",\"title\":\"从零开始\",\"content\":\"<!doctype html>\\n<html lang=\\\"zh-CN\\\">\\n<head>\\n  <meta charset=\\\"utf-8\\\">\\n  <meta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1\\\">\\n  <meta name=\\\"eleckoi-author-api\\\" content=\\\"0.1.0\\\">\\n  <title>积分对话</title>\\n  <style>\\n    body { margin: 16px; font: 14px/1.6 system-ui; color: #202124; background: #fff; }\\n    main { max-width: 760px; margin: auto; }\\n    form { display: grid; gap: 12px; }\\n    label { display: grid; gap: 4px; }\\n    input, select, button { box-sizing: border-box; max-width: 100%; min-height: 40px; font: inherit; padding: 8px; }\\n    .actions { display: flex; flex-wrap: wrap; gap: 8px; }\\n    output, li { overflow-wrap: anywhere; white-space: pre-wrap; }\\n    ol { padding-left: 24px; }\\n    h1 { font-size: 20px; } h2 { font-size: 16px; }\\n  </style>\\n</head>\\n<body>\\n  <main>\\n    <h1>积分对话</h1>\\n    <form>\\n      <label>开场白<select id=\\\"opening\\\" disabled></select></label>\\n      <label>积分<input id=\\\"score\\\" type=\\\"number\\\" min=\\\"0\\\" value=\\\"5\\\" required disabled></label>\\n      <label>消息<input id=\\\"message\\\" value=\\\"继续探索\\\" required disabled></label>\\n      <div class=\\\"actions\\\">\\n        <button type=\\\"button\\\" id=\\\"update\\\" disabled>更新积分</button>\\n        <button type=\\\"button\\\" id=\\\"draft\\\" disabled>填写输入框</button>\\n        <button id=\\\"send\\\" disabled>确认并发送</button>\\n        <button type=\\\"button\\\" id=\\\"stop\\\" disabled>停止生成</button>\\n      </div>\\n      <output id=\\\"state\\\"></output>\\n      <output id=\\\"status\\\" role=\\\"status\\\"></output>\\n    </form>\\n    <h2>已加载消息</h2>\\n    <ol id=\\\"messages\\\"></ol>\\n  </main>\\n  <script type=\\\"module\\\">\\n    const form = document.querySelector('form'), status = document.querySelector('#status');\\n    const score = document.querySelector('#score'), message = document.querySelector('#message');\\n    const controls = [...form.querySelectorAll('button, input, select')];\\n    try {\\n      const api = await window.ElecKoiReady;\\n      const opening = document.querySelector('#opening');\\n      const options = await api.openings.list(), current = await api.openings.current();\\n      for (const item of options.items) {\\n        const option = document.createElement('option'); option.value = item.id; option.textContent = item.title;\\n        opening.append(option);\\n      }\\n      opening.value = current?.id ?? '';\\n      async function refresh() {\\n        document.querySelector('#state').textContent = JSON.stringify(await api.variables.getState(), null, 2);\\n        const rows = await api.messages.list();\\n        document.querySelector('#messages').replaceChildren(...rows.map(row => {\\n          const li = document.createElement('li'); li.textContent = `${row.role}: ${row.content}`; return li;\\n        }));\\n      }\\n      async function perform(action) {\\n        controls.forEach(control => { control.disabled = true; });\\n        document.querySelector('#stop').disabled = false;\\n        status.textContent = '';\\n        try { await action(); await refresh(); }\\n        catch (error) { status.textContent = error.message; }\\n        finally { controls.forEach(control => { control.disabled = false; }); }\\n      }\\n      const acceptScore = () => api.variables.merge({ score: Number(score.value) });\\n      document.querySelector('#update').onclick = () => perform(acceptScore);\\n      document.querySelector('#draft').onclick = () => perform(() => api.input.set(message.value));\\n      document.querySelector('#stop').onclick = async () => {\\n        try { await api.chat.stopGeneration(); } catch (error) { status.textContent = error.message; }\\n      };\\n      opening.onchange = () => perform(() => api.openings.select(opening.value));\\n      form.onsubmit = event => {\\n        event.preventDefault();\\n        void perform(async () => {\\n          const accepted = await acceptScore();\\n          await api.chat.send(`${message.value}。当前积分 ${accepted.score}，等级 ${accepted.level}。`);\\n        });\\n      };\\n      const stopEvents = api.events.on('messages.changed', () => { void refresh().catch(error => { status.textContent = error.message; }); });\\n      window.addEventListener('pagehide', stopEvents, { once: true });\\n      await refresh();\\n      controls.forEach(control => { control.disabled = false; });\\n    } catch (error) { status.textContent = error.message; }\\n  </script>\\n</body>\\n</html>\\n\",\"initial_variable_state\":\"\",\"variable_version_id\":\"example-variables\"},{\"id\":\"example-advanced\",\"title\":\"从十开始\",\"content\":\"<!doctype html>\\n<html lang=\\\"zh-CN\\\">\\n<head>\\n  <meta charset=\\\"utf-8\\\">\\n  <meta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1\\\">\\n  <meta name=\\\"eleckoi-author-api\\\" content=\\\"0.1.0\\\">\\n  <title>积分对话</title>\\n  <style>\\n    body { margin: 16px; font: 14px/1.6 system-ui; color: #202124; background: #fff; }\\n    main { max-width: 760px; margin: auto; }\\n    form { display: grid; gap: 12px; }\\n    label { display: grid; gap: 4px; }\\n    input, select, button { box-sizing: border-box; max-width: 100%; min-height: 40px; font: inherit; padding: 8px; }\\n    .actions { display: flex; flex-wrap: wrap; gap: 8px; }\\n    output, li { overflow-wrap: anywhere; white-space: pre-wrap; }\\n    ol { padding-left: 24px; }\\n    h1 { font-size: 20px; } h2 { font-size: 16px; }\\n  </style>\\n</head>\\n<body>\\n  <main>\\n    <h1>积分对话</h1>\\n    <form>\\n      <label>开场白<select id=\\\"opening\\\" disabled></select></label>\\n      <label>积分<input id=\\\"score\\\" type=\\\"number\\\" min=\\\"0\\\" value=\\\"5\\\" required disabled></label>\\n      <label>消息<input id=\\\"message\\\" value=\\\"继续探索\\\" required disabled></label>\\n      <div class=\\\"actions\\\">\\n        <button type=\\\"button\\\" id=\\\"update\\\" disabled>更新积分</button>\\n        <button type=\\\"button\\\" id=\\\"draft\\\" disabled>填写输入框</button>\\n        <button id=\\\"send\\\" disabled>确认并发送</button>\\n        <button type=\\\"button\\\" id=\\\"stop\\\" disabled>停止生成</button>\\n      </div>\\n      <output id=\\\"state\\\"></output>\\n      <output id=\\\"status\\\" role=\\\"status\\\"></output>\\n    </form>\\n    <h2>已加载消息</h2>\\n    <ol id=\\\"messages\\\"></ol>\\n  </main>\\n  <script type=\\\"module\\\">\\n    const form = document.querySelector('form'), status = document.querySelector('#status');\\n    const score = document.querySelector('#score'), message = document.querySelector('#message');\\n    const controls = [...form.querySelectorAll('button, input, select')];\\n    try {\\n      const api = await window.ElecKoiReady;\\n      const opening = document.querySelector('#opening');\\n      const options = await api.openings.list(), current = await api.openings.current();\\n      for (const item of options.items) {\\n        const option = document.createElement('option'); option.value = item.id; option.textContent = item.title;\\n        opening.append(option);\\n      }\\n      opening.value = current?.id ?? '';\\n      async function refresh() {\\n        document.querySelector('#state').textContent = JSON.stringify(await api.variables.getState(), null, 2);\\n        const rows = await api.messages.list();\\n        document.querySelector('#messages').replaceChildren(...rows.map(row => {\\n          const li = document.createElement('li'); li.textContent = `${row.role}: ${row.content}`; return li;\\n        }));\\n      }\\n      async function perform(action) {\\n        controls.forEach(control => { control.disabled = true; });\\n        document.querySelector('#stop').disabled = false;\\n        status.textContent = '';\\n        try { await action(); await refresh(); }\\n        catch (error) { status.textContent = error.message; }\\n        finally { controls.forEach(control => { control.disabled = false; }); }\\n      }\\n      const acceptScore = () => api.variables.merge({ score: Number(score.value) });\\n      document.querySelector('#update').onclick = () => perform(acceptScore);\\n      document.querySelector('#draft').onclick = () => perform(() => api.input.set(message.value));\\n      document.querySelector('#stop').onclick = async () => {\\n        try { await api.chat.stopGeneration(); } catch (error) { status.textContent = error.message; }\\n      };\\n      opening.onchange = () => perform(() => api.openings.select(opening.value));\\n      form.onsubmit = event => {\\n        event.preventDefault();\\n        void perform(async () => {\\n          const accepted = await acceptScore();\\n          await api.chat.send(`${message.value}。当前积分 ${accepted.score}，等级 ${accepted.level}。`);\\n        });\\n      };\\n      const stopEvents = api.events.on('messages.changed', () => { void refresh().catch(error => { status.textContent = error.message; }); });\\n      window.addEventListener('pagehide', stopEvents, { once: true });\\n      await refresh();\\n      controls.forEach(control => { control.disabled = false; });\\n    } catch (error) { status.textContent = error.message; }\\n  </script>\\n</body>\\n</html>\\n\",\"initial_variable_state\":\"\",\"variable_version_id\":\"example-advanced-variables\"}],\"default_opening_message_id\":\"example-start\"}],\"groups\":[],\"prompt_positions\":[],\"list_all_expanded\":true,\"expanded_group_ids\":[]}]}",
  "variable_config": "{\"format\":\"eleckoi.variable-config\",\"version\":1,\"name\":\"积分变量\",\"active_version_id\":\"example-variables\",\"versions\":[{\"id\":\"example-variables\",\"name\":\"从零开始\",\"initial_state\":{\"score\":0,\"total\":0,\"level\":1},\"schema_code\":\"z.looseObject({ score: z.number().min(0), total: z.number(), level: z.number().int().min(1) })\",\"objects\":[],\"variables\":[{\"id\":\"example-score\",\"title\":\"score\",\"object_id\":\"\",\"enabled\":true,\"type\":\"number\",\"default_value\":\"0\",\"description\":\"合成积分字段\",\"update_rule\":\"按剧情修改积分，其他字段由作者规则计算。\",\"read_mode\":\"required\",\"order\":1,\"tree_view_order\":1},{\"id\":\"example-total\",\"title\":\"total\",\"object_id\":\"\",\"enabled\":true,\"type\":\"number\",\"default_value\":\"0\",\"description\":\"合成积分字段\",\"update_rule\":\"由作者规则计算。\",\"read_mode\":\"required\",\"order\":2,\"tree_view_order\":2},{\"id\":\"example-level\",\"title\":\"level\",\"object_id\":\"\",\"enabled\":true,\"type\":\"number\",\"default_value\":\"1\",\"description\":\"合成积分字段\",\"update_rule\":\"由作者规则计算。\",\"read_mode\":\"required\",\"order\":3,\"tree_view_order\":3}],\"expanded_object_ids\":[]},{\"id\":\"example-advanced-variables\",\"name\":\"从十开始\",\"initial_state\":{\"score\":10,\"total\":20,\"level\":2},\"schema_code\":\"z.looseObject({ score: z.number().min(0), total: z.number(), level: z.number().int().min(1) })\",\"objects\":[],\"variables\":[{\"id\":\"example-score\",\"title\":\"score\",\"object_id\":\"\",\"enabled\":true,\"type\":\"number\",\"default_value\":\"10\",\"description\":\"合成积分字段\",\"update_rule\":\"按剧情修改积分，其他字段由作者规则计算。\",\"read_mode\":\"required\",\"order\":1,\"tree_view_order\":1},{\"id\":\"example-total\",\"title\":\"total\",\"object_id\":\"\",\"enabled\":true,\"type\":\"number\",\"default_value\":\"20\",\"description\":\"合成积分字段\",\"update_rule\":\"由作者规则计算。\",\"read_mode\":\"required\",\"order\":2,\"tree_view_order\":2},{\"id\":\"example-level\",\"title\":\"level\",\"object_id\":\"\",\"enabled\":true,\"type\":\"number\",\"default_value\":\"2\",\"description\":\"合成积分字段\",\"update_rule\":\"由作者规则计算。\",\"read_mode\":\"required\",\"order\":3,\"tree_view_order\":3}],\"expanded_object_ids\":[]}]}",
  "script_library": {
    "enabled": false,
    "variables": {},
    "trees": [
      {
        "type": "script",
        "id": "example-score-rule",
        "name": "积分规则",
        "enabled": false,
        "content": "import { derive } from '../derive.js';\n\nexport default context => {\n  const stopRule = context.api.variables.registerRule({\n    id: 'derive-score',\n    stage: 'afterSchema',\n    async run(candidate) {\n      await Promise.resolve();\n      candidate.signal.throwIfAborted();\n      Object.assign(candidate.state, derive(candidate.state.score, context.settings.multiplier ?? 2));\n    },\n  });\n  const stopButton = context.api.ui.registerButton({\n    id: 'settings',\n    label: '积分设置',\n    async onClick({ api, signal }) {\n      const response = await fetch(new URL('../settings.html', import.meta.url), { signal });\n      if (!response.ok) throw new Error(`读取设置页面失败：${response.status}`);\n      await api.ui.openPanel({ id: 'settings', title: '积分设置', html: await response.text() });\n    },\n  });\n  return () => { stopButton(); stopRule(); };\n};\n",
        "info": "",
        "data": {
          "multiplier": 2
        },
        "button": {
          "enabled": true,
          "buttons": []
        },
        "export_with": {
          "data": true,
          "button": true
        },
        "runtime": {
          "kind": "eleckoi-module",
          "apiVersion": 1
        }
      }
    ],
    "files": [
      {
        "path": "derive.js",
        "data": "ZXhwb3J0IGZ1bmN0aW9uIGRlcml2ZShzY29yZSwgbXVsdGlwbGllcikgewogIGlmICghTnVtYmVyLmlzRmluaXRlKHNjb3JlKSB8fCBzY29yZSA8IDApIHRocm93IG5ldyBFcnJvcignc2NvcmUg5b+F6aG75piv6Z2e6LSf5pyJ6ZmQ5pWw5a2XJyk7CiAgaWYgKCFOdW1iZXIuaXNGaW5pdGUobXVsdGlwbGllcikgfHwgbXVsdGlwbGllciA8PSAwKSB0aHJvdyBuZXcgRXJyb3IoJ+WAjeeOh+W/hemhu+aYr+ato+aciemZkOaVsOWtlycpOwogIHJldHVybiB7IHRvdGFsOiBzY29yZSAqIG11bHRpcGxpZXIsIGxldmVsOiBNYXRoLmZsb29yKHNjb3JlIC8gMTApICsgMSB9Owp9Cg=="
      },
      {
        "path": "settings.html",
        "data": "PCFkb2N0eXBlIGh0bWw+CjxodG1sIGxhbmc9InpoLUNOIj4KPGhlYWQ+CiAgPG1ldGEgY2hhcnNldD0idXRmLTgiPgogIDxtZXRhIG5hbWU9InZpZXdwb3J0IiBjb250ZW50PSJ3aWR0aD1kZXZpY2Utd2lkdGgsIGluaXRpYWwtc2NhbGU9MSI+CiAgPG1ldGEgbmFtZT0iZWxlY2tvaS1hdXRob3ItYXBpIiBjb250ZW50PSIwLjEuMCI+CiAgPHRpdGxlPuenr+WIhuiuvue9rjwvdGl0bGU+CiAgPHN0eWxlPgogICAgYm9keSB7IG1hcmdpbjogMjBweDsgZm9udDogMTRweC8xLjYgc3lzdGVtLXVpOyBjb2xvcjogIzIwMjEyNDsgYmFja2dyb3VuZDogI2ZmZjsgfQogICAgZm9ybSB7IGRpc3BsYXk6IGdyaWQ7IGdhcDogMTZweDsgbWF4LXdpZHRoOiA0MjBweDsgfQogICAgbGFiZWwgeyBkaXNwbGF5OiBncmlkOyBnYXA6IDRweDsgfQogICAgaW5wdXQsIGJ1dHRvbiB7IGJveC1zaXppbmc6IGJvcmRlci1ib3g7IGZvbnQ6IGluaGVyaXQ7IHBhZGRpbmc6IDhweDsgbWluLWhlaWdodDogNDBweDsgfQogICAgb3V0cHV0IHsgZGlzcGxheTogYmxvY2s7IG92ZXJmbG93LXdyYXA6IGFueXdoZXJlOyB9CiAgPC9zdHlsZT4KPC9oZWFkPgo8Ym9keT4KICA8Zm9ybT4KICAgIDxsYWJlbD7np6/liIblgI3njoc8aW5wdXQgaWQ9Im11bHRpcGxpZXIiIHR5cGU9Im51bWJlciIgbWluPSIwLjEiIHN0ZXA9IjAuMSIgcmVxdWlyZWQgZGlzYWJsZWQ+PC9sYWJlbD4KICAgIDxidXR0b24gZGlzYWJsZWQ+5L+d5a2YPC9idXR0b24+CiAgICA8b3V0cHV0IHJvbGU9InN0YXR1cyI+PC9vdXRwdXQ+CiAgPC9mb3JtPgogIDxzY3JpcHQgdHlwZT0ibW9kdWxlIj4KICAgIGNvbnN0IGZvcm0gPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCdmb3JtJyksIGZpZWxkID0gZG9jdW1lbnQucXVlcnlTZWxlY3RvcignaW5wdXQnKTsKICAgIGNvbnN0IGJ1dHRvbiA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJ2J1dHRvbicpLCBvdXRwdXQgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCdvdXRwdXQnKTsKICAgIHRyeSB7CiAgICAgIGNvbnN0IGFwaSA9IGF3YWl0IHdpbmRvdy5FbGVjS29pUmVhZHk7CiAgICAgIGxldCBzZXR0aW5ncyA9IGF3YWl0IGFwaS5zY3JpcHQuZ2V0U2V0dGluZ3MoKTsKICAgICAgZmllbGQudmFsdWUgPSBzZXR0aW5ncy52YWx1ZXMubXVsdGlwbGllciA/PyAyOwogICAgICBmaWVsZC5kaXNhYmxlZCA9IGJ1dHRvbi5kaXNhYmxlZCA9IGZhbHNlOwogICAgICBmb3JtLmFkZEV2ZW50TGlzdGVuZXIoJ3N1Ym1pdCcsIGFzeW5jIGV2ZW50ID0+IHsKICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpOyBidXR0b24uZGlzYWJsZWQgPSB0cnVlOwogICAgICAgIHRyeSB7CiAgICAgICAgICBjb25zdCBtdWx0aXBsaWVyID0gTnVtYmVyKGZpZWxkLnZhbHVlKTsKICAgICAgICAgIGlmICghTnVtYmVyLmlzRmluaXRlKG11bHRpcGxpZXIpIHx8IG11bHRpcGxpZXIgPD0gMCkgdGhyb3cgbmV3IEVycm9yKCflgI3njoflv4XpobvmmK/mraPmnInpmZDmlbDlrZcnKTsKICAgICAgICAgIHNldHRpbmdzID0gYXdhaXQgYXBpLnNjcmlwdC5zZXRTZXR0aW5ncyh7IC4uLnNldHRpbmdzLnZhbHVlcywgbXVsdGlwbGllciB9LCBzZXR0aW5ncy5yZXZpc2lvbik7CiAgICAgICAgICBvdXRwdXQudGV4dENvbnRlbnQgPSAn5bey5L+d5a2YJzsKICAgICAgICB9IGNhdGNoIChlcnJvcikgewogICAgICAgICAgb3V0cHV0LnRleHRDb250ZW50ID0gZXJyb3IubWVzc2FnZTsKICAgICAgICAgIHRyeSB7IHNldHRpbmdzID0gYXdhaXQgYXBpLnNjcmlwdC5nZXRTZXR0aW5ncygpOyB9CiAgICAgICAgICBjYXRjaCAocmVmcmVzaEVycm9yKSB7IG91dHB1dC50ZXh0Q29udGVudCArPSBg77yb6YeN5paw6K+75Y+W5aSx6LSl77yaJHtyZWZyZXNoRXJyb3IubWVzc2FnZX1gOyB9CiAgICAgICAgfSBmaW5hbGx5IHsgYnV0dG9uLmRpc2FibGVkID0gZmFsc2U7IH0KICAgICAgfSk7CiAgICB9IGNhdGNoIChlcnJvcikgeyBvdXRwdXQudGV4dENvbnRlbnQgPSBlcnJvci5tZXNzYWdlOyB9CiAgPC9zY3JpcHQ+CjwvYm9keT4KPC9odG1sPgo="
      }
    ]
  }
}
