entari-plugin-hyw 3.2.105__py3-none-any.whl → 3.2.107__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of entari-plugin-hyw might be problematic. Click here for more details.
- entari_plugin_hyw/__init__.py +97 -1
- entari_plugin_hyw/assets/libs/tailwind.css +1 -1
- entari_plugin_hyw/assets/package-lock.json +953 -0
- entari_plugin_hyw/assets/package.json +16 -0
- entari_plugin_hyw/assets/tailwind.config.js +1 -1
- entari_plugin_hyw/assets/tailwind.input.css +8 -8
- entari_plugin_hyw/assets/template.html.bak +157 -0
- entari_plugin_hyw/assets/template.j2 +259 -0
- entari_plugin_hyw/core/pipeline.py +41 -17
- entari_plugin_hyw/core/render.py +270 -683
- entari_plugin_hyw/core/render.py.bak +926 -0
- entari_plugin_hyw/utils/prompts.py +5 -4
- {entari_plugin_hyw-3.2.105.dist-info → entari_plugin_hyw-3.2.107.dist-info}/METADATA +2 -1
- {entari_plugin_hyw-3.2.105.dist-info → entari_plugin_hyw-3.2.107.dist-info}/RECORD +16 -11
- {entari_plugin_hyw-3.2.105.dist-info → entari_plugin_hyw-3.2.107.dist-info}/WHEEL +0 -0
- {entari_plugin_hyw-3.2.105.dist-info → entari_plugin_hyw-3.2.107.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "assets",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"main": "tailwind.config.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [],
|
|
10
|
+
"author": "",
|
|
11
|
+
"license": "ISC",
|
|
12
|
+
"type": "commonjs",
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"tailwindcss": "^3.4.17"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -51,7 +51,7 @@ pre::-webkit-scrollbar-thumb {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
.markdown-body a {
|
|
54
|
-
@apply text-
|
|
54
|
+
@apply text-blue-600 underline decoration-blue-300 underline-offset-2 hover:text-blue-700 hover:decoration-blue-700 transition-colors;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
.markdown-body blockquote {
|
|
@@ -109,11 +109,11 @@ pre::-webkit-scrollbar-thumb {
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
.markdown-body code {
|
|
112
|
-
@apply bg-gray-100 px-1.5 py-0.5 rounded text-sm font-mono text-
|
|
112
|
+
@apply bg-gray-100 px-1.5 py-0.5 rounded text-sm font-mono text-gray-700;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
.markdown-body pre {
|
|
116
|
-
@apply bg-
|
|
116
|
+
@apply bg-gray-50 p-4 rounded-lg overflow-x-auto mb-4 text-sm leading-normal border border-gray-100 shadow-sm;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
.markdown-body pre code {
|
|
@@ -159,12 +159,12 @@ pre::-webkit-scrollbar-thumb {
|
|
|
159
159
|
display: inline-flex;
|
|
160
160
|
align-items: center;
|
|
161
161
|
justify-content: center;
|
|
162
|
-
vertical-align:
|
|
162
|
+
vertical-align: baseline;
|
|
163
163
|
font-size: 0.75em;
|
|
164
164
|
font-weight: bold;
|
|
165
|
-
color: #
|
|
166
|
-
background-color: #
|
|
167
|
-
border: 1px solid #
|
|
165
|
+
color: #4b5563;
|
|
166
|
+
background-color: #f3f4f6;
|
|
167
|
+
border: 1px solid #e5e7eb;
|
|
168
168
|
border-radius: 9999px;
|
|
169
169
|
width: 1.4em;
|
|
170
170
|
height: 1.4em;
|
|
@@ -178,7 +178,7 @@ pre::-webkit-scrollbar-thumb {
|
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
.citation-ref:hover {
|
|
181
|
-
background-color: #
|
|
181
|
+
background-color: #4b5563;
|
|
182
182
|
color: white;
|
|
183
183
|
text-decoration: none !important;
|
|
184
184
|
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<title>Entari Render</title>
|
|
8
|
+
<!-- @formatter:off -->
|
|
9
|
+
<!-- prettier-ignore -->
|
|
10
|
+
<!-- IMPORTANT: Asset placeholders MUST be {{ name }} on single line! -->
|
|
11
|
+
<style>
|
|
12
|
+
{
|
|
13
|
+
{
|
|
14
|
+
tailwind_css
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
</style>
|
|
18
|
+
<style>
|
|
19
|
+
{
|
|
20
|
+
{
|
|
21
|
+
highlight_css
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
</style>
|
|
25
|
+
<script>{ { highlight_js } }</script>
|
|
26
|
+
<style>
|
|
27
|
+
{
|
|
28
|
+
{
|
|
29
|
+
katex_css
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
</style>
|
|
33
|
+
<script>{ { katex_js } }</script>
|
|
34
|
+
<script>{ { katex_auto_render_js } }</script>
|
|
35
|
+
<!-- @formatter:on -->
|
|
36
|
+
<style>
|
|
37
|
+
#markdown-content h1,
|
|
38
|
+
#markdown-content h2,
|
|
39
|
+
#markdown-content h3,
|
|
40
|
+
#markdown-content h4,
|
|
41
|
+
#markdown-content h5,
|
|
42
|
+
#markdown-content h6 {
|
|
43
|
+
color: #db2777;
|
|
44
|
+
/* text-pink-600 */
|
|
45
|
+
}
|
|
46
|
+
</style>
|
|
47
|
+
</head>
|
|
48
|
+
|
|
49
|
+
<body class="bg-[#f2f2f2] p-0 box-border m-0 font-sans text-gray-800">
|
|
50
|
+
<div id="main-container" class="w-full max-w-[450px] flex flex-col gap-4 mx-auto bg-[#f2f2f2] p-0 font-sans h-fit">
|
|
51
|
+
<!-- Response Card (Content First) -->
|
|
52
|
+
<div class="bg-[#f2f2f2] rounded-2xl p-5 overflow-hidden">
|
|
53
|
+
<div id="markdown-content" class="markdown-body text-[15px] leading-relaxed text-gray-800">
|
|
54
|
+
{{ content_html }}
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
<!-- Speculation Card (Optional) -->
|
|
61
|
+
{{ suggestions }}
|
|
62
|
+
|
|
63
|
+
<!-- Model Header (Moved to Bottom) -->
|
|
64
|
+
<div class="bg-[#f2f2f2] rounded-2xl p-5 overflow-hidden">
|
|
65
|
+
{{ response_header }}
|
|
66
|
+
<!-- Pipeline Stages -->
|
|
67
|
+
{{ stages }}
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<!-- Stats Footer -->
|
|
71
|
+
{{ stats }}
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<script>window.REFERENCES = {{ references_json }};</script>
|
|
75
|
+
|
|
76
|
+
<script>
|
|
77
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
78
|
+
// Render Math (KaTeX)
|
|
79
|
+
const contentDiv = document.getElementById("markdown-content");
|
|
80
|
+
renderMathInElement(contentDiv, {
|
|
81
|
+
delimiters: [
|
|
82
|
+
{ left: "$$", right: "$$", display: true },
|
|
83
|
+
{ left: "$", right: "$", display: false },
|
|
84
|
+
{ left: "\\(", right: "\\)", display: false },
|
|
85
|
+
{ left: "\\[", right: "\\]", display: true }
|
|
86
|
+
],
|
|
87
|
+
throwOnError: false
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Process Citations
|
|
91
|
+
function processCitations(rootNode) {
|
|
92
|
+
const walker = document.createTreeWalker(
|
|
93
|
+
rootNode,
|
|
94
|
+
NodeFilter.SHOW_TEXT,
|
|
95
|
+
null,
|
|
96
|
+
false
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const nodesToReplace = [];
|
|
100
|
+
let node;
|
|
101
|
+
while (node = walker.nextNode()) {
|
|
102
|
+
if (node.parentElement.tagName === "SCRIPT" || node.parentElement.tagName === "STYLE" || node.parentElement.tagName === "A") continue;
|
|
103
|
+
if (/\[\d+\]/.test(node.nodeValue)) {
|
|
104
|
+
nodesToReplace.push(node);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
nodesToReplace.forEach(textNode => {
|
|
109
|
+
const fragment = document.createDocumentFragment();
|
|
110
|
+
let lastIndex = 0;
|
|
111
|
+
const text = textNode.nodeValue;
|
|
112
|
+
const regex = /\[(\d+)\]/g;
|
|
113
|
+
let match;
|
|
114
|
+
|
|
115
|
+
while ((match = regex.exec(text)) !== null) {
|
|
116
|
+
fragment.appendChild(document.createTextNode(text.substring(lastIndex, match.index)));
|
|
117
|
+
|
|
118
|
+
const id = match[1];
|
|
119
|
+
const ref = window.REFERENCES ? window.REFERENCES.find(r => r.id == id) : null;
|
|
120
|
+
|
|
121
|
+
if (ref) {
|
|
122
|
+
const span = document.createElement("span");
|
|
123
|
+
const title = ref.title.replace(/"/g, """);
|
|
124
|
+
const url = ref.url;
|
|
125
|
+
let domain = "unknown";
|
|
126
|
+
try { domain = new URL(url).hostname; } catch (e) { }
|
|
127
|
+
const favicon = `https://www.google.com/s2/favicons?domain=${domain}&sz=32`;
|
|
128
|
+
|
|
129
|
+
span.innerHTML = `<a href="${url}" target="_blank" class="citation-ref" data-id="${id}">
|
|
130
|
+
${id}
|
|
131
|
+
<span class="citation-tooltip">
|
|
132
|
+
<span class="citation-tooltip-title">${title}</span>
|
|
133
|
+
<span class="citation-tooltip-url">
|
|
134
|
+
<img src="${favicon}" style="width:12px;height:12px;vertical-align:middle;margin-right:4px;">
|
|
135
|
+
${domain}
|
|
136
|
+
</span>
|
|
137
|
+
</span>
|
|
138
|
+
</a>`;
|
|
139
|
+
fragment.appendChild(span.firstElementChild);
|
|
140
|
+
} else {
|
|
141
|
+
fragment.appendChild(document.createTextNode(match[0]));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
lastIndex = regex.lastIndex;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
fragment.appendChild(document.createTextNode(text.substring(lastIndex)));
|
|
148
|
+
textNode.parentNode.replaceChild(fragment, textNode);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
processCitations(contentDiv);
|
|
153
|
+
});
|
|
154
|
+
</script>
|
|
155
|
+
</body>
|
|
156
|
+
|
|
157
|
+
</html>
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<link href="https://cdn.jsdelivr.net/npm/remixicon@4.1.0/fonts/remixicon.css" rel="stylesheet"/>
|
|
7
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
8
|
+
<title>Entari Render</title>
|
|
9
|
+
<!-- @formatter:off -->
|
|
10
|
+
<!-- prettier-ignore -->
|
|
11
|
+
<style>{{ tailwind_css | safe }}</style>
|
|
12
|
+
<style>{{ highlight_css | safe }}</style>
|
|
13
|
+
<script>{{ highlight_js | safe }}</script>
|
|
14
|
+
<style>{{ katex_css | safe }}</style>
|
|
15
|
+
<script>{{ katex_js | safe }}</script>
|
|
16
|
+
<script>{{ katex_auto_render_js | safe }}</script>
|
|
17
|
+
<!-- @formatter:on -->
|
|
18
|
+
|
|
19
|
+
</head>
|
|
20
|
+
|
|
21
|
+
<body class="bg-[#f2f2f2] p-0 box-border m-0 font-sans text-gray-800">
|
|
22
|
+
<div id="main-container" class="w-full max-w-[450px] flex flex-col gap-4 mx-auto bg-[#f2f2f2] p-0 font-sans h-fit">
|
|
23
|
+
|
|
24
|
+
{# --- MACROS --- #}
|
|
25
|
+
|
|
26
|
+
{% macro icon_container(icon_html, box_class="bg-gray-50 rounded-lg border border-gray-100", size_class="w-10 h-10") %}
|
|
27
|
+
<div class="flex items-center justify-center {{ size_class }} {{ box_class }} shrink-0">
|
|
28
|
+
{{ icon_html | safe }}
|
|
29
|
+
</div>
|
|
30
|
+
{% endmacro %}
|
|
31
|
+
|
|
32
|
+
{% macro card_header(title, icon_html=None, subtitle_html=None, is_plain=False, icon_box_class=None) %}
|
|
33
|
+
{% set container_class = "flex items-center gap-3" if is_plain else "flex items-center gap-3 pb-3 mb-3 border-b border-gray-100" %}
|
|
34
|
+
{% set default_box_class = icon_box_class if icon_box_class else "bg-gray-50 rounded-lg border border-gray-100" %}
|
|
35
|
+
<div class="{{ container_class }}">
|
|
36
|
+
{{ icon_container(icon_html, box_class=default_box_class) }}
|
|
37
|
+
<div class="flex flex-col min-w-0">
|
|
38
|
+
<div class="text-sm font-bold text-gray-900 uppercase tracking-wide whitespace-nowrap overflow-hidden text-ellipsis">{{ title }}</div>
|
|
39
|
+
{% if subtitle_html and not is_plain %}
|
|
40
|
+
{{ subtitle_html | safe }}
|
|
41
|
+
{% endif %}
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
{% endmacro %}
|
|
45
|
+
|
|
46
|
+
{% macro list_card(icon_html, title_html, subtitle_html=None, link_url=None, right_content_html=None, is_compact=False, icon_box_class="bg-gray-50 rounded-md shrink-0 ring-1 ring-inset ring-black/5") %}
|
|
47
|
+
{% set tag = "a" if link_url else "div" %}
|
|
48
|
+
{% set href_attr = 'href="' ~ link_url ~ '" target="_blank"' if link_url else "" %}
|
|
49
|
+
{% set hover_class = "transition-colors hover:bg-gray-50" if link_url else "" %}
|
|
50
|
+
|
|
51
|
+
{% set padding_class = "p-2.5" if is_compact else "px-4 py-3.5" %}
|
|
52
|
+
{% set align_class = "items-center" if is_compact else "items-start" %}
|
|
53
|
+
{% set icon_size = "w-6 h-6" if is_compact else "w-8 h-8" %}
|
|
54
|
+
|
|
55
|
+
<{{ tag }} {{ href_attr | safe }} class="flex {{ align_class }} gap-3 {{ padding_class }} rounded-lg border border-gray-100 bg-white shadow-sm no-underline text-inherit {{ hover_class }}">
|
|
56
|
+
<div class="flex items-center justify-center {{ icon_size }} {{ icon_box_class }}">
|
|
57
|
+
{{ icon_html | safe }}
|
|
58
|
+
</div>
|
|
59
|
+
<div class="flex flex-col flex-1 min-w-0 gap-0.5">
|
|
60
|
+
<div class="flex items-center gap-2 leading-tight min-w-0">
|
|
61
|
+
{{ title_html | safe }}
|
|
62
|
+
</div>
|
|
63
|
+
{% if subtitle_html %}
|
|
64
|
+
<div>{{ subtitle_html | safe }}</div>
|
|
65
|
+
{% endif %}
|
|
66
|
+
</div>
|
|
67
|
+
{% if right_content_html %}
|
|
68
|
+
<div class="shrink-0 ml-2">{{ right_content_html | safe }}</div>
|
|
69
|
+
{% endif %}
|
|
70
|
+
</{{ tag }}>
|
|
71
|
+
{% endmacro %}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
{# --- MAIN CONTENT --- #}
|
|
75
|
+
|
|
76
|
+
<!-- Response Card (Content First) -->
|
|
77
|
+
<div class="bg-[#f2f2f2] rounded-2xl p-5 overflow-hidden">
|
|
78
|
+
<div id="markdown-content" class="markdown-body text-[15px] leading-relaxed text-gray-800">
|
|
79
|
+
{{ content_html | safe }}
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
<!-- Speculation Card (Optional) -->
|
|
84
|
+
{% if suggestions %}
|
|
85
|
+
<div class="flex flex-col gap-2 bg-[#f2f2f2] rounded-2xl p-5 overflow-hidden">
|
|
86
|
+
{% set sug_icon %}
|
|
87
|
+
<i class="ri-magic-line text-lg text-pink-600"></i>
|
|
88
|
+
{% endset %}
|
|
89
|
+
{{ card_header("SUGGESTIONS", icon_html=sug_icon, is_plain=True) }}
|
|
90
|
+
|
|
91
|
+
<div class="grid grid-cols-2 gap-2.5">
|
|
92
|
+
{% for sug in suggestions %}
|
|
93
|
+
<div class="flex items-baseline gap-2 text-sm text-gray-600 px-3.5 py-2.5 bg-white/80 backdrop-blur-sm rounded-full shadow-sm hover:shadow transition-shadow cursor-default">
|
|
94
|
+
<span class="text-gray-500 font-mono font-bold text-[13px] whitespace-nowrap">{{ loop.index }}</span>
|
|
95
|
+
<span class="flex-1 text-[13px] text-gray-600 font-medium whitespace-nowrap overflow-hidden text-ellipsis">{{ sug }}</span>
|
|
96
|
+
</div>
|
|
97
|
+
{% endfor %}
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
{% endif %}
|
|
101
|
+
|
|
102
|
+
<!-- Pipeline & Children (Nested) -->
|
|
103
|
+
<div class="bg-[#f2f2f2] rounded-2xl p-5 overflow-hidden flex flex-col gap-4">
|
|
104
|
+
|
|
105
|
+
{% if stages %}
|
|
106
|
+
{% for stage in stages %}
|
|
107
|
+
<div>
|
|
108
|
+
{# Stage Card #}
|
|
109
|
+
{% set color_class = "bg-white text-gray-700 border-gray-200 shadow-sm" %}
|
|
110
|
+
|
|
111
|
+
{% set icon_box_class = color_class + " rounded-md border shrink-0" %}
|
|
112
|
+
|
|
113
|
+
{% set title_html %}
|
|
114
|
+
<span class="text-[11px] font-bold uppercase text-gray-700 shrink-0">{{ stage.name }}</span>
|
|
115
|
+
<span class="text-[11px] font-medium text-gray-700 truncate min-w-0" title="{{ stage.model }}">{{ stage.model_short }}</span>
|
|
116
|
+
<span class="ml-auto text-[10px] text-gray-400 shrink-0 truncate max-w-[80px]">{{ stage.provider }}</span>
|
|
117
|
+
{% endset %}
|
|
118
|
+
|
|
119
|
+
{% set stats_html %}
|
|
120
|
+
<div class="flex items-center gap-3 text-[11px] text-gray-500 font-mono mt-0.5">
|
|
121
|
+
<span>{{ stage.time_str }}</span><span>{{ stage.cost_str }}</span>
|
|
122
|
+
</div>
|
|
123
|
+
{% endset %}
|
|
124
|
+
|
|
125
|
+
{{ list_card(stage.icon_html, title_html, subtitle_html=stats_html, is_compact=True, icon_box_class=icon_box_class) }}
|
|
126
|
+
|
|
127
|
+
{# Nested Children (Indent & Connect) #}
|
|
128
|
+
{% if stage.references or stage.mcp_steps %}
|
|
129
|
+
<div class="ml-4 pl-4 border-l-2 border-gray-200 mt-2 flex flex-col gap-2">
|
|
130
|
+
|
|
131
|
+
{# References #}
|
|
132
|
+
{% if stage.references %}
|
|
133
|
+
<div class="text-[11px] uppercase font-bold text-blue-600 tracking-wider mb-1 mt-1">References</div>
|
|
134
|
+
{% for ref in stage.references %}
|
|
135
|
+
{% set favicon_url = "https://www.google.com/s2/favicons?domain=" + ref.domain + "&sz=32" %}
|
|
136
|
+
|
|
137
|
+
{% set ref_icon %}
|
|
138
|
+
<img src="{{ favicon_url }}" class="w-3.5 h-3.5 rounded-sm opacity-80 decoration-0">
|
|
139
|
+
{% endset %}
|
|
140
|
+
|
|
141
|
+
{% set ref_icon_box = "bg-white rounded border border-gray-100 w-6 h-6 shrink-0" %}
|
|
142
|
+
|
|
143
|
+
{% set title_html = '<div class="text-[11px] font-medium text-gray-900 truncate underline decoration-gray-300 decoration-1 underline-offset-2 hover:text-black hover:decoration-gray-500 transition-colors">' + ref.title + '</div>' %}
|
|
144
|
+
{% set subtitle_html = '<div class="text-[10px] text-gray-700 truncate">' + ref.domain + '</div>' %}
|
|
145
|
+
{% set right_html = '<div class="flex items-center justify-center min-w-[14px] h-3.5 px-0.5 text-[9px] font-bold text-blue-600 bg-blue-50 border border-blue-200 rounded">' + (loop.index|string) + '</div>' %}
|
|
146
|
+
|
|
147
|
+
{{ list_card(ref_icon, title_html, subtitle_html=subtitle_html, link_url=ref.url, right_content_html=right_html, is_compact=True, icon_box_class=ref_icon_box) }}
|
|
148
|
+
{% endfor %}
|
|
149
|
+
{% endif %}
|
|
150
|
+
|
|
151
|
+
{# MCP Steps #}
|
|
152
|
+
{% if stage.mcp_steps %}
|
|
153
|
+
<div class="text-[11px] uppercase font-bold text-orange-600 tracking-wider mb-1 mt-1">MCP Flow</div>
|
|
154
|
+
{% for step in stage.mcp_steps %}
|
|
155
|
+
{% set icon_box_class = "rounded-md border border-gray-100 bg-white text-gray-500 shrink-0" %}
|
|
156
|
+
|
|
157
|
+
{% set title_html = '<div class="text-[11px] font-medium text-gray-900 font-mono">' + step.name + '</div>' %}
|
|
158
|
+
{% set subtitle_html = '<div class="text-[10px] text-gray-700 leading-tight">' + step.description + '</div>' if step.description else None %}
|
|
159
|
+
{% set right_html = '<div class="flex items-center justify-center min-w-[14px] h-3.5 px-0.5 text-[9px] font-bold text-orange-600 bg-orange-50 border border-orange-200 rounded">' + ('abcdefghijklmnopqrstuvwxyz'[loop.index0]) + '</div>' %}
|
|
160
|
+
|
|
161
|
+
{{ list_card(step.icon_svg, title_html, subtitle_html=subtitle_html, right_content_html=right_html, is_compact=True, icon_box_class=icon_box_class) }}
|
|
162
|
+
{% endfor %}
|
|
163
|
+
{% endif %}
|
|
164
|
+
|
|
165
|
+
</div>
|
|
166
|
+
{% endif %}
|
|
167
|
+
</div>
|
|
168
|
+
{% endfor %}
|
|
169
|
+
{% endif %}
|
|
170
|
+
</div>
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
</div>
|
|
174
|
+
|
|
175
|
+
<script>window.REFERENCES = {{ references_json | safe }};</script>
|
|
176
|
+
|
|
177
|
+
<script>
|
|
178
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
179
|
+
// Render Math (KaTeX)
|
|
180
|
+
const contentDiv = document.getElementById("markdown-content");
|
|
181
|
+
if(typeof renderMathInElement !== 'undefined') {
|
|
182
|
+
renderMathInElement(contentDiv, {
|
|
183
|
+
delimiters: [
|
|
184
|
+
{ left: "$$", right: "$$", display: true },
|
|
185
|
+
{ left: "$", right: "$", display: false },
|
|
186
|
+
{ left: "\\(", right: "\\)", display: false },
|
|
187
|
+
{ left: "\\[", right: "\\]", display: true }
|
|
188
|
+
],
|
|
189
|
+
throwOnError: false
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Process Citations
|
|
194
|
+
function processCitations(rootNode) {
|
|
195
|
+
const walker = document.createTreeWalker(
|
|
196
|
+
rootNode,
|
|
197
|
+
NodeFilter.SHOW_TEXT,
|
|
198
|
+
null,
|
|
199
|
+
false
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
const nodesToReplace = [];
|
|
203
|
+
let node;
|
|
204
|
+
while (node = walker.nextNode()) {
|
|
205
|
+
if (node.parentElement.tagName === "SCRIPT" || node.parentElement.tagName === "STYLE" || node.parentElement.tagName === "A") continue;
|
|
206
|
+
if (/\[\d+\]/.test(node.nodeValue)) {
|
|
207
|
+
nodesToReplace.push(node);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
nodesToReplace.forEach(textNode => {
|
|
212
|
+
const fragment = document.createDocumentFragment();
|
|
213
|
+
let lastIndex = 0;
|
|
214
|
+
const text = textNode.nodeValue;
|
|
215
|
+
const regex = /\[(\d+)\]/g;
|
|
216
|
+
let match;
|
|
217
|
+
|
|
218
|
+
while ((match = regex.exec(text)) !== null) {
|
|
219
|
+
fragment.appendChild(document.createTextNode(text.substring(lastIndex, match.index)));
|
|
220
|
+
|
|
221
|
+
const id = match[1];
|
|
222
|
+
const ref = window.REFERENCES ? window.REFERENCES.find(r => r.id == id) : null;
|
|
223
|
+
|
|
224
|
+
if (ref) {
|
|
225
|
+
const span = document.createElement("span");
|
|
226
|
+
const title = ref.title.replace(/"/g, """);
|
|
227
|
+
const url = ref.url;
|
|
228
|
+
let domain = "unknown";
|
|
229
|
+
try { domain = new URL(url).hostname; } catch (e) { }
|
|
230
|
+
const favicon = `https://www.google.com/s2/favicons?domain=${domain}&sz=32`;
|
|
231
|
+
|
|
232
|
+
span.innerHTML = `<a href="${url}" target="_blank" class="citation-ref" data-id="${id}">
|
|
233
|
+
${id}
|
|
234
|
+
<span class="citation-tooltip">
|
|
235
|
+
<span class="citation-tooltip-title">${title}</span>
|
|
236
|
+
<span class="citation-tooltip-url">
|
|
237
|
+
<img src="${favicon}" style="width:12px;height:12px;vertical-align:middle;margin-right:4px;">
|
|
238
|
+
${domain}
|
|
239
|
+
</span>
|
|
240
|
+
</span>
|
|
241
|
+
</a>`;
|
|
242
|
+
fragment.appendChild(span.firstElementChild);
|
|
243
|
+
} else {
|
|
244
|
+
fragment.appendChild(document.createTextNode(match[0]));
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
lastIndex = regex.lastIndex;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
fragment.appendChild(document.createTextNode(text.substring(lastIndex)));
|
|
251
|
+
textNode.parentNode.replaceChild(fragment, textNode);
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
processCitations(contentDiv);
|
|
256
|
+
});
|
|
257
|
+
</script>
|
|
258
|
+
</body>
|
|
259
|
+
</html>
|
|
@@ -495,29 +495,53 @@ class ProcessingPipeline:
|
|
|
495
495
|
current_step = None
|
|
496
496
|
|
|
497
497
|
for line in lines:
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
498
|
+
line_stripped = line.strip()
|
|
499
|
+
if not line_stripped: continue
|
|
500
|
+
|
|
501
|
+
# New Format: "1. [icon] name: description" OR "[icon] name: description"
|
|
502
|
+
# Regex details:
|
|
503
|
+
# ^(?:(?:\d+\.|[-*])\s+)? -> Optional numbering (1. or - or *)
|
|
504
|
+
# \[(\w+)\] -> Icon in brackets [icon] -> group 1
|
|
505
|
+
# \s+ -> separating space
|
|
506
|
+
# ([^:]+) -> Tool Name (chars before colon) -> group 2
|
|
507
|
+
# : -> Colon separator
|
|
508
|
+
# \s*(.+) -> Description -> group 3
|
|
509
|
+
new_format_match = re.match(r'^(?:(?:\d+\.|[-*])\s+)?\[(\w+)\]\s+([^:]+):\s*(.+)$', line_stripped)
|
|
510
|
+
|
|
511
|
+
# Old/Flexible Format: "[icon] name" (description might be on next line)
|
|
512
|
+
flexible_match = re.match(r'^(?:(?:\d+\.|[-*])\s+)?\[(\w+)\]\s+(.+)$', line_stripped)
|
|
513
|
+
|
|
514
|
+
if new_format_match:
|
|
515
|
+
if current_step: parsed["mcp_steps"].append(current_step)
|
|
504
516
|
current_step = {
|
|
505
|
-
"icon":
|
|
506
|
-
"name":
|
|
507
|
-
"description":
|
|
517
|
+
"icon": new_format_match.group(1).lower(),
|
|
518
|
+
"name": new_format_match.group(2).strip(),
|
|
519
|
+
"description": new_format_match.group(3).strip()
|
|
508
520
|
}
|
|
509
|
-
elif
|
|
510
|
-
#
|
|
511
|
-
current_step["
|
|
512
|
-
elif line.strip() and not line.startswith("[") and current_step is None:
|
|
513
|
-
# Fallback: plain tool name without icon
|
|
521
|
+
elif flexible_match:
|
|
522
|
+
# Could be just "[icon] name" without description, or mixed
|
|
523
|
+
if current_step: parsed["mcp_steps"].append(current_step)
|
|
514
524
|
current_step = {
|
|
515
|
-
"icon":
|
|
516
|
-
"name":
|
|
525
|
+
"icon": flexible_match.group(1).lower(),
|
|
526
|
+
"name": flexible_match.group(2).strip(),
|
|
517
527
|
"description": ""
|
|
518
528
|
}
|
|
529
|
+
elif line.startswith(" ") and current_step:
|
|
530
|
+
# Indented description line (continuation)
|
|
531
|
+
if current_step["description"]:
|
|
532
|
+
current_step["description"] += " " + line.strip()
|
|
533
|
+
else:
|
|
534
|
+
current_step["description"] = line.strip()
|
|
535
|
+
elif line_stripped and not line_stripped.startswith("[") and current_step is None:
|
|
536
|
+
# Plain text line without icon, treat as name if no current step
|
|
537
|
+
# (This handles cases where LLM forgets brackets but lists steps)
|
|
538
|
+
if current_step: parsed["mcp_steps"].append(current_step)
|
|
539
|
+
current_step = {
|
|
540
|
+
"icon": "default",
|
|
541
|
+
"name": line_stripped,
|
|
542
|
+
"description": ""
|
|
543
|
+
}
|
|
519
544
|
|
|
520
|
-
# Don't forget the last step
|
|
521
545
|
if current_step:
|
|
522
546
|
parsed["mcp_steps"].append(current_step)
|
|
523
547
|
remaining_text = remaining_text.replace(mcp_block_match.group(0), "").strip()
|