DibaAi commited on
Commit
fd7b0ad
·
verified ·
1 Parent(s): 58dac6f

Self-hosted UI assets, polished markdown, load public DibaAi/Diba-Base

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ vendor/fonts/Vazirmatn-Variable.woff2 filter=lfs diff=lfs merge=lfs -text
app.py CHANGED
@@ -14,7 +14,7 @@ import spaces
14
  import torch
15
  from transformers import AutoModelForCausalLM, AutoTokenizer, StoppingCriteria, StoppingCriteriaList, TextIteratorStreamer
16
 
17
- MODEL_ID = os.environ.get("DIBA_MODEL", "DibaAi/Diba-Base-staging-private")
18
  TOKEN = os.environ.get("HF_TOKEN")
19
  HERE = Path(__file__).parent
20
 
@@ -103,8 +103,10 @@ def ask(message, temperature=0.0, max_new_tokens=768):
103
 
104
  UI_CSS = (HERE / "diba-ui.css").read_text(encoding="utf-8")
105
  UI_JS = (HERE / "diba-ui.js").read_text(encoding="utf-8")
 
 
106
  HEAD = ('<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">'
107
- f"<style>{UI_CSS}</style><script>window.DIBA_BACKEND = 'gradio';</script><script>{UI_JS}</script>")
108
 
109
  with gr.Blocks(title="Diba · دیبا", head=HEAD, css="footer{display:none!important}") as demo:
110
  gr.HTML('<div style="padding:40px;text-align:center;font-family:Tahoma">در حال بارگذاری دیبا… · Loading Diba…</div>')
 
14
  import torch
15
  from transformers import AutoModelForCausalLM, AutoTokenizer, StoppingCriteria, StoppingCriteriaList, TextIteratorStreamer
16
 
17
+ MODEL_ID = os.environ.get("DIBA_MODEL", "DibaAi/Diba-Base")
18
  TOKEN = os.environ.get("HF_TOKEN")
19
  HERE = Path(__file__).parent
20
 
 
103
 
104
  UI_CSS = (HERE / "diba-ui.css").read_text(encoding="utf-8")
105
  UI_JS = (HERE / "diba-ui.js").read_text(encoding="utf-8")
106
+ gr.set_static_paths(paths=[str(HERE / "vendor")]) # self-hosted fonts and libraries, served at /gradio_api/file=...
107
+ ASSET_BASE = "/gradio_api/file=" + str(HERE).replace("\\", "/").rstrip("/") + "/"
108
  HEAD = ('<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">'
109
+ f"<style>{UI_CSS}</style><script>window.DIBA_BACKEND = 'gradio'; window.DIBA_ASSET_BASE = '{ASSET_BASE}';</script><script>{UI_JS}</script>")
110
 
111
  with gr.Blocks(title="Diba · دیبا", head=HEAD, css="footer{display:none!important}") as demo:
112
  gr.HTML('<div style="padding:40px;text-align:center;font-family:Tahoma">در حال بارگذاری دیبا… · Loading Diba…</div>')
diba-ui.css CHANGED
@@ -1,13 +1,15 @@
1
- /* Diba chat UI — shared by the CPU (llama.cpp) and ZeroGPU (Gradio) Spaces */
2
  :root, [data-theme="light"] {
3
- --bg: #FFFFFF; --side: #F6F6F3; --side-hover: #ECECE7; --panel: #FFFFFF; --line: #E6E4DD;
4
- --ink: #1B1F1D; --ink-2: #4C5552; --muted: #8A928E; --bubble: #F1F1EC; --code-bg: #0F1513; --code-head: #1B2421;
5
- --accent: #1F7A5A; --accent-ink: #FFFFFF; --accent-soft: #E3F1EA; --danger: #B3261E; --shadow: 0 6px 24px rgba(20, 30, 25, .08);
 
6
  }
7
  [data-theme="dark"] {
8
- --bg: #1B1D1C; --side: #131514; --side-hover: #232625; --panel: #242726; --line: #2F3331;
9
- --ink: #ECEEEA; --ink-2: #B7BEBA; --muted: #7F8783; --bubble: #2A2E2C; --code-bg: #0B0F0D; --code-head: #161C19;
10
- --accent: #4CB58C; --accent-ink: #0B1511; --accent-soft: #1E3A2F; --danger: #F2B8B5; --shadow: 0 6px 24px rgba(0, 0, 0, .35);
 
11
  }
12
 
13
  html, body { height: 100%; margin: 0; }
@@ -15,28 +17,30 @@ body.diba-lock { overflow: hidden !important; }
15
  #diba-root, #diba-root * { box-sizing: border-box; }
16
  #diba-root {
17
  position: fixed; inset: 0; z-index: 2147483000; display: flex; background: var(--bg); color: var(--ink);
18
- font-family: "Vazirmatn", Tahoma, system-ui, sans-serif; font-size: 15px; line-height: 1.7; -webkit-font-smoothing: antialiased;
 
19
  }
20
  #diba-root button { font-family: inherit; cursor: pointer; }
21
  #diba-root :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 8px; }
 
22
 
23
  /* ---------- sidebar ---------- */
24
  .d-side { width: 268px; flex: 0 0 268px; background: var(--side); display: flex; flex-direction: column; border-inline-end: 1px solid var(--line); transition: margin .2s ease; }
25
  .d-side.closed { margin-inline-start: -268px; }
26
  .d-brand { display: flex; align-items: center; gap: 10px; padding: 16px 16px 10px; }
27
  .d-mark { width: 32px; height: 32px; border-radius: 9px; display: grid; place-items: center; color: #fff; font-weight: 800; font-size: 17px; background: linear-gradient(135deg, #1F7A5A, #C8912E); }
28
- .d-brand-name { font-weight: 800; font-size: 17px; line-height: 1.1; }
29
  .d-brand-sub { font-size: 12px; color: var(--muted); }
30
  .d-new { margin: 6px 12px 10px; display: flex; align-items: center; gap: 8px; padding: 9px 12px; border-radius: 10px; border: 1px solid var(--line); background: var(--panel); color: var(--ink); font-size: 14px; font-weight: 600; }
31
  .d-new:hover { background: var(--side-hover); }
32
  .d-list-title { padding: 8px 18px 4px; font-size: 12px; color: var(--muted); font-weight: 600; }
33
  .d-list { flex: 1; overflow-y: auto; padding: 0 8px 8px; }
34
- .d-conv { display: flex; align-items: center; gap: 6px; padding: 8px 10px; border-radius: 9px; color: var(--ink-2); font-size: 14px; }
35
  .d-conv:hover, .d-conv.active { background: var(--side-hover); color: var(--ink); }
36
  .d-conv-title { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; unicode-bidi: plaintext; text-align: start; }
37
- .d-conv-del { opacity: 0; border: 0; background: transparent; color: var(--muted); padding: 2px 4px; border-radius: 6px; font-size: 14px; }
38
- .d-conv:hover .d-conv-del { opacity: 1; }
39
- .d-conv-del:hover { color: var(--danger); }
40
  .d-empty-list { padding: 10px 18px; color: var(--muted); font-size: 13px; }
41
  .d-side-foot { border-top: 1px solid var(--line); padding: 10px 12px 12px; display: flex; flex-direction: column; gap: 8px; }
42
  .d-seg { display: flex; background: var(--side-hover); border-radius: 10px; padding: 3px; }
@@ -55,42 +59,87 @@ body.diba-lock { overflow: hidden !important; }
55
  .d-badge { font-size: 11px; font-weight: 600; color: var(--accent); background: var(--accent-soft); border-radius: 999px; padding: 2px 8px; }
56
  .d-top-spacer { flex: 1; }
57
 
58
- .d-scroll { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
59
- .d-thread { max-width: 780px; margin: 0 auto; padding: 18px 20px 28px; display: flex; flex-direction: column; gap: 22px; }
60
 
61
  .d-msg { display: flex; gap: 12px; }
62
  .d-msg.user { justify-content: flex-end; }
63
- .d-msg.user .d-bubble { background: var(--bubble); border-radius: 18px; padding: 10px 16px; max-width: 78%; white-space: pre-wrap; word-wrap: break-word; unicode-bidi: plaintext; text-align: start; }
64
- .d-avatar { width: 30px; height: 30px; flex: 0 0 30px; border-radius: 50%; display: grid; place-items: center; font-weight: 800; font-size: 14px; color: #fff; background: linear-gradient(135deg, #1F7A5A, #C8912E); margin-top: 2px; }
65
  .d-body { flex: 1; min-width: 0; }
66
- .d-content { unicode-bidi: plaintext; text-align: start; word-wrap: break-word; }
67
- .d-content > *:first-child { margin-top: 0; }
68
- .d-content > *:last-child { margin-bottom: 0; }
69
- .d-content p, .d-content li { unicode-bidi: plaintext; text-align: start; }
70
- .d-content p { margin: 0 0 .8em; }
71
- .d-content ul, .d-content ol { margin: .2em 0 .9em; padding-inline-start: 1.4em; }
72
- .d-content h1, .d-content h2, .d-content h3 { font-size: 1.08em; margin: 1em 0 .5em; }
73
- .d-content table { border-collapse: collapse; margin: .6em 0 1em; display: block; overflow-x: auto; }
74
- .d-content th, .d-content td { border: 1px solid var(--line); padding: 6px 10px; }
75
- .d-content blockquote { margin: .6em 0; padding-inline-start: 12px; border-inline-start: 3px solid var(--line); color: var(--ink-2); }
76
- .d-content a { color: var(--accent); }
77
- .d-content :not(pre) > code { font-family: "JetBrains Mono", Consolas, monospace; font-size: .88em; background: var(--bubble); padding: 1px 6px; border-radius: 6px; direction: ltr; unicode-bidi: isolate; }
78
- .d-code { direction: ltr; text-align: left; margin: .7em 0 1em; border-radius: 12px; overflow: hidden; background: var(--code-bg); }
79
- .d-code-head { display: flex; align-items: center; justify-content: space-between; padding: 6px 12px; background: var(--code-head); color: #9DB0A8; font-size: 12px; font-family: "JetBrains Mono", Consolas, monospace; }
80
- .d-code-head button { border: 0; background: transparent; color: #9DB0A8; font-size: 12px; padding: 2px 6px; border-radius: 6px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  .d-code-head button:hover { color: #fff; background: rgba(255,255,255,.08); }
82
- .d-code pre { margin: 0; padding: 14px 16px; overflow-x: auto; }
83
- .d-code pre code { font-family: "JetBrains Mono", Consolas, monospace; font-size: 13px; line-height: 1.6; color: #DCE6E1; background: transparent !important; padding: 0 !important; }
84
- .d-cursor::after { content: "▍"; display: inline-block; margin-inline-start: 2px; color: var(--accent); animation: d-blink 1s steps(2) infinite; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  @keyframes d-blink { 50% { opacity: 0; } }
86
- .d-actions { display: flex; gap: 2px; margin-top: 6px; opacity: .85; }
87
- .d-actions button { border: 0; background: transparent; color: var(--muted); font-size: 12.5px; padding: 4px 8px; border-radius: 7px; }
88
  .d-actions button:hover { background: var(--side-hover); color: var(--ink); }
89
  .d-error { color: var(--danger); font-size: 14px; }
90
- .d-typing { display: inline-flex; gap: 4px; padding: 8px 0; }
91
  .d-typing span { width: 7px; height: 7px; border-radius: 50%; background: var(--muted); animation: d-dot 1.2s infinite ease-in-out; }
92
  .d-typing span:nth-child(2) { animation-delay: .15s; } .d-typing span:nth-child(3) { animation-delay: .3s; }
93
  @keyframes d-dot { 0%, 80%, 100% { transform: scale(.6); opacity: .4; } 40% { transform: scale(1); opacity: 1; } }
 
94
 
95
  /* ---------- welcome ---------- */
96
  .d-welcome { max-width: 780px; margin: 0 auto; padding: 12vh 20px 20px; text-align: center; }
@@ -105,13 +154,11 @@ body.diba-lock { overflow: hidden !important; }
105
 
106
  /* ---------- composer ---------- */
107
  .d-compose-wrap { flex: 0 0 auto; padding: 8px 20px 12px; background: linear-gradient(to top, var(--bg) 70%, transparent); }
108
- .d-compose { max-width: 780px; margin: 0 auto; display: flex; align-items: flex-end; gap: 8px; background: var(--panel); border: 1px solid var(--line); border-radius: 22px; padding: 8px 8px 8px 16px; box-shadow: var(--shadow); }
109
  [dir="rtl"] .d-compose { padding: 8px 16px 8px 8px; }
110
- #diba-root .d-compose textarea:focus, #diba-root .d-compose textarea:focus-visible { outline: none !important; box-shadow: none !important; }
111
  .d-compose:focus-within { border-color: var(--accent); }
112
- .d-content ul[dir="rtl"], .d-content ol[dir="rtl"] { padding-inline-start: 0; padding-right: 1.4em; }
113
- .d-content ul[dir="ltr"], .d-content ol[dir="ltr"] { padding-inline-start: 0; padding-left: 1.4em; }
114
- .d-compose textarea { flex: 1; border: 0; outline: none; resize: none; background: transparent; color: var(--ink); font: inherit; font-size: 15px; line-height: 1.6; max-height: 200px; padding: 6px 0; unicode-bidi: plaintext; text-align: start; }
115
  .d-compose textarea::placeholder { color: var(--muted); }
116
  .d-send { width: 38px; height: 38px; flex: 0 0 38px; border-radius: 50%; border: 0; display: grid; place-items: center; background: var(--accent); color: var(--accent-ink); }
117
  .d-send:disabled { opacity: .35; cursor: default; }
@@ -124,6 +171,7 @@ body.diba-lock { overflow: hidden !important; }
124
  .d-pop { position: absolute; top: 52px; inset-inline-end: 12px; width: 280px; background: var(--panel); border: 1px solid var(--line); border-radius: 14px; box-shadow: var(--shadow); padding: 14px; display: none; z-index: 5; }
125
  .d-pop.show { display: block; }
126
  .d-pop label { display: flex; justify-content: space-between; font-size: 13px; color: var(--ink-2); margin: 6px 0 2px; }
 
127
  .d-pop input[type=range] { width: 100%; accent-color: var(--accent); }
128
  .d-pop .d-hint { font-size: 11.5px; color: var(--muted); margin-top: 8px; }
129
 
@@ -134,5 +182,7 @@ body.diba-lock { overflow: hidden !important; }
134
  .d-sugg { grid-template-columns: 1fr; }
135
  .d-welcome { padding-top: 7vh; }
136
  .d-msg.user .d-bubble { max-width: 88%; }
 
 
137
  }
138
- @media (prefers-reduced-motion: reduce) { .d-side { transition: none; } .d-cursor::after, .d-typing span { animation: none; } }
 
1
+ /* Diba chat UI — shared by the CPU (llama.cpp) and ZeroGPU (Gradio) Spaces. All assets self-hosted. */
2
  :root, [data-theme="light"] {
3
+ --bg: #FFFFFF; --side: #F6F6F3; --side-hover: #ECECE7; --panel: #FFFFFF; --line: #E6E4DD; --line-soft: #EFEDE7;
4
+ --ink: #1B1F1D; --ink-2: #4C5552; --muted: #8A928E; --bubble: #F1F1EC; --zebra: #FAFAF7;
5
+ --code-bg: #0F1513; --code-head: #1A2320; --code-ink: #DCE6E1; --code-muted: #8FA39B; --inline-code: #EEEDE6;
6
+ --accent: #1F7A5A; --accent-ink: #FFFFFF; --accent-soft: #E7F3ED; --danger: #B3261E; --shadow: 0 6px 24px rgba(20, 30, 25, .08);
7
  }
8
  [data-theme="dark"] {
9
+ --bg: #1B1D1C; --side: #131514; --side-hover: #232625; --panel: #242726; --line: #313533; --line-soft: #2A2D2C;
10
+ --ink: #ECEEEA; --ink-2: #B7BEBA; --muted: #7F8783; --bubble: #2A2E2C; --zebra: #202322;
11
+ --code-bg: #0B0F0D; --code-head: #151B18; --code-ink: #DCE6E1; --code-muted: #8FA39B; --inline-code: #2C302E;
12
+ --accent: #4CB58C; --accent-ink: #0B1511; --accent-soft: #1C3229; --danger: #F2B8B5; --shadow: 0 6px 24px rgba(0, 0, 0, .35);
13
  }
14
 
15
  html, body { height: 100%; margin: 0; }
 
17
  #diba-root, #diba-root * { box-sizing: border-box; }
18
  #diba-root {
19
  position: fixed; inset: 0; z-index: 2147483000; display: flex; background: var(--bg); color: var(--ink);
20
+ font-family: "Vazirmatn", Tahoma, "Segoe UI", system-ui, sans-serif; font-size: 15px; line-height: 1.7;
21
+ -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility;
22
  }
23
  #diba-root button { font-family: inherit; cursor: pointer; }
24
  #diba-root :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 8px; }
25
+ #diba-root svg { flex: none; }
26
 
27
  /* ---------- sidebar ---------- */
28
  .d-side { width: 268px; flex: 0 0 268px; background: var(--side); display: flex; flex-direction: column; border-inline-end: 1px solid var(--line); transition: margin .2s ease; }
29
  .d-side.closed { margin-inline-start: -268px; }
30
  .d-brand { display: flex; align-items: center; gap: 10px; padding: 16px 16px 10px; }
31
  .d-mark { width: 32px; height: 32px; border-radius: 9px; display: grid; place-items: center; color: #fff; font-weight: 800; font-size: 17px; background: linear-gradient(135deg, #1F7A5A, #C8912E); }
32
+ .d-brand-name { font-weight: 800; font-size: 17px; line-height: 1.15; }
33
  .d-brand-sub { font-size: 12px; color: var(--muted); }
34
  .d-new { margin: 6px 12px 10px; display: flex; align-items: center; gap: 8px; padding: 9px 12px; border-radius: 10px; border: 1px solid var(--line); background: var(--panel); color: var(--ink); font-size: 14px; font-weight: 600; }
35
  .d-new:hover { background: var(--side-hover); }
36
  .d-list-title { padding: 8px 18px 4px; font-size: 12px; color: var(--muted); font-weight: 600; }
37
  .d-list { flex: 1; overflow-y: auto; padding: 0 8px 8px; }
38
+ .d-conv { display: flex; align-items: center; gap: 6px; padding: 8px 10px; border-radius: 9px; color: var(--ink-2); font-size: 14px; cursor: pointer; }
39
  .d-conv:hover, .d-conv.active { background: var(--side-hover); color: var(--ink); }
40
  .d-conv-title { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; unicode-bidi: plaintext; text-align: start; }
41
+ .d-conv-del { opacity: 0; border: 0; background: transparent; color: var(--muted); padding: 2px 4px; border-radius: 6px; display: grid; place-items: center; }
42
+ .d-conv:hover .d-conv-del, .d-conv.active .d-conv-del { opacity: 1; }
43
+ .d-conv-del:hover { color: var(--danger); background: var(--panel); }
44
  .d-empty-list { padding: 10px 18px; color: var(--muted); font-size: 13px; }
45
  .d-side-foot { border-top: 1px solid var(--line); padding: 10px 12px 12px; display: flex; flex-direction: column; gap: 8px; }
46
  .d-seg { display: flex; background: var(--side-hover); border-radius: 10px; padding: 3px; }
 
59
  .d-badge { font-size: 11px; font-weight: 600; color: var(--accent); background: var(--accent-soft); border-radius: 999px; padding: 2px 8px; }
60
  .d-top-spacer { flex: 1; }
61
 
62
+ .d-scroll { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; scroll-behavior: auto; }
63
+ .d-thread { max-width: 780px; margin: 0 auto; padding: 18px 20px 28px; display: flex; flex-direction: column; gap: 26px; }
64
 
65
  .d-msg { display: flex; gap: 12px; }
66
  .d-msg.user { justify-content: flex-end; }
67
+ .d-msg.user .d-bubble { background: var(--bubble); border-radius: 18px; padding: 10px 16px; max-width: 78%; white-space: pre-wrap; overflow-wrap: anywhere; unicode-bidi: plaintext; text-align: start; }
68
+ .d-avatar { width: 30px; height: 30px; flex: 0 0 30px; border-radius: 50%; display: grid; place-items: center; font-weight: 800; font-size: 14px; color: #fff; background: linear-gradient(135deg, #1F7A5A, #C8912E); margin-top: 3px; }
69
  .d-body { flex: 1; min-width: 0; }
70
+
71
+ /* ---------- markdown ---------- */
72
+ .d-content { font-size: 15.5px; line-height: 1.85; color: var(--ink); overflow-wrap: anywhere; }
73
+ .d-content > *:first-child { margin-top: 0 !important; }
74
+ .d-content > *:last-child { margin-bottom: 0 !important; }
75
+ .d-content p { margin: 0 0 .9em; }
76
+ .d-content h1, .d-content h2, .d-content h3, .d-content h4, .d-content h5, .d-content h6 { color: var(--ink); line-height: 1.4; text-wrap: balance; }
77
+ .d-content h1 { font-size: 1.5em; font-weight: 800; margin: 1.3em 0 .55em; }
78
+ .d-content h2 { font-size: 1.28em; font-weight: 750; margin: 1.25em 0 .5em; padding-bottom: .3em; border-bottom: 1px solid var(--line-soft); }
79
+ .d-content h3 { font-size: 1.12em; font-weight: 700; margin: 1.1em 0 .45em; }
80
+ .d-content h4, .d-content h5, .d-content h6 { font-size: 1em; font-weight: 700; margin: 1em 0 .4em; }
81
+ .d-content strong, .d-content b { font-weight: 700; color: var(--ink); }
82
+ .d-content em { font-style: italic; }
83
+ .d-content del { color: var(--muted); }
84
+ .d-content hr { border: 0; border-top: 1px solid var(--line); margin: 1.5em 0; }
85
+ .d-content a { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px; }
86
+ .d-content a:hover { text-decoration-thickness: 2px; }
87
+ .d-content ul, .d-content ol { margin: .3em 0 1em; padding: 0; }
88
+ .d-content ul[dir="rtl"], .d-content ol[dir="rtl"] { padding-right: 1.5em; }
89
+ .d-content ul[dir="ltr"], .d-content ol[dir="ltr"], .d-content ul:not([dir]), .d-content ol:not([dir]) { padding-left: 1.5em; }
90
+ .d-content li { margin: .3em 0; padding-inline-start: .15em; }
91
+ .d-content li > p { margin: 0 0 .4em; }
92
+ .d-content li > ul, .d-content li > ol { margin: .25em 0 .35em; }
93
+ .d-content ul > li::marker { color: var(--muted); }
94
+ .d-content ol > li::marker { color: var(--ink-2); font-weight: 600; }
95
+ .d-content li input[type="checkbox"] { margin-inline-end: .5em; accent-color: var(--accent); vertical-align: middle; }
96
+ .d-content blockquote { margin: 1em 0; padding: .6em 1em; border-inline-start: 3px solid var(--accent); background: var(--accent-soft); border-radius: 0 10px 10px 0; color: var(--ink-2); }
97
+ [dir="rtl"] .d-content blockquote, .d-content blockquote[dir="rtl"] { border-radius: 10px 0 0 10px; }
98
+ .d-content blockquote > p:last-child { margin-bottom: 0; }
99
+ .d-content img { max-width: 100%; border-radius: 10px; }
100
+ .d-content kbd { font-family: "JetBrains Mono", Consolas, monospace; font-size: .82em; padding: 1px 6px; border: 1px solid var(--line); border-bottom-width: 2px; border-radius: 6px; background: var(--panel); }
101
+ .d-content :not(pre) > code { font-family: "JetBrains Mono", Consolas, monospace; font-size: .86em; background: var(--inline-code); color: var(--ink); padding: .12em .42em; border-radius: 6px; direction: ltr; unicode-bidi: isolate; white-space: break-spaces; }
102
+
103
+ .d-table { margin: .9em 0 1.2em; border: 1px solid var(--line); border-radius: 12px; overflow-x: auto; }
104
+ .d-table table { border-collapse: collapse; width: 100%; font-size: 14px; line-height: 1.6; }
105
+ .d-table th, .d-table td { padding: 9px 13px; text-align: start; vertical-align: top; border-bottom: 1px solid var(--line-soft); }
106
+ .d-table th { background: var(--side); font-weight: 700; color: var(--ink); white-space: nowrap; }
107
+ .d-table tbody tr:nth-child(even) td { background: var(--zebra); }
108
+ .d-table tbody tr:last-child td { border-bottom: 0; }
109
+ .d-table th + th, .d-table td + td { border-inline-start: 1px solid var(--line-soft); }
110
+
111
+ .d-code { direction: ltr; text-align: left; margin: .9em 0 1.1em; border-radius: 12px; overflow: hidden; background: var(--code-bg); border: 1px solid rgba(255,255,255,.04); }
112
+ .d-code-head { display: flex; align-items: center; justify-content: space-between; padding: 7px 10px 7px 14px; background: var(--code-head); color: var(--code-muted); font-size: 12px; font-family: "JetBrains Mono", Consolas, monospace; }
113
+ .d-code-head button { display: inline-flex; align-items: center; gap: 6px; border: 0; background: transparent; color: var(--code-muted); font-size: 12px; font-family: "Vazirmatn", Tahoma, sans-serif; padding: 3px 8px; border-radius: 6px; }
114
+ #diba-root .d-code-head button, #diba-root .d-code-head button span { font-family: "Vazirmatn", Tahoma, "Segoe UI", sans-serif; letter-spacing: 0; }
115
  .d-code-head button:hover { color: #fff; background: rgba(255,255,255,.08); }
116
+ .d-code pre { margin: 0; padding: 14px 16px; overflow-x: auto; tab-size: 2; }
117
+ .d-code pre code { font-family: "JetBrains Mono", Consolas, monospace; font-size: 13px; line-height: 1.65; color: var(--code-ink); background: transparent !important; padding: 0 !important; white-space: pre; }
118
+
119
+ /* syntax theme (dark code surface in both UI themes) */
120
+ .d-code .hljs-comment, .d-code .hljs-quote { color: #6F8580; font-style: italic; }
121
+ .d-code .hljs-keyword, .d-code .hljs-selector-tag, .d-code .hljs-built_in, .d-code .hljs-doctag { color: #C792EA; }
122
+ .d-code .hljs-string, .d-code .hljs-template-tag, .d-code .hljs-addition { color: #C3E88D; }
123
+ .d-code .hljs-number, .d-code .hljs-literal, .d-code .hljs-symbol { color: #F78C6C; }
124
+ .d-code .hljs-title, .d-code .hljs-title.function_, .d-code .hljs-section { color: #82AAFF; }
125
+ .d-code .hljs-title.class_, .d-code .hljs-type, .d-code .hljs-attr, .d-code .hljs-attribute, .d-code .hljs-property { color: #FFCB6B; }
126
+ .d-code .hljs-variable, .d-code .hljs-template-variable, .d-code .hljs-tag, .d-code .hljs-name, .d-code .hljs-deletion { color: #F07178; }
127
+ .d-code .hljs-meta, .d-code .hljs-regexp, .d-code .hljs-bullet, .d-code .hljs-link, .d-code .hljs-selector-class, .d-code .hljs-selector-id { color: #89DDFF; }
128
+ .d-code .hljs-params { color: var(--code-ink); }
129
+ .d-code .hljs-emphasis { font-style: italic; } .d-code .hljs-strong { font-weight: 700; }
130
+ .d-code .hljs-addition { background: rgba(195,232,141,.08); } .d-code .hljs-deletion { background: rgba(240,113,120,.08); }
131
+
132
+ .d-cursor > *:last-child::after, .d-cursor:empty::after { content: ""; display: inline-block; width: 8px; height: 1.05em; margin-inline-start: 3px; vertical-align: text-bottom; background: var(--accent); border-radius: 2px; animation: d-blink 1s steps(2) infinite; }
133
  @keyframes d-blink { 50% { opacity: 0; } }
134
+ .d-actions { display: flex; gap: 2px; margin-top: 8px; }
135
+ .d-actions button { display: inline-flex; align-items: center; gap: 5px; border: 0; background: transparent; color: var(--muted); font-size: 12.5px; padding: 4px 8px; border-radius: 7px; }
136
  .d-actions button:hover { background: var(--side-hover); color: var(--ink); }
137
  .d-error { color: var(--danger); font-size: 14px; }
138
+ .d-typing { display: inline-flex; gap: 4px; padding: 10px 0; }
139
  .d-typing span { width: 7px; height: 7px; border-radius: 50%; background: var(--muted); animation: d-dot 1.2s infinite ease-in-out; }
140
  .d-typing span:nth-child(2) { animation-delay: .15s; } .d-typing span:nth-child(3) { animation-delay: .3s; }
141
  @keyframes d-dot { 0%, 80%, 100% { transform: scale(.6); opacity: .4; } 40% { transform: scale(1); opacity: 1; } }
142
+ .d-cold { color: var(--muted); font-size: 13px; }
143
 
144
  /* ---------- welcome ---------- */
145
  .d-welcome { max-width: 780px; margin: 0 auto; padding: 12vh 20px 20px; text-align: center; }
 
154
 
155
  /* ---------- composer ---------- */
156
  .d-compose-wrap { flex: 0 0 auto; padding: 8px 20px 12px; background: linear-gradient(to top, var(--bg) 70%, transparent); }
157
+ .d-compose { max-width: 780px; margin: 0 auto; display: flex; align-items: flex-end; gap: 8px; background: var(--panel); border: 1px solid var(--line); border-radius: 22px; padding: 8px 8px 8px 16px; box-shadow: var(--shadow); transition: border-color .15s; }
158
  [dir="rtl"] .d-compose { padding: 8px 16px 8px 8px; }
 
159
  .d-compose:focus-within { border-color: var(--accent); }
160
+ #diba-root .d-compose textarea, #diba-root .d-compose textarea:focus, #diba-root .d-compose textarea:focus-visible { outline: none !important; box-shadow: none !important; }
161
+ .d-compose textarea { flex: 1; border: 0; resize: none; background: transparent; color: var(--ink); font: inherit; font-size: 15px; line-height: 1.6; max-height: 200px; padding: 6px 0; unicode-bidi: plaintext; text-align: start; }
 
162
  .d-compose textarea::placeholder { color: var(--muted); }
163
  .d-send { width: 38px; height: 38px; flex: 0 0 38px; border-radius: 50%; border: 0; display: grid; place-items: center; background: var(--accent); color: var(--accent-ink); }
164
  .d-send:disabled { opacity: .35; cursor: default; }
 
171
  .d-pop { position: absolute; top: 52px; inset-inline-end: 12px; width: 280px; background: var(--panel); border: 1px solid var(--line); border-radius: 14px; box-shadow: var(--shadow); padding: 14px; display: none; z-index: 5; }
172
  .d-pop.show { display: block; }
173
  .d-pop label { display: flex; justify-content: space-between; font-size: 13px; color: var(--ink-2); margin: 6px 0 2px; }
174
+ .d-pop b { font-variant-numeric: tabular-nums; }
175
  .d-pop input[type=range] { width: 100%; accent-color: var(--accent); }
176
  .d-pop .d-hint { font-size: 11.5px; color: var(--muted); margin-top: 8px; }
177
 
 
182
  .d-sugg { grid-template-columns: 1fr; }
183
  .d-welcome { padding-top: 7vh; }
184
  .d-msg.user .d-bubble { max-width: 88%; }
185
+ .d-thread { padding: 14px 14px 24px; }
186
+ .d-compose-wrap { padding: 6px 12px 10px; }
187
  }
188
+ @media (prefers-reduced-motion: reduce) { .d-side, .d-compose { transition: none; } .d-cursor > *:last-child::after, .d-typing span { animation: none; } }
diba-ui.js CHANGED
@@ -1,15 +1,18 @@
1
- /* Diba chat UI — shared front-end. Backend adapters: "openai" (llama.cpp server, same origin) and "gradio" (ZeroGPU Space). */
 
 
2
  (function () {
3
  "use strict";
4
  if (window.__dibaUI) return; window.__dibaUI = true;
5
 
6
- const CDN = {
7
- marked: "https://cdn.jsdelivr.net/npm/marked@12.0.2/marked.min.js",
8
- purify: "https://cdn.jsdelivr.net/npm/dompurify@3.1.6/dist/purify.min.js",
9
- hljs: "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js",
10
- hljsCss: "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css",
11
- fonts: "https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;600&display=swap",
12
- gradio: "https://cdn.jsdelivr.net/npm/@gradio/client@1/dist/index.min.js",
 
13
  };
14
 
15
  const T = {
@@ -17,9 +20,9 @@
17
  dir: "rtl", brandSub: "دستیار هوش مصنوعی دیباچین", newChat: "گفت‌وگوی جدید", history: "گفت‌وگوها", noChats: "هنوز گفت‌وگویی ندارید.",
18
  welcome: "امروز چطور می‌توانم کمکتان کنم؟", welcomeSub: "به هر زبانی بنویسید، به همان زبان پاسخ می‌دهم.",
19
  placeholder: "پیام به دیبا…", disclaimer: "دیبا ممکن است اشتباه کند. اطلاعات مهم را بررسی کنید.", preview: "پیش‌نمایش",
20
- copy: "کپی", copied: "کپی شد", regenerate: "پاسخ دوباره", delete: "حذف", stop: "توقف", send: "ارسال", menu: "منو",
21
- settings: "تنظیمات", temperature: "خلاقیت", maxTokens: "حداکثر طول پاسخ", tempHint: "برای کدنویسی و پاسخ دقیق، خلاقیت را صفر نگذارید بیشتر.",
22
- theme: "تم", light: "روشن", dark: "تیره", error: "ارتباط با مدل برقرار نشد. لطفاً دوباره تلاش کنید.", jump: "رفتن به آخرین پیام",
23
  footer: 'ساخته‌ی <a href="https://dibachain.ir" target="_blank" rel="noopener">دیباچین</a>', untitled: "گفت‌وگوی بدون عنوان",
24
  coldStart: "در حال آماده‌سازی مدل… اولین پاسخ ممکن است چند ثانیه طول بکشد.",
25
  },
@@ -27,14 +30,13 @@
27
  dir: "ltr", brandSub: "AI assistant by Dibachain", newChat: "New chat", history: "Chats", noChats: "No chats yet.",
28
  welcome: "How can I help you today?", welcomeSub: "Write in any language and I will reply in the same one.",
29
  placeholder: "Message Diba…", disclaimer: "Diba can make mistakes. Check important information.", preview: "Preview",
30
- copy: "Copy", copied: "Copied", regenerate: "Regenerate", delete: "Delete", stop: "Stop", send: "Send", menu: "Menu",
31
  settings: "Settings", temperature: "Creativity", maxTokens: "Max response length", tempHint: "Keep creativity at 0 for code and precise answers.",
32
- theme: "Theme", light: "Light", dark: "Dark", error: "Could not reach the model. Please try again.", jump: "Jump to latest",
33
  footer: 'Built by <a href="https://dibachain.ir" target="_blank" rel="noopener">Dibachain</a>', untitled: "Untitled chat",
34
  coldStart: "Warming up the model… the first reply may take a few seconds.",
35
  },
36
  };
37
- T.fa.tempHint = "برای کدنویسی و پاسخ دقیق، خلاقیت را روی صفر نگه دارید.";
38
 
39
  const SUGG = {
40
  fa: [
@@ -52,9 +54,9 @@
52
  };
53
 
54
  const SYSTEM = {
55
- fa: "تو «دیبا» هستی، دستیار هوش مصنوعی ساخته‌ی شرکت دیباچین. کاربر به فارسی نوشته؛ فقط به فارسی جواب بده، به زبان نوشتاری و مؤدبانه. دقیقاً به همان چیزی که کاربر خواسته جواب بده، روشن و بدون مقدمه‌ی اضافه. اگر کد لازم است، کد تمیز و کامل در بلوک کد بده. هرگز نام مدل پایه یا سازنده‌ی آن را نگو؛ اگر پرسیدند فقط بگو روی یک مدل زبانی متن‌باز ساخته شده‌ای و دیباچین تو را آموزش داده است.",
56
- en: "You are Diba, an AI assistant built by the company Dibachain. The user wrote in English, so reply ONLY in English. Be clear, accurate and professional. Answer exactly what was asked, without unnecessary preamble. When code is needed, give clean, complete code in a fenced code block. Never name or hint at any base model or its maker; if asked, say only that you were built on an open-source language model and trained by Dibachain.",
57
- other: "You are Diba, an AI assistant built by the company Dibachain. Reply in the same language the user wrote in. Be clear, accurate and professional. Never name any base model or its maker.",
58
  };
59
 
60
  const ICON = {
@@ -62,8 +64,12 @@
62
  menu: '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M4 6h16M4 12h16M4 18h16"/></svg>',
63
  send: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19V5M5 12l7-7 7 7"/></svg>',
64
  stop: '<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><rect x="5" y="5" width="14" height="14" rx="2"/></svg>',
65
- gear: '<svg width="19" height="19" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>',
66
  down: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M19 12l-7 7-7-7"/></svg>',
 
 
 
 
67
  };
68
 
69
  // ---------------------------------------------------------------- utils
@@ -73,10 +79,14 @@
73
  set(k, v) { try { localStorage.setItem("diba." + k, JSON.stringify(v)); } catch (e) { /* storage unavailable */ } },
74
  };
75
  const uid = () => Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
76
- const esc = (s) => s.replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" }[c]));
77
- const loadScript = (src) => new Promise((res, rej) => { const s = document.createElement("script"); s.src = src; s.onload = res; s.onerror = rej; document.head.appendChild(s); });
78
- const loadCss = (href) => { const l = document.createElement("link"); l.rel = "stylesheet"; l.href = href; document.head.appendChild(l); };
79
-
 
 
 
 
80
  function detectLang(text) {
81
  const noCode = text.replace(/```[\s\S]*?```|`[^`]*`/g, " ");
82
  const fa = (noCode.match(/[؀-ۿ]/g) || []).length;
@@ -84,17 +94,16 @@
84
  if (!fa && !en) return "other";
85
  return fa >= en ? "fa" : "en";
86
  }
 
87
 
88
  // ---------------------------------------------------------------- state
89
  const BACKEND = window.DIBA_BACKEND || (document.querySelector("gradio-app") || window.gradio_config ? "gradio" : "openai");
90
  const S = {
91
- lang: store.get("lang", (navigator.language || "fa").startsWith("fa") ? "fa" : "fa"),
92
  theme: store.get("theme", window.matchMedia && matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"),
93
  settings: Object.assign({ temperature: 0, max_tokens: 1024 }, store.get("settings", {})),
94
  convs: store.get("convs", []),
95
- currentId: null,
96
- streaming: false,
97
- controller: null,
98
  sideOpen: window.innerWidth > 820,
99
  };
100
  const qp = new URLSearchParams(location.search);
@@ -106,86 +115,92 @@
106
 
107
  // ---------------------------------------------------------------- markdown
108
  function renderMarkdown(md) {
109
- if (!window.marked || !window.DOMPurify) return "<p>" + esc(md).replace(/\n/g, "<br>") + "</p>";
110
- const html = window.DOMPurify.sanitize(window.marked.parse(md, { gfm: true, breaks: true }));
 
 
111
  const box = document.createElement("div");
112
  box.innerHTML = html;
113
  box.querySelectorAll("pre > code").forEach((code) => {
114
  const pre = code.parentElement;
115
- const lang = ((code.className.match(/language-([\w+#-]+)/) || [])[1] || "code");
116
  const wrap = document.createElement("div"); wrap.className = "d-code";
117
- wrap.innerHTML = '<div class="d-code-head"><span>' + esc(lang) + '</span><button type="button" data-copy-code>' + esc(t("copy")) + "</button></div>";
118
  pre.replaceWith(wrap); wrap.appendChild(pre);
119
- if (window.hljs) { try { window.hljs.highlightElement(code); } catch (e) { /* unknown language */ } }
 
 
 
 
 
120
  });
121
- box.querySelectorAll("a").forEach((a) => { a.target = "_blank"; a.rel = "noopener"; });
122
- // give every text block its own direction from its first strong character, so Persian and English mix cleanly
123
- box.querySelectorAll("p, ul, ol, li, h1, h2, h3, h4, blockquote, table").forEach((n) => {
124
  if (n.closest(".d-code")) return;
125
- const txt = (n.textContent || "").replace(/[`*_\d\s.,:;!?()\-–—"'«»]/g, "");
126
- const first = txt.match(/[؀-ۿA-Za-z]/);
127
- n.setAttribute("dir", first && /[؀-ۿ]/.test(first[0]) ? "rtl" : "ltr");
128
  });
129
  return box.innerHTML;
130
  }
131
 
132
  // ---------------------------------------------------------------- backends (each yields the full text so far)
133
- async function* streamOpenAI(messages) {
134
- const lang = detectLang([...messages].reverse().find((m) => m.role === "user")?.content || "");
135
- const body = {
136
- model: "diba", stream: true, temperature: S.settings.temperature, max_tokens: S.settings.max_tokens,
137
- messages: [{ role: "system", content: SYSTEM[lang] }, ...messages.slice(-16)],
138
- chat_template_kwargs: { enable_thinking: false },
139
- };
140
- const res = await fetch((window.DIBA_API_BASE || "") + "/v1/chat/completions", {
141
- method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), signal: S.controller.signal,
142
- });
143
- if (!res.ok || !res.body) throw new Error("HTTP " + res.status);
144
- const reader = res.body.getReader(); const dec = new TextDecoder(); let buf = ""; let text = "";
145
  for (;;) {
146
- const { value, done } = await reader.read(); if (done) break;
147
  buf += dec.decode(value, { stream: true });
148
  let i;
149
  while ((i = buf.indexOf("\n")) >= 0) {
150
- const line = buf.slice(0, i).trim(); buf = buf.slice(i + 1);
151
- if (!line.startsWith("data:")) continue;
152
- const data = line.slice(5).trim(); if (data === "[DONE]") return;
153
- try { const j = JSON.parse(data); const d = j.choices && j.choices[0] && j.choices[0].delta; if (d && d.content) { text += d.content; yield text; } } catch (e) { /* keep-alive */ }
154
  }
155
  }
156
  }
157
 
158
- // Gradio REST streaming protocol: POST /gradio_api/call/<api> -> {event_id}; GET .../<event_id> -> SSE (generating|complete|error)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  let gradioWarm = false;
160
  async function* streamGradio(messages) {
161
  const base = window.location.origin + window.location.pathname.replace(/\/+$/, "");
162
- const signal = S.controller.signal;
163
  const start = await fetch(base + "/gradio_api/call/chat_stream", {
164
- method: "POST", headers: { "Content-Type": "application/json" }, signal,
165
  body: JSON.stringify({ data: [JSON.stringify(messages.slice(-16)), S.settings.temperature, S.settings.max_tokens] }),
166
  });
167
  if (!start.ok) throw new Error("HTTP " + start.status);
168
  const { event_id } = await start.json();
169
- const res = await fetch(base + "/gradio_api/call/chat_stream/" + event_id, { signal });
170
  if (!res.ok || !res.body) throw new Error("HTTP " + res.status);
171
- const reader = res.body.getReader(); const dec = new TextDecoder(); let buf = ""; let event = "";
172
- for (;;) {
173
- const { value, done } = await reader.read(); if (done) return;
174
- buf += dec.decode(value, { stream: true });
175
- let i;
176
- while ((i = buf.indexOf("\n")) >= 0) {
177
- const line = buf.slice(0, i).replace(/\r$/, ""); buf = buf.slice(i + 1);
178
- if (line.startsWith("event:")) { event = line.slice(6).trim(); continue; }
179
- if (!line.startsWith("data:")) continue;
180
- const raw = line.slice(5).trim();
181
- if (event === "error") throw new Error(raw || "gradio error");
182
- if (event === "generating" || event === "complete") {
183
- try { const arr = JSON.parse(raw); if (Array.isArray(arr) && typeof arr[0] === "string") { gradioWarm = true; yield arr[0]; } } catch (e) { /* heartbeat */ }
184
- if (event === "complete") return;
185
- }
186
  }
187
- }
 
 
188
  }
 
189
 
190
  // ---------------------------------------------------------------- DOM
191
  const root = document.createElement("div");
@@ -241,15 +256,15 @@
241
  $(".d-max", root).value = S.settings.max_tokens; $(".d-max-v", root).textContent = S.settings.max_tokens;
242
  el.side.classList.toggle("closed", !S.sideOpen);
243
  el.backdrop.classList.toggle("show", S.sideOpen && window.innerWidth <= 820);
244
- renderList(); renderThread();
245
  }
246
 
247
  function renderList() {
248
- if (!S.convs.length) { el.list.innerHTML = '<div class="d-empty-list">' + esc(t("noChats")) + "</div>"; return; }
249
  el.list.innerHTML = S.convs.map((c) =>
250
  `<div class="d-conv${c.id === S.currentId ? " active" : ""}" data-id="${c.id}" role="button" tabindex="0">` +
251
  `<span class="d-conv-title">${esc(c.title || t("untitled"))}</span>` +
252
- `<button class="d-conv-del" type="button" data-del="${c.id}" title="${esc(t("delete"))}"></button></div>`).join("");
253
  }
254
 
255
  function renderWelcome() {
@@ -259,11 +274,12 @@
259
 
260
  function msgHTML(m, idx, isLast) {
261
  if (m.role === "user") return `<div class="d-msg user" data-idx="${idx}"><div class="d-bubble" dir="auto">${esc(m.content)}</div></div>`;
262
- const body = m.error ? `<div class="d-error">${esc(t("error"))}</div>`
263
- : (m.content ? `<div class="d-content${m.pending ? " d-cursor" : ""}">${renderMarkdown(m.content)}</div>`
264
- : `<div class="d-typing"><span></span><span></span><span></span></div>` + (m.cold ? `<div class="d-hint" style="color:var(--muted);font-size:13px">${esc(t("coldStart"))}</div>` : ""));
265
- const actions = m.pending ? "" : `<div class="d-actions"><button type="button" data-copy-msg="${idx}">${esc(t("copy"))}</button>` +
266
- (isLast ? `<button type="button" data-regen>${esc(t("regenerate"))}</button>` : "") + "</div>";
 
267
  return `<div class="d-msg bot" data-idx="${idx}"><div class="d-avatar">د</div><div class="d-body">${body}${actions}</div></div>`;
268
  }
269
 
@@ -280,7 +296,7 @@
280
  const c = current(); if (!c) return;
281
  const idx = c.messages.length - 1; const node = el.thread.querySelector(`.d-msg[data-idx="${idx}"]`);
282
  const html = msgHTML(c.messages[idx], idx, true);
283
- if (node) { node.outerHTML = html; } else { el.thread.insertAdjacentHTML("beforeend", html); }
284
  }
285
 
286
  // ---------------------------------------------------------------- scrolling: follow only when the reader is at the bottom
@@ -312,11 +328,10 @@
312
  const gen = BACKEND === "gradio" ? streamGradio(history) : streamOpenAI(history);
313
  for await (const text of gen) { latest = text; if (!frame) frame = requestAnimationFrame(paint); }
314
  } catch (e) {
315
- if (!(S.controller && S.controller.signal.aborted)) { bot.error = true; console.error("[diba]", e); }
316
  } finally {
317
  if (frame) cancelAnimationFrame(frame);
318
  bot.content = latest; bot.pending = false; bot.cold = false;
319
- if (!bot.content && !bot.error) bot.error = !(S.controller && S.controller.signal.aborted);
320
  if (!bot.content && !bot.error) c.messages.pop();
321
  S.streaming = false; S.controller = null; c.updated = Date.now();
322
  saveConvs(); renderThread(); toBottom(false); setSendState();
@@ -338,16 +353,26 @@
338
  el.send.classList.toggle("stop", S.streaming);
339
  el.send.innerHTML = S.streaming ? ICON.stop : ICON.send;
340
  el.send.title = S.streaming ? t("stop") : t("send");
 
341
  el.send.disabled = !S.streaming && !el.input.value.trim();
342
  }
343
  function autoGrow() { el.input.style.height = "auto"; el.input.style.height = Math.min(el.input.scrollHeight, 200) + "px"; setSendState(); }
344
 
 
 
 
 
 
 
 
 
 
 
 
345
  // ---------------------------------------------------------------- events
346
  el.form.addEventListener("submit", (e) => { e.preventDefault(); if (S.streaming) stopStream(); else sendMessage(el.input.value); });
347
  el.input.addEventListener("input", autoGrow);
348
- el.input.addEventListener("keydown", (e) => {
349
- if (e.key === "Enter" && !e.shiftKey && !e.isComposing) { e.preventDefault(); if (!S.streaming) sendMessage(el.input.value); }
350
- });
351
  $(".d-new", root).addEventListener("click", newChat);
352
  $(".d-menu", root).addEventListener("click", () => { S.sideOpen = !S.sideOpen; applyChrome(); });
353
  el.backdrop.addEventListener("click", () => { S.sideOpen = false; applyChrome(); });
@@ -365,35 +390,31 @@
365
  if (b.dataset.del) { e.stopPropagation(); S.convs = S.convs.filter((c) => c.id !== b.dataset.del); if (S.currentId === b.dataset.del) S.currentId = null; saveConvs(); renderList(); renderThread(); return; }
366
  if (b.classList.contains("d-conv")) { if (S.streaming) stopStream(); S.currentId = b.dataset.id; renderList(); renderThread(); toBottom(true); if (window.innerWidth <= 820) { S.sideOpen = false; applyChrome(); } return; }
367
  if (b.dataset.copyMsg !== undefined) { const m = current().messages[+b.dataset.copyMsg]; copyText(m.content, b); return; }
368
- if (b.hasAttribute("data-copy-code")) { copyText(b.closest(".d-code").querySelector("code").innerText, b); return; }
369
  if (b.hasAttribute("data-regen")) { const c = current(); if (!c || S.streaming) return; if (c.messages[c.messages.length - 1].role === "assistant") c.messages.pop(); runAssistant(); }
370
  });
371
  root.addEventListener("keydown", (e) => { const conv = e.target.closest && e.target.closest(".d-conv"); if (conv && (e.key === "Enter" || e.key === " ")) { e.preventDefault(); conv.click(); } });
372
-
373
- function copyText(text, btn) {
374
- const done = () => { const old = btn.textContent; btn.textContent = t("copied"); setTimeout(() => { btn.textContent = old; }, 1400); };
375
- if (navigator.clipboard && navigator.clipboard.writeText) navigator.clipboard.writeText(text).then(done, done);
376
- else { const ta = document.createElement("textarea"); ta.value = text; document.body.appendChild(ta); ta.select(); try { document.execCommand("copy"); } catch (err) { /* ignore */ } ta.remove(); done(); }
377
- }
378
  window.addEventListener("resize", () => { if (window.innerWidth > 820 && !S.sideOpen) { S.sideOpen = true; applyChrome(); } });
379
 
380
  // ---------------------------------------------------------------- demo seed for screenshots (#demo)
381
  if (location.hash === "#demo") {
382
- S.convs = [{ id: "demo", title: "تابع تبدیل اعداد", updated: Date.now(), messages: [
383
- { role: "user", content: "یک تابع جاوااسکریپت بنویس که اعداد انگلیسی یک رشته را به فارسی تبدیل کند." },
384
- { role: "assistant", content: "این تابع هر رقم انگلیسی را با معادل فارسی آن جایگزین می‌کند:\n\n```javascript\nfunction toPersianDigits(str) {\n return str.replace(/\\d/g, (d) => \"۰۱۲۳۴۵۶۷۸۹\"[d]);\n}\n```\n\n- ورودی‌های غیرعددی دست‌نخورده می‌مانند.\n- پیچیدگی زمانی `O(n)` است." },
385
- { role: "user", content: "Can you also add a TypeScript type?" },
386
- { role: "assistant", content: "Sure:\n\n```ts\nexport function toPersianDigits(str: string): string {\n return str.replace(/\\d/g, (d) => \"۰۱۲۳۴۵۶۷۸۹\"[Number(d)]);\n}\n```" },
387
  ] }, { id: "demo2", title: "Who was Cyrus the Great?", updated: Date.now(), messages: [] }];
388
  S.currentId = "demo";
389
  }
390
 
391
  // ---------------------------------------------------------------- boot
392
  function boot() {
393
- loadCss(CDN.fonts); loadCss(CDN.hljsCss);
394
  document.body.appendChild(root); document.body.classList.add("diba-lock");
395
- applyChrome(); toBottom(true); setSendState();
396
- Promise.all([loadScript(CDN.marked), loadScript(CDN.purify), loadScript(CDN.hljs)]).then(() => { renderThread(); toBottom(true); }).catch(() => { /* plain text fallback */ });
 
 
397
  if (window.innerWidth > 820) el.input.focus();
398
  const autoQ = qp.get("q"); // test hook: ?q=... sends one message on load
399
  if (autoQ && location.hash !== "#demo") setTimeout(() => { newChat(); sendMessage(autoQ); }, 1200);
 
1
+ /* Diba chat UI — shared front-end, fully self-hosted (no external CDN).
2
+ Backends: "openai" (llama.cpp server, same origin) and "gradio" (ZeroGPU Space, Gradio REST SSE).
3
+ Config globals: DIBA_BACKEND, DIBA_ASSET_BASE (prefix for vendor/ assets), DIBA_API_BASE. */
4
  (function () {
5
  "use strict";
6
  if (window.__dibaUI) return; window.__dibaUI = true;
7
 
8
+ const ASSET = window.DIBA_ASSET_BASE || "";
9
+ const VENDOR = {
10
+ marked: ASSET + "vendor/marked.min.js",
11
+ purify: ASSET + "vendor/purify.min.js",
12
+ hljs: ASSET + "vendor/highlight.min.js",
13
+ vazir: ASSET + "vendor/fonts/Vazirmatn-Variable.woff2",
14
+ monoRegular: ASSET + "vendor/fonts/JetBrainsMono-Regular.woff2",
15
+ monoSemi: ASSET + "vendor/fonts/JetBrainsMono-SemiBold.woff2",
16
  };
17
 
18
  const T = {
 
20
  dir: "rtl", brandSub: "دستیار هوش مصنوعی دیباچین", newChat: "گفت‌وگوی جدید", history: "گفت‌وگوها", noChats: "هنوز گفت‌وگویی ندارید.",
21
  welcome: "امروز چطور می‌توانم کمکتان کنم؟", welcomeSub: "به هر زبانی بنویسید، به همان زبان پاسخ می‌دهم.",
22
  placeholder: "پیام به دیبا…", disclaimer: "دیبا ممکن است اشتباه کند. اطلاعات مهم را بررسی کنید.", preview: "پیش‌نمایش",
23
+ copy: "کپی", copied: "کپی شد", copyCode: "کپی کد", regenerate: "پاسخ دوباره", delete: "حذف", stop: "توقف", send: "ارسال", menu: "منو",
24
+ settings: "تنظیمات", temperature: "خلاقیت", maxTokens: "حداکثر طول پاسخ", tempHint: "برای کدنویسی و پاسخ دقیق، خلاقیت را روی صفر نگه دارید.",
25
+ light: "روشن", dark: "تیره", error: "ارتباط با مدل برقرار نشد. لطفاً دوباره تلاش کنید.", jump: "رفتن به آخرین پیام",
26
  footer: 'ساخته‌ی <a href="https://dibachain.ir" target="_blank" rel="noopener">دیباچین</a>', untitled: "گفت‌وگوی بدون عنوان",
27
  coldStart: "در حال آماده‌سازی مدل… اولین پاسخ ممکن است چند ثانیه طول بکشد.",
28
  },
 
30
  dir: "ltr", brandSub: "AI assistant by Dibachain", newChat: "New chat", history: "Chats", noChats: "No chats yet.",
31
  welcome: "How can I help you today?", welcomeSub: "Write in any language and I will reply in the same one.",
32
  placeholder: "Message Diba…", disclaimer: "Diba can make mistakes. Check important information.", preview: "Preview",
33
+ copy: "Copy", copied: "Copied", copyCode: "Copy code", regenerate: "Regenerate", delete: "Delete", stop: "Stop", send: "Send", menu: "Menu",
34
  settings: "Settings", temperature: "Creativity", maxTokens: "Max response length", tempHint: "Keep creativity at 0 for code and precise answers.",
35
+ light: "Light", dark: "Dark", error: "Could not reach the model. Please try again.", jump: "Jump to latest",
36
  footer: 'Built by <a href="https://dibachain.ir" target="_blank" rel="noopener">Dibachain</a>', untitled: "Untitled chat",
37
  coldStart: "Warming up the model… the first reply may take a few seconds.",
38
  },
39
  };
 
40
 
41
  const SUGG = {
42
  fa: [
 
54
  };
55
 
56
  const SYSTEM = {
57
+ fa: "تو «دیبا» هستی، دستیار هوش مصنوعی ساخته‌ی شرکت دیباچین. کاربر به فارسی نوشته؛ فقط به فارسی جواب بده، به زبان نوشتاری و مؤدبانه. دقیقاً به همان چیزی که کاربر خواسته جواب بده، روشن و بدون مقدمه‌ی اضافه. از قالب‌بندی Markdown (عنوان، فهرست، جدول، بلوک کد) هر جا به خوانایی کمک می‌کند استفاده کن. اگر کد لازم است، کد تمیز و کامل در بلوک کد بده. هرگز نام مدل پایه یا سازنده‌ی آن را نگو؛ اگر پرسیدند فقط بگو روی یک مدل زبانی متن‌باز ساخته شده‌ای و دیباچین تو را آموزش داده است.",
58
+ en: "You are Diba, an AI assistant built by the company Dibachain. The user wrote in English, so reply ONLY in English. Be clear, accurate and professional. Answer exactly what was asked, without unnecessary preamble. Use Markdown formatting (headings, lists, tables, code blocks) where it improves readability. When code is needed, give clean, complete code in a fenced code block. Never name or hint at any base model or its maker; if asked, say only that you were built on an open-source language model and trained by Dibachain.",
59
+ other: "You are Diba, an AI assistant built by the company Dibachain. Reply in the same language the user wrote in. Be clear, accurate and professional, and use Markdown where helpful. Never name any base model or its maker.",
60
  };
61
 
62
  const ICON = {
 
64
  menu: '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M4 6h16M4 12h16M4 18h16"/></svg>',
65
  send: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19V5M5 12l7-7 7 7"/></svg>',
66
  stop: '<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><rect x="5" y="5" width="14" height="14" rx="2"/></svg>',
67
+ gear: '<svg width="19" height="19" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1Z"/></svg>',
68
  down: '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M19 12l-7 7-7-7"/></svg>',
69
+ copy: '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="12" height="12" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>',
70
+ check: '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>',
71
+ regen: '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-2.64-6.36L21 8"/><path d="M21 3v5h-5"/></svg>',
72
+ trash: '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18M8 6V4h8v2M19 6l-1 14H6L5 6"/></svg>',
73
  };
74
 
75
  // ---------------------------------------------------------------- utils
 
79
  set(k, v) { try { localStorage.setItem("diba." + k, JSON.stringify(v)); } catch (e) { /* storage unavailable */ } },
80
  };
81
  const uid = () => Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
82
+ const esc = (s) => String(s).replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" }[c]));
83
+ const loadScript = (src) => new Promise((res, rej) => { const s = document.createElement("script"); s.src = src; s.onload = res; s.onerror = () => rej(new Error("load " + src)); document.head.appendChild(s); });
84
+ function injectFonts() {
85
+ const css = `@font-face{font-family:"Vazirmatn";src:url("${VENDOR.vazir}") format("woff2");font-weight:100 900;font-style:normal;font-display:swap}` +
86
+ `@font-face{font-family:"JetBrains Mono";src:url("${VENDOR.monoRegular}") format("woff2");font-weight:400;font-display:swap}` +
87
+ `@font-face{font-family:"JetBrains Mono";src:url("${VENDOR.monoSemi}") format("woff2");font-weight:600;font-display:swap}`;
88
+ const st = document.createElement("style"); st.textContent = css; document.head.appendChild(st);
89
+ }
90
  function detectLang(text) {
91
  const noCode = text.replace(/```[\s\S]*?```|`[^`]*`/g, " ");
92
  const fa = (noCode.match(/[؀-ۿ]/g) || []).length;
 
94
  if (!fa && !en) return "other";
95
  return fa >= en ? "fa" : "en";
96
  }
97
+ const strongDir = (text) => { const m = (text || "").match(/[؀-ۿA-Za-z]/); return m && /[؀-ۿ]/.test(m[0]) ? "rtl" : "ltr"; };
98
 
99
  // ---------------------------------------------------------------- state
100
  const BACKEND = window.DIBA_BACKEND || (document.querySelector("gradio-app") || window.gradio_config ? "gradio" : "openai");
101
  const S = {
102
+ lang: store.get("lang", "fa"),
103
  theme: store.get("theme", window.matchMedia && matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"),
104
  settings: Object.assign({ temperature: 0, max_tokens: 1024 }, store.get("settings", {})),
105
  convs: store.get("convs", []),
106
+ currentId: null, streaming: false, controller: null,
 
 
107
  sideOpen: window.innerWidth > 820,
108
  };
109
  const qp = new URLSearchParams(location.search);
 
115
 
116
  // ---------------------------------------------------------------- markdown
117
  function renderMarkdown(md) {
118
+ if (!window.marked || !window.DOMPurify) {
119
+ return md.split(/\n{2,}/).map((p) => `<p dir="${strongDir(p)}">${esc(p).replace(/\n/g, "<br>")}</p>`).join("");
120
+ }
121
+ const html = window.DOMPurify.sanitize(window.marked.parse(md, { gfm: true, breaks: false }), { ADD_ATTR: ["target"] });
122
  const box = document.createElement("div");
123
  box.innerHTML = html;
124
  box.querySelectorAll("pre > code").forEach((code) => {
125
  const pre = code.parentElement;
126
+ const lang = (code.className.match(/language-([\w+#.-]+)/) || [])[1] || "";
127
  const wrap = document.createElement("div"); wrap.className = "d-code";
128
+ wrap.innerHTML = `<div class="d-code-head"><span>${esc(lang || "code")}</span><button type="button" data-copy-code>${ICON.copy}<span>${esc(t("copyCode"))}</span></button></div>`;
129
  pre.replaceWith(wrap); wrap.appendChild(pre);
130
+ if (window.hljs) {
131
+ try {
132
+ if (lang && window.hljs.getLanguage(lang)) { code.innerHTML = window.hljs.highlight(code.textContent, { language: lang, ignoreIllegals: true }).value; }
133
+ else { code.innerHTML = window.hljs.highlightAuto(code.textContent).value; }
134
+ } catch (e) { /* leave plain */ }
135
+ }
136
  });
137
+ box.querySelectorAll("table").forEach((tb) => { const w = document.createElement("div"); w.className = "d-table"; tb.replaceWith(w); w.appendChild(tb); });
138
+ box.querySelectorAll("a").forEach((a) => { a.target = "_blank"; a.rel = "noopener noreferrer"; });
139
+ box.querySelectorAll("p, ul, ol, li, h1, h2, h3, h4, h5, h6, blockquote, .d-table").forEach((n) => {
140
  if (n.closest(".d-code")) return;
141
+ n.setAttribute("dir", strongDir((n.textContent || "").replace(/[`*_\d\s.,:;!?()\-–—"'«»|#]/g, "")));
 
 
142
  });
143
  return box.innerHTML;
144
  }
145
 
146
  // ---------------------------------------------------------------- backends (each yields the full text so far)
147
+ async function* readSSE(res, onEvent) {
148
+ const reader = res.body.getReader(); const dec = new TextDecoder(); let buf = ""; let event = "message";
 
 
 
 
 
 
 
 
 
 
149
  for (;;) {
150
+ const { value, done } = await reader.read(); if (done) return;
151
  buf += dec.decode(value, { stream: true });
152
  let i;
153
  while ((i = buf.indexOf("\n")) >= 0) {
154
+ const line = buf.slice(0, i).replace(/\r$/, ""); buf = buf.slice(i + 1);
155
+ if (line.startsWith("event:")) { event = line.slice(6).trim(); continue; }
156
+ if (line.startsWith("data:")) { const r = onEvent(event, line.slice(5).trim()); if (r === null) return; if (r !== undefined) yield r; }
157
+ if (line === "") event = "message";
158
  }
159
  }
160
  }
161
 
162
+ async function* streamOpenAI(messages) {
163
+ const lang = detectLang([...messages].reverse().find((m) => m.role === "user")?.content || "");
164
+ const res = await fetch((window.DIBA_API_BASE || "") + "/v1/chat/completions", {
165
+ method: "POST", headers: { "Content-Type": "application/json" }, signal: S.controller.signal,
166
+ body: JSON.stringify({
167
+ model: "diba", stream: true, temperature: S.settings.temperature, max_tokens: S.settings.max_tokens,
168
+ messages: [{ role: "system", content: SYSTEM[lang] }, ...messages.slice(-16)], chat_template_kwargs: { enable_thinking: false },
169
+ }),
170
+ });
171
+ if (!res.ok || !res.body) throw new Error("HTTP " + res.status);
172
+ let text = "";
173
+ yield* readSSE(res, (ev, data) => {
174
+ if (data === "[DONE]") return null;
175
+ try { const d = JSON.parse(data).choices?.[0]?.delta?.content; if (d) { text += d; return text; } } catch (e) { /* keep-alive */ }
176
+ return undefined;
177
+ });
178
+ }
179
+
180
  let gradioWarm = false;
181
  async function* streamGradio(messages) {
182
  const base = window.location.origin + window.location.pathname.replace(/\/+$/, "");
 
183
  const start = await fetch(base + "/gradio_api/call/chat_stream", {
184
+ method: "POST", headers: { "Content-Type": "application/json" }, signal: S.controller.signal,
185
  body: JSON.stringify({ data: [JSON.stringify(messages.slice(-16)), S.settings.temperature, S.settings.max_tokens] }),
186
  });
187
  if (!start.ok) throw new Error("HTTP " + start.status);
188
  const { event_id } = await start.json();
189
+ const res = await fetch(base + "/gradio_api/call/chat_stream/" + event_id, { signal: S.controller.signal });
190
  if (!res.ok || !res.body) throw new Error("HTTP " + res.status);
191
+ yield* readSSE(res, (ev, data) => {
192
+ if (ev === "error") throw new Error(data || "gradio error");
193
+ if (ev === "generating" || ev === "complete") {
194
+ let out;
195
+ try { const arr = JSON.parse(data); if (Array.isArray(arr) && typeof arr[0] === "string") { gradioWarm = true; out = arr[0]; } } catch (e) { /* heartbeat */ }
196
+ if (ev === "complete") { if (out !== undefined) latestFinal = out; return null; }
197
+ return out;
 
 
 
 
 
 
 
 
198
  }
199
+ return undefined;
200
+ });
201
+ if (latestFinal !== null) { const f = latestFinal; latestFinal = null; yield f; }
202
  }
203
+ let latestFinal = null;
204
 
205
  // ---------------------------------------------------------------- DOM
206
  const root = document.createElement("div");
 
256
  $(".d-max", root).value = S.settings.max_tokens; $(".d-max-v", root).textContent = S.settings.max_tokens;
257
  el.side.classList.toggle("closed", !S.sideOpen);
258
  el.backdrop.classList.toggle("show", S.sideOpen && window.innerWidth <= 820);
259
+ setSendState(); renderList(); renderThread();
260
  }
261
 
262
  function renderList() {
263
+ if (!S.convs.length) { el.list.innerHTML = `<div class="d-empty-list">${esc(t("noChats"))}</div>`; return; }
264
  el.list.innerHTML = S.convs.map((c) =>
265
  `<div class="d-conv${c.id === S.currentId ? " active" : ""}" data-id="${c.id}" role="button" tabindex="0">` +
266
  `<span class="d-conv-title">${esc(c.title || t("untitled"))}</span>` +
267
+ `<button class="d-conv-del" type="button" data-del="${c.id}" title="${esc(t("delete"))}" aria-label="${esc(t("delete"))}">${ICON.trash}</button></div>`).join("");
268
  }
269
 
270
  function renderWelcome() {
 
274
 
275
  function msgHTML(m, idx, isLast) {
276
  if (m.role === "user") return `<div class="d-msg user" data-idx="${idx}"><div class="d-bubble" dir="auto">${esc(m.content)}</div></div>`;
277
+ let body;
278
+ if (m.error) body = `<div class="d-error">${esc(t("error"))}</div>`;
279
+ else if (m.content) body = `<div class="d-content${m.pending ? " d-cursor" : ""}">${renderMarkdown(m.content)}</div>`;
280
+ else body = `<div class="d-typing"><span></span><span></span><span></span></div>` + (m.cold ? `<div class="d-cold">${esc(t("coldStart"))}</div>` : "");
281
+ const actions = m.pending ? "" : `<div class="d-actions"><button type="button" data-copy-msg="${idx}">${ICON.copy}<span>${esc(t("copy"))}</span></button>` +
282
+ (isLast ? `<button type="button" data-regen>${ICON.regen}<span>${esc(t("regenerate"))}</span></button>` : "") + "</div>";
283
  return `<div class="d-msg bot" data-idx="${idx}"><div class="d-avatar">د</div><div class="d-body">${body}${actions}</div></div>`;
284
  }
285
 
 
296
  const c = current(); if (!c) return;
297
  const idx = c.messages.length - 1; const node = el.thread.querySelector(`.d-msg[data-idx="${idx}"]`);
298
  const html = msgHTML(c.messages[idx], idx, true);
299
+ if (node) node.outerHTML = html; else el.thread.insertAdjacentHTML("beforeend", html);
300
  }
301
 
302
  // ---------------------------------------------------------------- scrolling: follow only when the reader is at the bottom
 
328
  const gen = BACKEND === "gradio" ? streamGradio(history) : streamOpenAI(history);
329
  for await (const text of gen) { latest = text; if (!frame) frame = requestAnimationFrame(paint); }
330
  } catch (e) {
331
+ if (!(S.controller && S.controller.signal.aborted)) { bot.error = !latest; console.error("[diba]", e); }
332
  } finally {
333
  if (frame) cancelAnimationFrame(frame);
334
  bot.content = latest; bot.pending = false; bot.cold = false;
 
335
  if (!bot.content && !bot.error) c.messages.pop();
336
  S.streaming = false; S.controller = null; c.updated = Date.now();
337
  saveConvs(); renderThread(); toBottom(false); setSendState();
 
353
  el.send.classList.toggle("stop", S.streaming);
354
  el.send.innerHTML = S.streaming ? ICON.stop : ICON.send;
355
  el.send.title = S.streaming ? t("stop") : t("send");
356
+ el.send.setAttribute("aria-label", el.send.title);
357
  el.send.disabled = !S.streaming && !el.input.value.trim();
358
  }
359
  function autoGrow() { el.input.style.height = "auto"; el.input.style.height = Math.min(el.input.scrollHeight, 200) + "px"; setSendState(); }
360
 
361
+ function copyText(text, btn) {
362
+ const label = btn.querySelector("span"); const icon = btn.querySelector("svg");
363
+ const done = () => {
364
+ const oldLabel = label ? label.textContent : ""; const oldIcon = icon ? icon.outerHTML : "";
365
+ if (label) label.textContent = t("copied"); if (icon) icon.outerHTML = ICON.check;
366
+ setTimeout(() => { if (label) label.textContent = oldLabel; const s = btn.querySelector("svg"); if (s && oldIcon) s.outerHTML = oldIcon; }, 1400);
367
+ };
368
+ if (navigator.clipboard && navigator.clipboard.writeText) navigator.clipboard.writeText(text).then(done, done);
369
+ else { const ta = document.createElement("textarea"); ta.value = text; document.body.appendChild(ta); ta.select(); try { document.execCommand("copy"); } catch (err) { /* ignore */ } ta.remove(); done(); }
370
+ }
371
+
372
  // ---------------------------------------------------------------- events
373
  el.form.addEventListener("submit", (e) => { e.preventDefault(); if (S.streaming) stopStream(); else sendMessage(el.input.value); });
374
  el.input.addEventListener("input", autoGrow);
375
+ el.input.addEventListener("keydown", (e) => { if (e.key === "Enter" && !e.shiftKey && !e.isComposing) { e.preventDefault(); if (!S.streaming) sendMessage(el.input.value); } });
 
 
376
  $(".d-new", root).addEventListener("click", newChat);
377
  $(".d-menu", root).addEventListener("click", () => { S.sideOpen = !S.sideOpen; applyChrome(); });
378
  el.backdrop.addEventListener("click", () => { S.sideOpen = false; applyChrome(); });
 
390
  if (b.dataset.del) { e.stopPropagation(); S.convs = S.convs.filter((c) => c.id !== b.dataset.del); if (S.currentId === b.dataset.del) S.currentId = null; saveConvs(); renderList(); renderThread(); return; }
391
  if (b.classList.contains("d-conv")) { if (S.streaming) stopStream(); S.currentId = b.dataset.id; renderList(); renderThread(); toBottom(true); if (window.innerWidth <= 820) { S.sideOpen = false; applyChrome(); } return; }
392
  if (b.dataset.copyMsg !== undefined) { const m = current().messages[+b.dataset.copyMsg]; copyText(m.content, b); return; }
393
+ if (b.hasAttribute("data-copy-code")) { copyText(b.closest(".d-code").querySelector("code").textContent, b); return; }
394
  if (b.hasAttribute("data-regen")) { const c = current(); if (!c || S.streaming) return; if (c.messages[c.messages.length - 1].role === "assistant") c.messages.pop(); runAssistant(); }
395
  });
396
  root.addEventListener("keydown", (e) => { const conv = e.target.closest && e.target.closest(".d-conv"); if (conv && (e.key === "Enter" || e.key === " ")) { e.preventDefault(); conv.click(); } });
 
 
 
 
 
 
397
  window.addEventListener("resize", () => { if (window.innerWidth > 820 && !S.sideOpen) { S.sideOpen = true; applyChrome(); } });
398
 
399
  // ---------------------------------------------------------------- demo seed for screenshots (#demo)
400
  if (location.hash === "#demo") {
401
+ S.convs = [{ id: "demo", title: "مقایسه‌ی روش‌های کپی آرایه", updated: Date.now(), messages: [
402
+ { role: "user", content: "روش‌های کپی آرایه در جاوااسکریپت را مقایسه کن." },
403
+ { role: "assistant", content: "## کپی سطحی در برابر کپی عمیق\n\nدر جاوااسکریپت **کپی سطحی** فقط لایه‌ی اول را کپی می‌کند، اما **کپی عمیق** همه‌ی لایه‌ها را.\n\n| روش | نوع کپی | اشیای تو در تو |\n|---|---|---|\n| `[...arr]` | سطحی | مشترک می‌مانند |\n| `arr.slice()` | سطحی | مشترک می‌مانند |\n| `structuredClone(arr)` | عمیق | مستقل میشوند |\n\n### نمونه\n\n```javascript\nconst original = [{ id: 1 }, { id: 2 }];\nconst shallow = [...original];\nconst deep = structuredClone(original);\n\nshallow[0].id = 99; // original هم تغییر می‌کند\nconsole.log(original[0].id); // 99\n```\n\n> برای داده‌هایی که تابع یا DOM ندارند، `structuredClone` سادهترین انتخاب است.\n\n1. اگر فقط ترتیب را عوض می‌کنید، کپی سطحی کافی است.\n2. اگر اشیای داخلی را تغییر می‌دهید، کپی عمیق بگیرید." },
404
+ { role: "user", content: "Show the same idea in Python." },
405
+ { role: "assistant", content: "Python has the same distinction:\n\n- **Shallow copy** with `list.copy()` or `copy.copy()`\n- **Deep copy** with `copy.deepcopy()`\n\n```python\nimport copy\n\noriginal = [{\"id\": 1}, {\"id\": 2}]\nshallow = original.copy()\ndeep = copy.deepcopy(original)\n\nshallow[0][\"id\"] = 99\nprint(original[0][\"id\"]) # 99\n```" },
406
  ] }, { id: "demo2", title: "Who was Cyrus the Great?", updated: Date.now(), messages: [] }];
407
  S.currentId = "demo";
408
  }
409
 
410
  // ---------------------------------------------------------------- boot
411
  function boot() {
412
+ injectFonts();
413
  document.body.appendChild(root); document.body.classList.add("diba-lock");
414
+ applyChrome(); toBottom(true);
415
+ Promise.all([loadScript(VENDOR.marked), loadScript(VENDOR.purify), loadScript(VENDOR.hljs)])
416
+ .then(() => { renderThread(); toBottom(true); })
417
+ .catch((e) => console.error("[diba] vendor load failed", e));
418
  if (window.innerWidth > 820) el.input.focus();
419
  const autoQ = qp.get("q"); // test hook: ?q=... sends one message on load
420
  if (autoQ && location.hash !== "#demo") setTimeout(() => { newChat(); sendMessage(autoQ); }, 1200);
vendor/fonts/JetBrainsMono-Regular.woff2 ADDED
Binary file (92.2 kB). View file
 
vendor/fonts/JetBrainsMono-SemiBold.woff2 ADDED
Binary file (94.5 kB). View file
 
vendor/fonts/Vazirmatn-Variable.woff2 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4e3fa217d38fdafc1fea4414ceb58ca5e662cf0ab5fa735a8c8c20e8b42cad92
3
+ size 111152
vendor/highlight.min.js ADDED
The diff for this file is too large to render. See raw diff
 
vendor/marked.min.js ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ /**
2
+ * marked v12.0.2 - a markdown parser
3
+ * Copyright (c) 2011-2024, Christopher Jeffrey. (MIT Licensed)
4
+ * https://github.com/markedjs/marked
5
+ */
6
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).marked={})}(this,(function(e){"use strict";function t(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}function n(t){e.defaults=t}e.defaults={async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null};const s=/[&<>"']/,r=new RegExp(s.source,"g"),i=/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,l=new RegExp(i.source,"g"),o={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"},a=e=>o[e];function c(e,t){if(t){if(s.test(e))return e.replace(r,a)}else if(i.test(e))return e.replace(l,a);return e}const h=/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/gi;function p(e){return e.replace(h,((e,t)=>"colon"===(t=t.toLowerCase())?":":"#"===t.charAt(0)?"x"===t.charAt(1)?String.fromCharCode(parseInt(t.substring(2),16)):String.fromCharCode(+t.substring(1)):""))}const u=/(^|[^\[])\^/g;function k(e,t){let n="string"==typeof e?e:e.source;t=t||"";const s={replace:(e,t)=>{let r="string"==typeof t?t:t.source;return r=r.replace(u,"$1"),n=n.replace(e,r),s},getRegex:()=>new RegExp(n,t)};return s}function g(e){try{e=encodeURI(e).replace(/%25/g,"%")}catch(e){return null}return e}const f={exec:()=>null};function d(e,t){const n=e.replace(/\|/g,((e,t,n)=>{let s=!1,r=t;for(;--r>=0&&"\\"===n[r];)s=!s;return s?"|":" |"})).split(/ \|/);let s=0;if(n[0].trim()||n.shift(),n.length>0&&!n[n.length-1].trim()&&n.pop(),t)if(n.length>t)n.splice(t);else for(;n.length<t;)n.push("");for(;s<n.length;s++)n[s]=n[s].trim().replace(/\\\|/g,"|");return n}function x(e,t,n){const s=e.length;if(0===s)return"";let r=0;for(;r<s;){const i=e.charAt(s-r-1);if(i!==t||n){if(i===t||!n)break;r++}else r++}return e.slice(0,s-r)}function b(e,t,n,s){const r=t.href,i=t.title?c(t.title):null,l=e[1].replace(/\\([\[\]])/g,"$1");if("!"!==e[0].charAt(0)){s.state.inLink=!0;const e={type:"link",raw:n,href:r,title:i,text:l,tokens:s.inlineTokens(l)};return s.state.inLink=!1,e}return{type:"image",raw:n,href:r,title:i,text:c(l)}}class w{options;rules;lexer;constructor(t){this.options=t||e.defaults}space(e){const t=this.rules.block.newline.exec(e);if(t&&t[0].length>0)return{type:"space",raw:t[0]}}code(e){const t=this.rules.block.code.exec(e);if(t){const e=t[0].replace(/^ {1,4}/gm,"");return{type:"code",raw:t[0],codeBlockStyle:"indented",text:this.options.pedantic?e:x(e,"\n")}}}fences(e){const t=this.rules.block.fences.exec(e);if(t){const e=t[0],n=function(e,t){const n=e.match(/^(\s+)(?:```)/);if(null===n)return t;const s=n[1];return t.split("\n").map((e=>{const t=e.match(/^\s+/);if(null===t)return e;const[n]=t;return n.length>=s.length?e.slice(s.length):e})).join("\n")}(e,t[3]||"");return{type:"code",raw:e,lang:t[2]?t[2].trim().replace(this.rules.inline.anyPunctuation,"$1"):t[2],text:n}}}heading(e){const t=this.rules.block.heading.exec(e);if(t){let e=t[2].trim();if(/#$/.test(e)){const t=x(e,"#");this.options.pedantic?e=t.trim():t&&!/ $/.test(t)||(e=t.trim())}return{type:"heading",raw:t[0],depth:t[1].length,text:e,tokens:this.lexer.inline(e)}}}hr(e){const t=this.rules.block.hr.exec(e);if(t)return{type:"hr",raw:t[0]}}blockquote(e){const t=this.rules.block.blockquote.exec(e);if(t){let e=t[0].replace(/\n {0,3}((?:=+|-+) *)(?=\n|$)/g,"\n $1");e=x(e.replace(/^ *>[ \t]?/gm,""),"\n");const n=this.lexer.state.top;this.lexer.state.top=!0;const s=this.lexer.blockTokens(e);return this.lexer.state.top=n,{type:"blockquote",raw:t[0],tokens:s,text:e}}}list(e){let t=this.rules.block.list.exec(e);if(t){let n=t[1].trim();const s=n.length>1,r={type:"list",raw:"",ordered:s,start:s?+n.slice(0,-1):"",loose:!1,items:[]};n=s?`\\d{1,9}\\${n.slice(-1)}`:`\\${n}`,this.options.pedantic&&(n=s?n:"[*+-]");const i=new RegExp(`^( {0,3}${n})((?:[\t ][^\\n]*)?(?:\\n|$))`);let l="",o="",a=!1;for(;e;){let n=!1;if(!(t=i.exec(e)))break;if(this.rules.block.hr.test(e))break;l=t[0],e=e.substring(l.length);let s=t[2].split("\n",1)[0].replace(/^\t+/,(e=>" ".repeat(3*e.length))),c=e.split("\n",1)[0],h=0;this.options.pedantic?(h=2,o=s.trimStart()):(h=t[2].search(/[^ ]/),h=h>4?1:h,o=s.slice(h),h+=t[1].length);let p=!1;if(!s&&/^ *$/.test(c)&&(l+=c+"\n",e=e.substring(c.length+1),n=!0),!n){const t=new RegExp(`^ {0,${Math.min(3,h-1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ \t][^\\n]*)?(?:\\n|$))`),n=new RegExp(`^ {0,${Math.min(3,h-1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`),r=new RegExp(`^ {0,${Math.min(3,h-1)}}(?:\`\`\`|~~~)`),i=new RegExp(`^ {0,${Math.min(3,h-1)}}#`);for(;e;){const a=e.split("\n",1)[0];if(c=a,this.options.pedantic&&(c=c.replace(/^ {1,4}(?=( {4})*[^ ])/g," ")),r.test(c))break;if(i.test(c))break;if(t.test(c))break;if(n.test(e))break;if(c.search(/[^ ]/)>=h||!c.trim())o+="\n"+c.slice(h);else{if(p)break;if(s.search(/[^ ]/)>=4)break;if(r.test(s))break;if(i.test(s))break;if(n.test(s))break;o+="\n"+c}p||c.trim()||(p=!0),l+=a+"\n",e=e.substring(a.length+1),s=c.slice(h)}}r.loose||(a?r.loose=!0:/\n *\n *$/.test(l)&&(a=!0));let u,k=null;this.options.gfm&&(k=/^\[[ xX]\] /.exec(o),k&&(u="[ ] "!==k[0],o=o.replace(/^\[[ xX]\] +/,""))),r.items.push({type:"list_item",raw:l,task:!!k,checked:u,loose:!1,text:o,tokens:[]}),r.raw+=l}r.items[r.items.length-1].raw=l.trimEnd(),r.items[r.items.length-1].text=o.trimEnd(),r.raw=r.raw.trimEnd();for(let e=0;e<r.items.length;e++)if(this.lexer.state.top=!1,r.items[e].tokens=this.lexer.blockTokens(r.items[e].text,[]),!r.loose){const t=r.items[e].tokens.filter((e=>"space"===e.type)),n=t.length>0&&t.some((e=>/\n.*\n/.test(e.raw)));r.loose=n}if(r.loose)for(let e=0;e<r.items.length;e++)r.items[e].loose=!0;return r}}html(e){const t=this.rules.block.html.exec(e);if(t){return{type:"html",block:!0,raw:t[0],pre:"pre"===t[1]||"script"===t[1]||"style"===t[1],text:t[0]}}}def(e){const t=this.rules.block.def.exec(e);if(t){const e=t[1].toLowerCase().replace(/\s+/g," "),n=t[2]?t[2].replace(/^<(.*)>$/,"$1").replace(this.rules.inline.anyPunctuation,"$1"):"",s=t[3]?t[3].substring(1,t[3].length-1).replace(this.rules.inline.anyPunctuation,"$1"):t[3];return{type:"def",tag:e,raw:t[0],href:n,title:s}}}table(e){const t=this.rules.block.table.exec(e);if(!t)return;if(!/[:|]/.test(t[2]))return;const n=d(t[1]),s=t[2].replace(/^\||\| *$/g,"").split("|"),r=t[3]&&t[3].trim()?t[3].replace(/\n[ \t]*$/,"").split("\n"):[],i={type:"table",raw:t[0],header:[],align:[],rows:[]};if(n.length===s.length){for(const e of s)/^ *-+: *$/.test(e)?i.align.push("right"):/^ *:-+: *$/.test(e)?i.align.push("center"):/^ *:-+ *$/.test(e)?i.align.push("left"):i.align.push(null);for(const e of n)i.header.push({text:e,tokens:this.lexer.inline(e)});for(const e of r)i.rows.push(d(e,i.header.length).map((e=>({text:e,tokens:this.lexer.inline(e)}))));return i}}lheading(e){const t=this.rules.block.lheading.exec(e);if(t)return{type:"heading",raw:t[0],depth:"="===t[2].charAt(0)?1:2,text:t[1],tokens:this.lexer.inline(t[1])}}paragraph(e){const t=this.rules.block.paragraph.exec(e);if(t){const e="\n"===t[1].charAt(t[1].length-1)?t[1].slice(0,-1):t[1];return{type:"paragraph",raw:t[0],text:e,tokens:this.lexer.inline(e)}}}text(e){const t=this.rules.block.text.exec(e);if(t)return{type:"text",raw:t[0],text:t[0],tokens:this.lexer.inline(t[0])}}escape(e){const t=this.rules.inline.escape.exec(e);if(t)return{type:"escape",raw:t[0],text:c(t[1])}}tag(e){const t=this.rules.inline.tag.exec(e);if(t)return!this.lexer.state.inLink&&/^<a /i.test(t[0])?this.lexer.state.inLink=!0:this.lexer.state.inLink&&/^<\/a>/i.test(t[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&/^<(pre|code|kbd|script)(\s|>)/i.test(t[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&/^<\/(pre|code|kbd|script)(\s|>)/i.test(t[0])&&(this.lexer.state.inRawBlock=!1),{type:"html",raw:t[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:t[0]}}link(e){const t=this.rules.inline.link.exec(e);if(t){const e=t[2].trim();if(!this.options.pedantic&&/^</.test(e)){if(!/>$/.test(e))return;const t=x(e.slice(0,-1),"\\");if((e.length-t.length)%2==0)return}else{const e=function(e,t){if(-1===e.indexOf(t[1]))return-1;let n=0;for(let s=0;s<e.length;s++)if("\\"===e[s])s++;else if(e[s]===t[0])n++;else if(e[s]===t[1]&&(n--,n<0))return s;return-1}(t[2],"()");if(e>-1){const n=(0===t[0].indexOf("!")?5:4)+t[1].length+e;t[2]=t[2].substring(0,e),t[0]=t[0].substring(0,n).trim(),t[3]=""}}let n=t[2],s="";if(this.options.pedantic){const e=/^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(n);e&&(n=e[1],s=e[3])}else s=t[3]?t[3].slice(1,-1):"";return n=n.trim(),/^</.test(n)&&(n=this.options.pedantic&&!/>$/.test(e)?n.slice(1):n.slice(1,-1)),b(t,{href:n?n.replace(this.rules.inline.anyPunctuation,"$1"):n,title:s?s.replace(this.rules.inline.anyPunctuation,"$1"):s},t[0],this.lexer)}}reflink(e,t){let n;if((n=this.rules.inline.reflink.exec(e))||(n=this.rules.inline.nolink.exec(e))){const e=t[(n[2]||n[1]).replace(/\s+/g," ").toLowerCase()];if(!e){const e=n[0].charAt(0);return{type:"text",raw:e,text:e}}return b(n,e,n[0],this.lexer)}}emStrong(e,t,n=""){let s=this.rules.inline.emStrongLDelim.exec(e);if(!s)return;if(s[3]&&n.match(/[\p{L}\p{N}]/u))return;if(!(s[1]||s[2]||"")||!n||this.rules.inline.punctuation.exec(n)){const n=[...s[0]].length-1;let r,i,l=n,o=0;const a="*"===s[0][0]?this.rules.inline.emStrongRDelimAst:this.rules.inline.emStrongRDelimUnd;for(a.lastIndex=0,t=t.slice(-1*e.length+n);null!=(s=a.exec(t));){if(r=s[1]||s[2]||s[3]||s[4]||s[5]||s[6],!r)continue;if(i=[...r].length,s[3]||s[4]){l+=i;continue}if((s[5]||s[6])&&n%3&&!((n+i)%3)){o+=i;continue}if(l-=i,l>0)continue;i=Math.min(i,i+l+o);const t=[...s[0]][0].length,a=e.slice(0,n+s.index+t+i);if(Math.min(n,i)%2){const e=a.slice(1,-1);return{type:"em",raw:a,text:e,tokens:this.lexer.inlineTokens(e)}}const c=a.slice(2,-2);return{type:"strong",raw:a,text:c,tokens:this.lexer.inlineTokens(c)}}}}codespan(e){const t=this.rules.inline.code.exec(e);if(t){let e=t[2].replace(/\n/g," ");const n=/[^ ]/.test(e),s=/^ /.test(e)&&/ $/.test(e);return n&&s&&(e=e.substring(1,e.length-1)),e=c(e,!0),{type:"codespan",raw:t[0],text:e}}}br(e){const t=this.rules.inline.br.exec(e);if(t)return{type:"br",raw:t[0]}}del(e){const t=this.rules.inline.del.exec(e);if(t)return{type:"del",raw:t[0],text:t[2],tokens:this.lexer.inlineTokens(t[2])}}autolink(e){const t=this.rules.inline.autolink.exec(e);if(t){let e,n;return"@"===t[2]?(e=c(t[1]),n="mailto:"+e):(e=c(t[1]),n=e),{type:"link",raw:t[0],text:e,href:n,tokens:[{type:"text",raw:e,text:e}]}}}url(e){let t;if(t=this.rules.inline.url.exec(e)){let e,n;if("@"===t[2])e=c(t[0]),n="mailto:"+e;else{let s;do{s=t[0],t[0]=this.rules.inline._backpedal.exec(t[0])?.[0]??""}while(s!==t[0]);e=c(t[0]),n="www."===t[1]?"http://"+t[0]:t[0]}return{type:"link",raw:t[0],text:e,href:n,tokens:[{type:"text",raw:e,text:e}]}}}inlineText(e){const t=this.rules.inline.text.exec(e);if(t){let e;return e=this.lexer.state.inRawBlock?t[0]:c(t[0]),{type:"text",raw:t[0],text:e}}}}const m=/^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/,y=/(?:[*+-]|\d{1,9}[.)])/,$=k(/^(?!bull |blockCode|fences|blockquote|heading|html)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html))+?)\n {0,3}(=+|-+) *(?:\n+|$)/).replace(/bull/g,y).replace(/blockCode/g,/ {4}/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).getRegex(),z=/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/,T=/(?!\s*\])(?:\\.|[^\[\]\\])+/,R=k(/^ {0,3}\[(label)\]: *(?:\n *)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n *)?| *\n *)(title))? *(?:\n+|$)/).replace("label",T).replace("title",/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex(),_=k(/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/).replace(/bull/g,y).getRegex(),A="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",S=/<!--(?:-?>|[\s\S]*?(?:-->|$))/,I=k("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|<![A-Z][\\s\\S]*?(?:>\\n*|$)|<!\\[CDATA\\[[\\s\\S]*?(?:\\]\\]>\\n*|$)|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:(?:\\n *)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$)|</(?!script|pre|style|textarea)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n *)+\\n|$))","i").replace("comment",S).replace("tag",A).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),E=k(z).replace("hr",m).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("|table","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",A).getRegex(),q={blockquote:k(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph",E).getRegex(),code:/^( {4}[^\n]+(?:\n(?: *(?:\n|$))*)?)+/,def:R,fences:/^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/,heading:/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,hr:m,html:I,lheading:$,list:_,newline:/^(?: *(?:\n|$))+/,paragraph:E,table:f,text:/^[^\n]+/},Z=k("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr",m).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("blockquote"," {0,3}>").replace("code"," {4}[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",A).getRegex(),L={...q,table:Z,paragraph:k(z).replace("hr",m).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("table",Z).replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",A).getRegex()},P={...q,html:k("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)|<tag(?:\"[^\"]*\"|'[^']*'|\\s[^'\"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",S).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:f,lheading:/^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,paragraph:k(z).replace("hr",m).replace("heading"," *#{1,6} *[^\n]").replace("lheading",$).replace("|table","").replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").replace("|tag","").getRegex()},Q=/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,v=/^( {2,}|\\)\n(?!\s*$)/,B="\\p{P}\\p{S}",C=k(/^((?![*_])[\spunctuation])/,"u").replace(/punctuation/g,B).getRegex(),M=k(/^(?:\*+(?:((?!\*)[punct])|[^\s*]))|^_+(?:((?!_)[punct])|([^\s_]))/,"u").replace(/punct/g,B).getRegex(),O=k("^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)[punct](\\*+)(?=[\\s]|$)|[^punct\\s](\\*+)(?!\\*)(?=[punct\\s]|$)|(?!\\*)[punct\\s](\\*+)(?=[^punct\\s])|[\\s](\\*+)(?!\\*)(?=[punct])|(?!\\*)[punct](\\*+)(?!\\*)(?=[punct])|[^punct\\s](\\*+)(?=[^punct\\s])","gu").replace(/punct/g,B).getRegex(),D=k("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)[punct](_+)(?=[\\s]|$)|[^punct\\s](_+)(?!_)(?=[punct\\s]|$)|(?!_)[punct\\s](_+)(?=[^punct\\s])|[\\s](_+)(?!_)(?=[punct])|(?!_)[punct](_+)(?!_)(?=[punct])","gu").replace(/punct/g,B).getRegex(),j=k(/\\([punct])/,"gu").replace(/punct/g,B).getRegex(),H=k(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace("scheme",/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace("email",/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex(),U=k(S).replace("(?:--\x3e|$)","--\x3e").getRegex(),X=k("^comment|^</[a-zA-Z][\\w:-]*\\s*>|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^<![a-zA-Z]+\\s[\\s\\S]*?>|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>").replace("comment",U).replace("attribute",/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex(),F=/(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/,N=k(/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/).replace("label",F).replace("href",/<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/).replace("title",/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex(),G=k(/^!?\[(label)\]\[(ref)\]/).replace("label",F).replace("ref",T).getRegex(),J=k(/^!?\[(ref)\](?:\[\])?/).replace("ref",T).getRegex(),K={_backpedal:f,anyPunctuation:j,autolink:H,blockSkip:/\[[^[\]]*?\]\([^\(\)]*?\)|`[^`]*?`|<[^<>]*?>/g,br:v,code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,del:f,emStrongLDelim:M,emStrongRDelimAst:O,emStrongRDelimUnd:D,escape:Q,link:N,nolink:J,punctuation:C,reflink:G,reflinkSearch:k("reflink|nolink(?!\\()","g").replace("reflink",G).replace("nolink",J).getRegex(),tag:X,text:/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<!\[`*_]|\b_|$)|[^ ](?= {2,}\n)))/,url:f},V={...K,link:k(/^!?\[(label)\]\((.*?)\)/).replace("label",F).getRegex(),reflink:k(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",F).getRegex()},W={...K,escape:k(Q).replace("])","~|])").getRegex(),url:k(/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,"i").replace("email",/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/).getRegex(),_backpedal:/(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,del:/^(~~?)(?=[^\s~])([\s\S]*?[^\s~])\1(?=[^~]|$)/,text:/^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\<!\[`*~_]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)))/},Y={...W,br:k(v).replace("{2,}","*").getRegex(),text:k(W.text).replace("\\b_","\\b_| {2,}\\n").replace(/\{2,\}/g,"*").getRegex()},ee={normal:q,gfm:L,pedantic:P},te={normal:K,gfm:W,breaks:Y,pedantic:V};class ne{tokens;options;state;tokenizer;inlineQueue;constructor(t){this.tokens=[],this.tokens.links=Object.create(null),this.options=t||e.defaults,this.options.tokenizer=this.options.tokenizer||new w,this.tokenizer=this.options.tokenizer,this.tokenizer.options=this.options,this.tokenizer.lexer=this,this.inlineQueue=[],this.state={inLink:!1,inRawBlock:!1,top:!0};const n={block:ee.normal,inline:te.normal};this.options.pedantic?(n.block=ee.pedantic,n.inline=te.pedantic):this.options.gfm&&(n.block=ee.gfm,this.options.breaks?n.inline=te.breaks:n.inline=te.gfm),this.tokenizer.rules=n}static get rules(){return{block:ee,inline:te}}static lex(e,t){return new ne(t).lex(e)}static lexInline(e,t){return new ne(t).inlineTokens(e)}lex(e){e=e.replace(/\r\n|\r/g,"\n"),this.blockTokens(e,this.tokens);for(let e=0;e<this.inlineQueue.length;e++){const t=this.inlineQueue[e];this.inlineTokens(t.src,t.tokens)}return this.inlineQueue=[],this.tokens}blockTokens(e,t=[]){let n,s,r,i;for(e=this.options.pedantic?e.replace(/\t/g," ").replace(/^ +$/gm,""):e.replace(/^( *)(\t+)/gm,((e,t,n)=>t+" ".repeat(n.length)));e;)if(!(this.options.extensions&&this.options.extensions.block&&this.options.extensions.block.some((s=>!!(n=s.call({lexer:this},e,t))&&(e=e.substring(n.raw.length),t.push(n),!0)))))if(n=this.tokenizer.space(e))e=e.substring(n.raw.length),1===n.raw.length&&t.length>0?t[t.length-1].raw+="\n":t.push(n);else if(n=this.tokenizer.code(e))e=e.substring(n.raw.length),s=t[t.length-1],!s||"paragraph"!==s.type&&"text"!==s.type?t.push(n):(s.raw+="\n"+n.raw,s.text+="\n"+n.text,this.inlineQueue[this.inlineQueue.length-1].src=s.text);else if(n=this.tokenizer.fences(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.heading(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.hr(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.blockquote(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.list(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.html(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.def(e))e=e.substring(n.raw.length),s=t[t.length-1],!s||"paragraph"!==s.type&&"text"!==s.type?this.tokens.links[n.tag]||(this.tokens.links[n.tag]={href:n.href,title:n.title}):(s.raw+="\n"+n.raw,s.text+="\n"+n.raw,this.inlineQueue[this.inlineQueue.length-1].src=s.text);else if(n=this.tokenizer.table(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.lheading(e))e=e.substring(n.raw.length),t.push(n);else{if(r=e,this.options.extensions&&this.options.extensions.startBlock){let t=1/0;const n=e.slice(1);let s;this.options.extensions.startBlock.forEach((e=>{s=e.call({lexer:this},n),"number"==typeof s&&s>=0&&(t=Math.min(t,s))})),t<1/0&&t>=0&&(r=e.substring(0,t+1))}if(this.state.top&&(n=this.tokenizer.paragraph(r)))s=t[t.length-1],i&&"paragraph"===s.type?(s.raw+="\n"+n.raw,s.text+="\n"+n.text,this.inlineQueue.pop(),this.inlineQueue[this.inlineQueue.length-1].src=s.text):t.push(n),i=r.length!==e.length,e=e.substring(n.raw.length);else if(n=this.tokenizer.text(e))e=e.substring(n.raw.length),s=t[t.length-1],s&&"text"===s.type?(s.raw+="\n"+n.raw,s.text+="\n"+n.text,this.inlineQueue.pop(),this.inlineQueue[this.inlineQueue.length-1].src=s.text):t.push(n);else if(e){const t="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(t);break}throw new Error(t)}}return this.state.top=!0,t}inline(e,t=[]){return this.inlineQueue.push({src:e,tokens:t}),t}inlineTokens(e,t=[]){let n,s,r,i,l,o,a=e;if(this.tokens.links){const e=Object.keys(this.tokens.links);if(e.length>0)for(;null!=(i=this.tokenizer.rules.inline.reflinkSearch.exec(a));)e.includes(i[0].slice(i[0].lastIndexOf("[")+1,-1))&&(a=a.slice(0,i.index)+"["+"a".repeat(i[0].length-2)+"]"+a.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;null!=(i=this.tokenizer.rules.inline.blockSkip.exec(a));)a=a.slice(0,i.index)+"["+"a".repeat(i[0].length-2)+"]"+a.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);for(;null!=(i=this.tokenizer.rules.inline.anyPunctuation.exec(a));)a=a.slice(0,i.index)+"++"+a.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);for(;e;)if(l||(o=""),l=!1,!(this.options.extensions&&this.options.extensions.inline&&this.options.extensions.inline.some((s=>!!(n=s.call({lexer:this},e,t))&&(e=e.substring(n.raw.length),t.push(n),!0)))))if(n=this.tokenizer.escape(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.tag(e))e=e.substring(n.raw.length),s=t[t.length-1],s&&"text"===n.type&&"text"===s.type?(s.raw+=n.raw,s.text+=n.text):t.push(n);else if(n=this.tokenizer.link(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.reflink(e,this.tokens.links))e=e.substring(n.raw.length),s=t[t.length-1],s&&"text"===n.type&&"text"===s.type?(s.raw+=n.raw,s.text+=n.text):t.push(n);else if(n=this.tokenizer.emStrong(e,a,o))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.codespan(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.br(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.del(e))e=e.substring(n.raw.length),t.push(n);else if(n=this.tokenizer.autolink(e))e=e.substring(n.raw.length),t.push(n);else if(this.state.inLink||!(n=this.tokenizer.url(e))){if(r=e,this.options.extensions&&this.options.extensions.startInline){let t=1/0;const n=e.slice(1);let s;this.options.extensions.startInline.forEach((e=>{s=e.call({lexer:this},n),"number"==typeof s&&s>=0&&(t=Math.min(t,s))})),t<1/0&&t>=0&&(r=e.substring(0,t+1))}if(n=this.tokenizer.inlineText(r))e=e.substring(n.raw.length),"_"!==n.raw.slice(-1)&&(o=n.raw.slice(-1)),l=!0,s=t[t.length-1],s&&"text"===s.type?(s.raw+=n.raw,s.text+=n.text):t.push(n);else if(e){const t="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(t);break}throw new Error(t)}}else e=e.substring(n.raw.length),t.push(n);return t}}class se{options;constructor(t){this.options=t||e.defaults}code(e,t,n){const s=(t||"").match(/^\S*/)?.[0];return e=e.replace(/\n$/,"")+"\n",s?'<pre><code class="language-'+c(s)+'">'+(n?e:c(e,!0))+"</code></pre>\n":"<pre><code>"+(n?e:c(e,!0))+"</code></pre>\n"}blockquote(e){return`<blockquote>\n${e}</blockquote>\n`}html(e,t){return e}heading(e,t,n){return`<h${t}>${e}</h${t}>\n`}hr(){return"<hr>\n"}list(e,t,n){const s=t?"ol":"ul";return"<"+s+(t&&1!==n?' start="'+n+'"':"")+">\n"+e+"</"+s+">\n"}listitem(e,t,n){return`<li>${e}</li>\n`}checkbox(e){return"<input "+(e?'checked="" ':"")+'disabled="" type="checkbox">'}paragraph(e){return`<p>${e}</p>\n`}table(e,t){return t&&(t=`<tbody>${t}</tbody>`),"<table>\n<thead>\n"+e+"</thead>\n"+t+"</table>\n"}tablerow(e){return`<tr>\n${e}</tr>\n`}tablecell(e,t){const n=t.header?"th":"td";return(t.align?`<${n} align="${t.align}">`:`<${n}>`)+e+`</${n}>\n`}strong(e){return`<strong>${e}</strong>`}em(e){return`<em>${e}</em>`}codespan(e){return`<code>${e}</code>`}br(){return"<br>"}del(e){return`<del>${e}</del>`}link(e,t,n){const s=g(e);if(null===s)return n;let r='<a href="'+(e=s)+'"';return t&&(r+=' title="'+t+'"'),r+=">"+n+"</a>",r}image(e,t,n){const s=g(e);if(null===s)return n;let r=`<img src="${e=s}" alt="${n}"`;return t&&(r+=` title="${t}"`),r+=">",r}text(e){return e}}class re{strong(e){return e}em(e){return e}codespan(e){return e}del(e){return e}html(e){return e}text(e){return e}link(e,t,n){return""+n}image(e,t,n){return""+n}br(){return""}}class ie{options;renderer;textRenderer;constructor(t){this.options=t||e.defaults,this.options.renderer=this.options.renderer||new se,this.renderer=this.options.renderer,this.renderer.options=this.options,this.textRenderer=new re}static parse(e,t){return new ie(t).parse(e)}static parseInline(e,t){return new ie(t).parseInline(e)}parse(e,t=!0){let n="";for(let s=0;s<e.length;s++){const r=e[s];if(this.options.extensions&&this.options.extensions.renderers&&this.options.extensions.renderers[r.type]){const e=r,t=this.options.extensions.renderers[e.type].call({parser:this},e);if(!1!==t||!["space","hr","heading","code","table","blockquote","list","html","paragraph","text"].includes(e.type)){n+=t||"";continue}}switch(r.type){case"space":continue;case"hr":n+=this.renderer.hr();continue;case"heading":{const e=r;n+=this.renderer.heading(this.parseInline(e.tokens),e.depth,p(this.parseInline(e.tokens,this.textRenderer)));continue}case"code":{const e=r;n+=this.renderer.code(e.text,e.lang,!!e.escaped);continue}case"table":{const e=r;let t="",s="";for(let t=0;t<e.header.length;t++)s+=this.renderer.tablecell(this.parseInline(e.header[t].tokens),{header:!0,align:e.align[t]});t+=this.renderer.tablerow(s);let i="";for(let t=0;t<e.rows.length;t++){const n=e.rows[t];s="";for(let t=0;t<n.length;t++)s+=this.renderer.tablecell(this.parseInline(n[t].tokens),{header:!1,align:e.align[t]});i+=this.renderer.tablerow(s)}n+=this.renderer.table(t,i);continue}case"blockquote":{const e=r,t=this.parse(e.tokens);n+=this.renderer.blockquote(t);continue}case"list":{const e=r,t=e.ordered,s=e.start,i=e.loose;let l="";for(let t=0;t<e.items.length;t++){const n=e.items[t],s=n.checked,r=n.task;let o="";if(n.task){const e=this.renderer.checkbox(!!s);i?n.tokens.length>0&&"paragraph"===n.tokens[0].type?(n.tokens[0].text=e+" "+n.tokens[0].text,n.tokens[0].tokens&&n.tokens[0].tokens.length>0&&"text"===n.tokens[0].tokens[0].type&&(n.tokens[0].tokens[0].text=e+" "+n.tokens[0].tokens[0].text)):n.tokens.unshift({type:"text",text:e+" "}):o+=e+" "}o+=this.parse(n.tokens,i),l+=this.renderer.listitem(o,r,!!s)}n+=this.renderer.list(l,t,s);continue}case"html":{const e=r;n+=this.renderer.html(e.text,e.block);continue}case"paragraph":{const e=r;n+=this.renderer.paragraph(this.parseInline(e.tokens));continue}case"text":{let i=r,l=i.tokens?this.parseInline(i.tokens):i.text;for(;s+1<e.length&&"text"===e[s+1].type;)i=e[++s],l+="\n"+(i.tokens?this.parseInline(i.tokens):i.text);n+=t?this.renderer.paragraph(l):l;continue}default:{const e='Token with "'+r.type+'" type was not found.';if(this.options.silent)return console.error(e),"";throw new Error(e)}}}return n}parseInline(e,t){t=t||this.renderer;let n="";for(let s=0;s<e.length;s++){const r=e[s];if(this.options.extensions&&this.options.extensions.renderers&&this.options.extensions.renderers[r.type]){const e=this.options.extensions.renderers[r.type].call({parser:this},r);if(!1!==e||!["escape","html","link","image","strong","em","codespan","br","del","text"].includes(r.type)){n+=e||"";continue}}switch(r.type){case"escape":{const e=r;n+=t.text(e.text);break}case"html":{const e=r;n+=t.html(e.text);break}case"link":{const e=r;n+=t.link(e.href,e.title,this.parseInline(e.tokens,t));break}case"image":{const e=r;n+=t.image(e.href,e.title,e.text);break}case"strong":{const e=r;n+=t.strong(this.parseInline(e.tokens,t));break}case"em":{const e=r;n+=t.em(this.parseInline(e.tokens,t));break}case"codespan":{const e=r;n+=t.codespan(e.text);break}case"br":n+=t.br();break;case"del":{const e=r;n+=t.del(this.parseInline(e.tokens,t));break}case"text":{const e=r;n+=t.text(e.text);break}default:{const e='Token with "'+r.type+'" type was not found.';if(this.options.silent)return console.error(e),"";throw new Error(e)}}}return n}}class le{options;constructor(t){this.options=t||e.defaults}static passThroughHooks=new Set(["preprocess","postprocess","processAllTokens"]);preprocess(e){return e}postprocess(e){return e}processAllTokens(e){return e}}class oe{defaults={async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null};options=this.setOptions;parse=this.#e(ne.lex,ie.parse);parseInline=this.#e(ne.lexInline,ie.parseInline);Parser=ie;Renderer=se;TextRenderer=re;Lexer=ne;Tokenizer=w;Hooks=le;constructor(...e){this.use(...e)}walkTokens(e,t){let n=[];for(const s of e)switch(n=n.concat(t.call(this,s)),s.type){case"table":{const e=s;for(const s of e.header)n=n.concat(this.walkTokens(s.tokens,t));for(const s of e.rows)for(const e of s)n=n.concat(this.walkTokens(e.tokens,t));break}case"list":{const e=s;n=n.concat(this.walkTokens(e.items,t));break}default:{const e=s;this.defaults.extensions?.childTokens?.[e.type]?this.defaults.extensions.childTokens[e.type].forEach((s=>{const r=e[s].flat(1/0);n=n.concat(this.walkTokens(r,t))})):e.tokens&&(n=n.concat(this.walkTokens(e.tokens,t)))}}return n}use(...e){const t=this.defaults.extensions||{renderers:{},childTokens:{}};return e.forEach((e=>{const n={...e};if(n.async=this.defaults.async||n.async||!1,e.extensions&&(e.extensions.forEach((e=>{if(!e.name)throw new Error("extension name required");if("renderer"in e){const n=t.renderers[e.name];t.renderers[e.name]=n?function(...t){let s=e.renderer.apply(this,t);return!1===s&&(s=n.apply(this,t)),s}:e.renderer}if("tokenizer"in e){if(!e.level||"block"!==e.level&&"inline"!==e.level)throw new Error("extension level must be 'block' or 'inline'");const n=t[e.level];n?n.unshift(e.tokenizer):t[e.level]=[e.tokenizer],e.start&&("block"===e.level?t.startBlock?t.startBlock.push(e.start):t.startBlock=[e.start]:"inline"===e.level&&(t.startInline?t.startInline.push(e.start):t.startInline=[e.start]))}"childTokens"in e&&e.childTokens&&(t.childTokens[e.name]=e.childTokens)})),n.extensions=t),e.renderer){const t=this.defaults.renderer||new se(this.defaults);for(const n in e.renderer){if(!(n in t))throw new Error(`renderer '${n}' does not exist`);if("options"===n)continue;const s=n,r=e.renderer[s],i=t[s];t[s]=(...e)=>{let n=r.apply(t,e);return!1===n&&(n=i.apply(t,e)),n||""}}n.renderer=t}if(e.tokenizer){const t=this.defaults.tokenizer||new w(this.defaults);for(const n in e.tokenizer){if(!(n in t))throw new Error(`tokenizer '${n}' does not exist`);if(["options","rules","lexer"].includes(n))continue;const s=n,r=e.tokenizer[s],i=t[s];t[s]=(...e)=>{let n=r.apply(t,e);return!1===n&&(n=i.apply(t,e)),n}}n.tokenizer=t}if(e.hooks){const t=this.defaults.hooks||new le;for(const n in e.hooks){if(!(n in t))throw new Error(`hook '${n}' does not exist`);if("options"===n)continue;const s=n,r=e.hooks[s],i=t[s];le.passThroughHooks.has(n)?t[s]=e=>{if(this.defaults.async)return Promise.resolve(r.call(t,e)).then((e=>i.call(t,e)));const n=r.call(t,e);return i.call(t,n)}:t[s]=(...e)=>{let n=r.apply(t,e);return!1===n&&(n=i.apply(t,e)),n}}n.hooks=t}if(e.walkTokens){const t=this.defaults.walkTokens,s=e.walkTokens;n.walkTokens=function(e){let n=[];return n.push(s.call(this,e)),t&&(n=n.concat(t.call(this,e))),n}}this.defaults={...this.defaults,...n}})),this}setOptions(e){return this.defaults={...this.defaults,...e},this}lexer(e,t){return ne.lex(e,t??this.defaults)}parser(e,t){return ie.parse(e,t??this.defaults)}#e(e,t){return(n,s)=>{const r={...s},i={...this.defaults,...r};!0===this.defaults.async&&!1===r.async&&(i.silent||console.warn("marked(): The async option was set to true by an extension. The async: false option sent to parse will be ignored."),i.async=!0);const l=this.#t(!!i.silent,!!i.async);if(null==n)return l(new Error("marked(): input parameter is undefined or null"));if("string"!=typeof n)return l(new Error("marked(): input parameter is of type "+Object.prototype.toString.call(n)+", string expected"));if(i.hooks&&(i.hooks.options=i),i.async)return Promise.resolve(i.hooks?i.hooks.preprocess(n):n).then((t=>e(t,i))).then((e=>i.hooks?i.hooks.processAllTokens(e):e)).then((e=>i.walkTokens?Promise.all(this.walkTokens(e,i.walkTokens)).then((()=>e)):e)).then((e=>t(e,i))).then((e=>i.hooks?i.hooks.postprocess(e):e)).catch(l);try{i.hooks&&(n=i.hooks.preprocess(n));let s=e(n,i);i.hooks&&(s=i.hooks.processAllTokens(s)),i.walkTokens&&this.walkTokens(s,i.walkTokens);let r=t(s,i);return i.hooks&&(r=i.hooks.postprocess(r)),r}catch(e){return l(e)}}}#t(e,t){return n=>{if(n.message+="\nPlease report this to https://github.com/markedjs/marked.",e){const e="<p>An error occurred:</p><pre>"+c(n.message+"",!0)+"</pre>";return t?Promise.resolve(e):e}if(t)return Promise.reject(n);throw n}}}const ae=new oe;function ce(e,t){return ae.parse(e,t)}ce.options=ce.setOptions=function(e){return ae.setOptions(e),ce.defaults=ae.defaults,n(ce.defaults),ce},ce.getDefaults=t,ce.defaults=e.defaults,ce.use=function(...e){return ae.use(...e),ce.defaults=ae.defaults,n(ce.defaults),ce},ce.walkTokens=function(e,t){return ae.walkTokens(e,t)},ce.parseInline=ae.parseInline,ce.Parser=ie,ce.parser=ie.parse,ce.Renderer=se,ce.TextRenderer=re,ce.Lexer=ne,ce.lexer=ne.lex,ce.Tokenizer=w,ce.Hooks=le,ce.parse=ce;const he=ce.options,pe=ce.setOptions,ue=ce.use,ke=ce.walkTokens,ge=ce.parseInline,fe=ce,de=ie.parse,xe=ne.lex;e.Hooks=le,e.Lexer=ne,e.Marked=oe,e.Parser=ie,e.Renderer=se,e.TextRenderer=re,e.Tokenizer=w,e.getDefaults=t,e.lexer=xe,e.marked=ce,e.options=he,e.parse=fe,e.parseInline=ge,e.parser=de,e.setOptions=pe,e.use=ue,e.walkTokens=ke}));
vendor/purify.min.js ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ /*! @license DOMPurify 3.1.6 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.1.6/LICENSE */
2
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).DOMPurify=t()}(this,(function(){"use strict";const{entries:e,setPrototypeOf:t,isFrozen:n,getPrototypeOf:o,getOwnPropertyDescriptor:r}=Object;let{freeze:i,seal:a,create:l}=Object,{apply:c,construct:s}="undefined"!=typeof Reflect&&Reflect;i||(i=function(e){return e}),a||(a=function(e){return e}),c||(c=function(e,t,n){return e.apply(t,n)}),s||(s=function(e,t){return new e(...t)});const u=b(Array.prototype.forEach),m=b(Array.prototype.pop),p=b(Array.prototype.push),f=b(String.prototype.toLowerCase),d=b(String.prototype.toString),h=b(String.prototype.match),g=b(String.prototype.replace),T=b(String.prototype.indexOf),y=b(String.prototype.trim),E=b(Object.prototype.hasOwnProperty),_=b(RegExp.prototype.test),A=(N=TypeError,function(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return s(N,t)});var N;function b(e){return function(t){for(var n=arguments.length,o=new Array(n>1?n-1:0),r=1;r<n;r++)o[r-1]=arguments[r];return c(e,t,o)}}function S(e,o){let r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:f;t&&t(e,null);let i=o.length;for(;i--;){let t=o[i];if("string"==typeof t){const e=r(t);e!==t&&(n(o)||(o[i]=e),t=e)}e[t]=!0}return e}function R(e){for(let t=0;t<e.length;t++){E(e,t)||(e[t]=null)}return e}function w(t){const n=l(null);for(const[o,r]of e(t)){E(t,o)&&(Array.isArray(r)?n[o]=R(r):r&&"object"==typeof r&&r.constructor===Object?n[o]=w(r):n[o]=r)}return n}function C(e,t){for(;null!==e;){const n=r(e,t);if(n){if(n.get)return b(n.get);if("function"==typeof n.value)return b(n.value)}e=o(e)}return function(){return null}}const L=i(["a","abbr","acronym","address","area","article","aside","audio","b","bdi","bdo","big","blink","blockquote","body","br","button","canvas","caption","center","cite","code","col","colgroup","content","data","datalist","dd","decorator","del","details","dfn","dialog","dir","div","dl","dt","element","em","fieldset","figcaption","figure","font","footer","form","h1","h2","h3","h4","h5","h6","head","header","hgroup","hr","html","i","img","input","ins","kbd","label","legend","li","main","map","mark","marquee","menu","menuitem","meter","nav","nobr","ol","optgroup","option","output","p","picture","pre","progress","q","rp","rt","ruby","s","samp","section","select","shadow","small","source","spacer","span","strike","strong","style","sub","summary","sup","table","tbody","td","template","textarea","tfoot","th","thead","time","tr","track","tt","u","ul","var","video","wbr"]),D=i(["svg","a","altglyph","altglyphdef","altglyphitem","animatecolor","animatemotion","animatetransform","circle","clippath","defs","desc","ellipse","filter","font","g","glyph","glyphref","hkern","image","line","lineargradient","marker","mask","metadata","mpath","path","pattern","polygon","polyline","radialgradient","rect","stop","style","switch","symbol","text","textpath","title","tref","tspan","view","vkern"]),v=i(["feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feDistantLight","feDropShadow","feFlood","feFuncA","feFuncB","feFuncG","feFuncR","feGaussianBlur","feImage","feMerge","feMergeNode","feMorphology","feOffset","fePointLight","feSpecularLighting","feSpotLight","feTile","feTurbulence"]),O=i(["animate","color-profile","cursor","discard","font-face","font-face-format","font-face-name","font-face-src","font-face-uri","foreignobject","hatch","hatchpath","mesh","meshgradient","meshpatch","meshrow","missing-glyph","script","set","solidcolor","unknown","use"]),x=i(["math","menclose","merror","mfenced","mfrac","mglyph","mi","mlabeledtr","mmultiscripts","mn","mo","mover","mpadded","mphantom","mroot","mrow","ms","mspace","msqrt","mstyle","msub","msup","msubsup","mtable","mtd","mtext","mtr","munder","munderover","mprescripts"]),k=i(["maction","maligngroup","malignmark","mlongdiv","mscarries","mscarry","msgroup","mstack","msline","msrow","semantics","annotation","annotation-xml","mprescripts","none"]),M=i(["#text"]),I=i(["accept","action","align","alt","autocapitalize","autocomplete","autopictureinpicture","autoplay","background","bgcolor","border","capture","cellpadding","cellspacing","checked","cite","class","clear","color","cols","colspan","controls","controlslist","coords","crossorigin","datetime","decoding","default","dir","disabled","disablepictureinpicture","disableremoteplayback","download","draggable","enctype","enterkeyhint","face","for","headers","height","hidden","high","href","hreflang","id","inputmode","integrity","ismap","kind","label","lang","list","loading","loop","low","max","maxlength","media","method","min","minlength","multiple","muted","name","nonce","noshade","novalidate","nowrap","open","optimum","pattern","placeholder","playsinline","popover","popovertarget","popovertargetaction","poster","preload","pubdate","radiogroup","readonly","rel","required","rev","reversed","role","rows","rowspan","spellcheck","scope","selected","shape","size","sizes","span","srclang","start","src","srcset","step","style","summary","tabindex","title","translate","type","usemap","valign","value","width","wrap","xmlns","slot"]),U=i(["accent-height","accumulate","additive","alignment-baseline","ascent","attributename","attributetype","azimuth","basefrequency","baseline-shift","begin","bias","by","class","clip","clippathunits","clip-path","clip-rule","color","color-interpolation","color-interpolation-filters","color-profile","color-rendering","cx","cy","d","dx","dy","diffuseconstant","direction","display","divisor","dur","edgemode","elevation","end","fill","fill-opacity","fill-rule","filter","filterunits","flood-color","flood-opacity","font-family","font-size","font-size-adjust","font-stretch","font-style","font-variant","font-weight","fx","fy","g1","g2","glyph-name","glyphref","gradientunits","gradienttransform","height","href","id","image-rendering","in","in2","k","k1","k2","k3","k4","kerning","keypoints","keysplines","keytimes","lang","lengthadjust","letter-spacing","kernelmatrix","kernelunitlength","lighting-color","local","marker-end","marker-mid","marker-start","markerheight","markerunits","markerwidth","maskcontentunits","maskunits","max","mask","media","method","mode","min","name","numoctaves","offset","operator","opacity","order","orient","orientation","origin","overflow","paint-order","path","pathlength","patterncontentunits","patterntransform","patternunits","points","preservealpha","preserveaspectratio","primitiveunits","r","rx","ry","radius","refx","refy","repeatcount","repeatdur","restart","result","rotate","scale","seed","shape-rendering","specularconstant","specularexponent","spreadmethod","startoffset","stddeviation","stitchtiles","stop-color","stop-opacity","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke","stroke-width","style","surfacescale","systemlanguage","tabindex","targetx","targety","transform","transform-origin","text-anchor","text-decoration","text-rendering","textlength","type","u1","u2","unicode","values","viewbox","visibility","version","vert-adv-y","vert-origin-x","vert-origin-y","width","word-spacing","wrap","writing-mode","xchannelselector","ychannelselector","x","x1","x2","xmlns","y","y1","y2","z","zoomandpan"]),P=i(["accent","accentunder","align","bevelled","close","columnsalign","columnlines","columnspan","denomalign","depth","dir","display","displaystyle","encoding","fence","frame","height","href","id","largeop","length","linethickness","lspace","lquote","mathbackground","mathcolor","mathsize","mathvariant","maxsize","minsize","movablelimits","notation","numalign","open","rowalign","rowlines","rowspacing","rowspan","rspace","rquote","scriptlevel","scriptminsize","scriptsizemultiplier","selection","separator","separators","stretchy","subscriptshift","supscriptshift","symmetric","voffset","width","xmlns"]),F=i(["xlink:href","xml:id","xlink:title","xml:space","xmlns:xlink"]),H=a(/\{\{[\w\W]*|[\w\W]*\}\}/gm),z=a(/<%[\w\W]*|[\w\W]*%>/gm),B=a(/\${[\w\W]*}/gm),W=a(/^data-[\-\w.\u00B7-\uFFFF]/),G=a(/^aria-[\-\w]+$/),Y=a(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),j=a(/^(?:\w+script|data):/i),X=a(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),q=a(/^html$/i),$=a(/^[a-z][.\w]*(-[.\w]+)+$/i);var K=Object.freeze({__proto__:null,MUSTACHE_EXPR:H,ERB_EXPR:z,TMPLIT_EXPR:B,DATA_ATTR:W,ARIA_ATTR:G,IS_ALLOWED_URI:Y,IS_SCRIPT_OR_DATA:j,ATTR_WHITESPACE:X,DOCTYPE_NAME:q,CUSTOM_ELEMENT:$});const V=1,Z=3,J=7,Q=8,ee=9,te=function(){return"undefined"==typeof window?null:window};var ne=function t(){let n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:te();const o=e=>t(e);if(o.version="3.1.6",o.removed=[],!n||!n.document||n.document.nodeType!==ee)return o.isSupported=!1,o;let{document:r}=n;const a=r,c=a.currentScript,{DocumentFragment:s,HTMLTemplateElement:N,Node:b,Element:R,NodeFilter:H,NamedNodeMap:z=n.NamedNodeMap||n.MozNamedAttrMap,HTMLFormElement:B,DOMParser:W,trustedTypes:G}=n,j=R.prototype,X=C(j,"cloneNode"),$=C(j,"remove"),ne=C(j,"nextSibling"),oe=C(j,"childNodes"),re=C(j,"parentNode");if("function"==typeof N){const e=r.createElement("template");e.content&&e.content.ownerDocument&&(r=e.content.ownerDocument)}let ie,ae="";const{implementation:le,createNodeIterator:ce,createDocumentFragment:se,getElementsByTagName:ue}=r,{importNode:me}=a;let pe={};o.isSupported="function"==typeof e&&"function"==typeof re&&le&&void 0!==le.createHTMLDocument;const{MUSTACHE_EXPR:fe,ERB_EXPR:de,TMPLIT_EXPR:he,DATA_ATTR:ge,ARIA_ATTR:Te,IS_SCRIPT_OR_DATA:ye,ATTR_WHITESPACE:Ee,CUSTOM_ELEMENT:_e}=K;let{IS_ALLOWED_URI:Ae}=K,Ne=null;const be=S({},[...L,...D,...v,...x,...M]);let Se=null;const Re=S({},[...I,...U,...P,...F]);let we=Object.seal(l(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),Ce=null,Le=null,De=!0,ve=!0,Oe=!1,xe=!0,ke=!1,Me=!0,Ie=!1,Ue=!1,Pe=!1,Fe=!1,He=!1,ze=!1,Be=!0,We=!1,Ge=!0,Ye=!1,je={},Xe=null;const qe=S({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]);let $e=null;const Ke=S({},["audio","video","img","source","image","track"]);let Ve=null;const Ze=S({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),Je="http://www.w3.org/1998/Math/MathML",Qe="http://www.w3.org/2000/svg",et="http://www.w3.org/1999/xhtml";let tt=et,nt=!1,ot=null;const rt=S({},[Je,Qe,et],d);let it=null;const at=["application/xhtml+xml","text/html"];let lt=null,ct=null;const st=r.createElement("form"),ut=function(e){return e instanceof RegExp||e instanceof Function},mt=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if(!ct||ct!==e){if(e&&"object"==typeof e||(e={}),e=w(e),it=-1===at.indexOf(e.PARSER_MEDIA_TYPE)?"text/html":e.PARSER_MEDIA_TYPE,lt="application/xhtml+xml"===it?d:f,Ne=E(e,"ALLOWED_TAGS")?S({},e.ALLOWED_TAGS,lt):be,Se=E(e,"ALLOWED_ATTR")?S({},e.ALLOWED_ATTR,lt):Re,ot=E(e,"ALLOWED_NAMESPACES")?S({},e.ALLOWED_NAMESPACES,d):rt,Ve=E(e,"ADD_URI_SAFE_ATTR")?S(w(Ze),e.ADD_URI_SAFE_ATTR,lt):Ze,$e=E(e,"ADD_DATA_URI_TAGS")?S(w(Ke),e.ADD_DATA_URI_TAGS,lt):Ke,Xe=E(e,"FORBID_CONTENTS")?S({},e.FORBID_CONTENTS,lt):qe,Ce=E(e,"FORBID_TAGS")?S({},e.FORBID_TAGS,lt):{},Le=E(e,"FORBID_ATTR")?S({},e.FORBID_ATTR,lt):{},je=!!E(e,"USE_PROFILES")&&e.USE_PROFILES,De=!1!==e.ALLOW_ARIA_ATTR,ve=!1!==e.ALLOW_DATA_ATTR,Oe=e.ALLOW_UNKNOWN_PROTOCOLS||!1,xe=!1!==e.ALLOW_SELF_CLOSE_IN_ATTR,ke=e.SAFE_FOR_TEMPLATES||!1,Me=!1!==e.SAFE_FOR_XML,Ie=e.WHOLE_DOCUMENT||!1,Fe=e.RETURN_DOM||!1,He=e.RETURN_DOM_FRAGMENT||!1,ze=e.RETURN_TRUSTED_TYPE||!1,Pe=e.FORCE_BODY||!1,Be=!1!==e.SANITIZE_DOM,We=e.SANITIZE_NAMED_PROPS||!1,Ge=!1!==e.KEEP_CONTENT,Ye=e.IN_PLACE||!1,Ae=e.ALLOWED_URI_REGEXP||Y,tt=e.NAMESPACE||et,we=e.CUSTOM_ELEMENT_HANDLING||{},e.CUSTOM_ELEMENT_HANDLING&&ut(e.CUSTOM_ELEMENT_HANDLING.tagNameCheck)&&(we.tagNameCheck=e.CUSTOM_ELEMENT_HANDLING.tagNameCheck),e.CUSTOM_ELEMENT_HANDLING&&ut(e.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)&&(we.attributeNameCheck=e.CUSTOM_ELEMENT_HANDLING.attributeNameCheck),e.CUSTOM_ELEMENT_HANDLING&&"boolean"==typeof e.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements&&(we.allowCustomizedBuiltInElements=e.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements),ke&&(ve=!1),He&&(Fe=!0),je&&(Ne=S({},M),Se=[],!0===je.html&&(S(Ne,L),S(Se,I)),!0===je.svg&&(S(Ne,D),S(Se,U),S(Se,F)),!0===je.svgFilters&&(S(Ne,v),S(Se,U),S(Se,F)),!0===je.mathMl&&(S(Ne,x),S(Se,P),S(Se,F))),e.ADD_TAGS&&(Ne===be&&(Ne=w(Ne)),S(Ne,e.ADD_TAGS,lt)),e.ADD_ATTR&&(Se===Re&&(Se=w(Se)),S(Se,e.ADD_ATTR,lt)),e.ADD_URI_SAFE_ATTR&&S(Ve,e.ADD_URI_SAFE_ATTR,lt),e.FORBID_CONTENTS&&(Xe===qe&&(Xe=w(Xe)),S(Xe,e.FORBID_CONTENTS,lt)),Ge&&(Ne["#text"]=!0),Ie&&S(Ne,["html","head","body"]),Ne.table&&(S(Ne,["tbody"]),delete Ce.tbody),e.TRUSTED_TYPES_POLICY){if("function"!=typeof e.TRUSTED_TYPES_POLICY.createHTML)throw A('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');if("function"!=typeof e.TRUSTED_TYPES_POLICY.createScriptURL)throw A('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');ie=e.TRUSTED_TYPES_POLICY,ae=ie.createHTML("")}else void 0===ie&&(ie=function(e,t){if("object"!=typeof e||"function"!=typeof e.createPolicy)return null;let n=null;const o="data-tt-policy-suffix";t&&t.hasAttribute(o)&&(n=t.getAttribute(o));const r="dompurify"+(n?"#"+n:"");try{return e.createPolicy(r,{createHTML:e=>e,createScriptURL:e=>e})}catch(e){return console.warn("TrustedTypes policy "+r+" could not be created."),null}}(G,c)),null!==ie&&"string"==typeof ae&&(ae=ie.createHTML(""));i&&i(e),ct=e}},pt=S({},["mi","mo","mn","ms","mtext"]),ft=S({},["foreignobject","annotation-xml"]),dt=S({},["title","style","font","a","script"]),ht=S({},[...D,...v,...O]),gt=S({},[...x,...k]),Tt=function(e){p(o.removed,{element:e});try{re(e).removeChild(e)}catch(t){$(e)}},yt=function(e,t){try{p(o.removed,{attribute:t.getAttributeNode(e),from:t})}catch(e){p(o.removed,{attribute:null,from:t})}if(t.removeAttribute(e),"is"===e&&!Se[e])if(Fe||He)try{Tt(t)}catch(e){}else try{t.setAttribute(e,"")}catch(e){}},Et=function(e){let t=null,n=null;if(Pe)e="<remove></remove>"+e;else{const t=h(e,/^[\r\n\t ]+/);n=t&&t[0]}"application/xhtml+xml"===it&&tt===et&&(e='<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>'+e+"</body></html>");const o=ie?ie.createHTML(e):e;if(tt===et)try{t=(new W).parseFromString(o,it)}catch(e){}if(!t||!t.documentElement){t=le.createDocument(tt,"template",null);try{t.documentElement.innerHTML=nt?ae:o}catch(e){}}const i=t.body||t.documentElement;return e&&n&&i.insertBefore(r.createTextNode(n),i.childNodes[0]||null),tt===et?ue.call(t,Ie?"html":"body")[0]:Ie?t.documentElement:i},_t=function(e){return ce.call(e.ownerDocument||e,e,H.SHOW_ELEMENT|H.SHOW_COMMENT|H.SHOW_TEXT|H.SHOW_PROCESSING_INSTRUCTION|H.SHOW_CDATA_SECTION,null)},At=function(e){return e instanceof B&&("string"!=typeof e.nodeName||"string"!=typeof e.textContent||"function"!=typeof e.removeChild||!(e.attributes instanceof z)||"function"!=typeof e.removeAttribute||"function"!=typeof e.setAttribute||"string"!=typeof e.namespaceURI||"function"!=typeof e.insertBefore||"function"!=typeof e.hasChildNodes)},Nt=function(e){return"function"==typeof b&&e instanceof b},bt=function(e,t,n){pe[e]&&u(pe[e],(e=>{e.call(o,t,n,ct)}))},St=function(e){let t=null;if(bt("beforeSanitizeElements",e,null),At(e))return Tt(e),!0;const n=lt(e.nodeName);if(bt("uponSanitizeElement",e,{tagName:n,allowedTags:Ne}),e.hasChildNodes()&&!Nt(e.firstElementChild)&&_(/<[/\w]/g,e.innerHTML)&&_(/<[/\w]/g,e.textContent))return Tt(e),!0;if(e.nodeType===J)return Tt(e),!0;if(Me&&e.nodeType===Q&&_(/<[/\w]/g,e.data))return Tt(e),!0;if(!Ne[n]||Ce[n]){if(!Ce[n]&&wt(n)){if(we.tagNameCheck instanceof RegExp&&_(we.tagNameCheck,n))return!1;if(we.tagNameCheck instanceof Function&&we.tagNameCheck(n))return!1}if(Ge&&!Xe[n]){const t=re(e)||e.parentNode,n=oe(e)||e.childNodes;if(n&&t){for(let o=n.length-1;o>=0;--o){const r=X(n[o],!0);r.__removalCount=(e.__removalCount||0)+1,t.insertBefore(r,ne(e))}}}return Tt(e),!0}return e instanceof R&&!function(e){let t=re(e);t&&t.tagName||(t={namespaceURI:tt,tagName:"template"});const n=f(e.tagName),o=f(t.tagName);return!!ot[e.namespaceURI]&&(e.namespaceURI===Qe?t.namespaceURI===et?"svg"===n:t.namespaceURI===Je?"svg"===n&&("annotation-xml"===o||pt[o]):Boolean(ht[n]):e.namespaceURI===Je?t.namespaceURI===et?"math"===n:t.namespaceURI===Qe?"math"===n&&ft[o]:Boolean(gt[n]):e.namespaceURI===et?!(t.namespaceURI===Qe&&!ft[o])&&!(t.namespaceURI===Je&&!pt[o])&&!gt[n]&&(dt[n]||!ht[n]):!("application/xhtml+xml"!==it||!ot[e.namespaceURI]))}(e)?(Tt(e),!0):"noscript"!==n&&"noembed"!==n&&"noframes"!==n||!_(/<\/no(script|embed|frames)/i,e.innerHTML)?(ke&&e.nodeType===Z&&(t=e.textContent,u([fe,de,he],(e=>{t=g(t,e," ")})),e.textContent!==t&&(p(o.removed,{element:e.cloneNode()}),e.textContent=t)),bt("afterSanitizeElements",e,null),!1):(Tt(e),!0)},Rt=function(e,t,n){if(Be&&("id"===t||"name"===t)&&(n in r||n in st))return!1;if(ve&&!Le[t]&&_(ge,t));else if(De&&_(Te,t));else if(!Se[t]||Le[t]){if(!(wt(e)&&(we.tagNameCheck instanceof RegExp&&_(we.tagNameCheck,e)||we.tagNameCheck instanceof Function&&we.tagNameCheck(e))&&(we.attributeNameCheck instanceof RegExp&&_(we.attributeNameCheck,t)||we.attributeNameCheck instanceof Function&&we.attributeNameCheck(t))||"is"===t&&we.allowCustomizedBuiltInElements&&(we.tagNameCheck instanceof RegExp&&_(we.tagNameCheck,n)||we.tagNameCheck instanceof Function&&we.tagNameCheck(n))))return!1}else if(Ve[t]);else if(_(Ae,g(n,Ee,"")));else if("src"!==t&&"xlink:href"!==t&&"href"!==t||"script"===e||0!==T(n,"data:")||!$e[e]){if(Oe&&!_(ye,g(n,Ee,"")));else if(n)return!1}else;return!0},wt=function(e){return"annotation-xml"!==e&&h(e,_e)},Ct=function(e){bt("beforeSanitizeAttributes",e,null);const{attributes:t}=e;if(!t)return;const n={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:Se};let r=t.length;for(;r--;){const i=t[r],{name:a,namespaceURI:l,value:c}=i,s=lt(a);let p="value"===a?c:y(c);if(n.attrName=s,n.attrValue=p,n.keepAttr=!0,n.forceKeepAttr=void 0,bt("uponSanitizeAttribute",e,n),p=n.attrValue,Me&&_(/((--!?|])>)|<\/(style|title)/i,p)){yt(a,e);continue}if(n.forceKeepAttr)continue;if(yt(a,e),!n.keepAttr)continue;if(!xe&&_(/\/>/i,p)){yt(a,e);continue}ke&&u([fe,de,he],(e=>{p=g(p,e," ")}));const f=lt(e.nodeName);if(Rt(f,s,p)){if(!We||"id"!==s&&"name"!==s||(yt(a,e),p="user-content-"+p),ie&&"object"==typeof G&&"function"==typeof G.getAttributeType)if(l);else switch(G.getAttributeType(f,s)){case"TrustedHTML":p=ie.createHTML(p);break;case"TrustedScriptURL":p=ie.createScriptURL(p)}try{l?e.setAttributeNS(l,a,p):e.setAttribute(a,p),At(e)?Tt(e):m(o.removed)}catch(e){}}}bt("afterSanitizeAttributes",e,null)},Lt=function e(t){let n=null;const o=_t(t);for(bt("beforeSanitizeShadowDOM",t,null);n=o.nextNode();)bt("uponSanitizeShadowNode",n,null),St(n)||(n.content instanceof s&&e(n.content),Ct(n));bt("afterSanitizeShadowDOM",t,null)};return o.sanitize=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=null,r=null,i=null,l=null;if(nt=!e,nt&&(e="\x3c!--\x3e"),"string"!=typeof e&&!Nt(e)){if("function"!=typeof e.toString)throw A("toString is not a function");if("string"!=typeof(e=e.toString()))throw A("dirty is not a string, aborting")}if(!o.isSupported)return e;if(Ue||mt(t),o.removed=[],"string"==typeof e&&(Ye=!1),Ye){if(e.nodeName){const t=lt(e.nodeName);if(!Ne[t]||Ce[t])throw A("root node is forbidden and cannot be sanitized in-place")}}else if(e instanceof b)n=Et("\x3c!----\x3e"),r=n.ownerDocument.importNode(e,!0),r.nodeType===V&&"BODY"===r.nodeName||"HTML"===r.nodeName?n=r:n.appendChild(r);else{if(!Fe&&!ke&&!Ie&&-1===e.indexOf("<"))return ie&&ze?ie.createHTML(e):e;if(n=Et(e),!n)return Fe?null:ze?ae:""}n&&Pe&&Tt(n.firstChild);const c=_t(Ye?e:n);for(;i=c.nextNode();)St(i)||(i.content instanceof s&&Lt(i.content),Ct(i));if(Ye)return e;if(Fe){if(He)for(l=se.call(n.ownerDocument);n.firstChild;)l.appendChild(n.firstChild);else l=n;return(Se.shadowroot||Se.shadowrootmode)&&(l=me.call(a,l,!0)),l}let m=Ie?n.outerHTML:n.innerHTML;return Ie&&Ne["!doctype"]&&n.ownerDocument&&n.ownerDocument.doctype&&n.ownerDocument.doctype.name&&_(q,n.ownerDocument.doctype.name)&&(m="<!DOCTYPE "+n.ownerDocument.doctype.name+">\n"+m),ke&&u([fe,de,he],(e=>{m=g(m,e," ")})),ie&&ze?ie.createHTML(m):m},o.setConfig=function(){mt(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{}),Ue=!0},o.clearConfig=function(){ct=null,Ue=!1},o.isValidAttribute=function(e,t,n){ct||mt({});const o=lt(e),r=lt(t);return Rt(o,r,n)},o.addHook=function(e,t){"function"==typeof t&&(pe[e]=pe[e]||[],p(pe[e],t))},o.removeHook=function(e){if(pe[e])return m(pe[e])},o.removeHooks=function(e){pe[e]&&(pe[e]=[])},o.removeAllHooks=function(){pe={}},o}();return ne}));
3
+ //# sourceMappingURL=purify.min.js.map