Spaces:
Sleeping
Sleeping
github-actions[bot] commited on
Commit ·
daa082c
1
Parent(s): aee641e
Integrate compact bound placeholder display
Browse files
.github/workflows/bound_placeholder_display_compaction.yml
DELETED
|
@@ -1,64 +0,0 @@
|
|
| 1 |
-
name: Bound placeholder display compaction
|
| 2 |
-
|
| 3 |
-
on:
|
| 4 |
-
pull_request:
|
| 5 |
-
branches:
|
| 6 |
-
- main
|
| 7 |
-
|
| 8 |
-
permissions:
|
| 9 |
-
contents: write
|
| 10 |
-
|
| 11 |
-
jobs:
|
| 12 |
-
implement-and-test:
|
| 13 |
-
runs-on: ubuntu-latest
|
| 14 |
-
env:
|
| 15 |
-
PYTHONPATH: ${{ github.workspace }}
|
| 16 |
-
|
| 17 |
-
steps:
|
| 18 |
-
- name: Checkout repository
|
| 19 |
-
uses: actions/checkout@v4
|
| 20 |
-
with:
|
| 21 |
-
fetch-depth: 0
|
| 22 |
-
|
| 23 |
-
- name: Apply display-only UI integration
|
| 24 |
-
shell: bash
|
| 25 |
-
run: |
|
| 26 |
-
set -euo pipefail
|
| 27 |
-
git checkout -B scrub-bound-placeholder-display-compaction origin/scrub-bound-placeholder-display-compaction
|
| 28 |
-
python - <<'PY'
|
| 29 |
-
from pathlib import Path
|
| 30 |
-
path = Path("ops/implement_bound_placeholder_display_compaction.py")
|
| 31 |
-
text = path.read_text(encoding="utf-8")
|
| 32 |
-
start_marker = '''patch(\n "side_by_side_review_panel_ui.py",\n """.sp-side-by-side-highlight-token'''
|
| 33 |
-
end_marker = '''\n\npatch(\n "side_by_side_review_panel_ui.py",\n \'\'\'def _highlighted_processed_inner_html'''
|
| 34 |
-
start = text.find(start_marker)
|
| 35 |
-
end = text.find(end_marker, start)
|
| 36 |
-
if start < 0 or end < 0:
|
| 37 |
-
raise SystemExit("CSS patch block anchors not found")
|
| 38 |
-
path.write_text(text[:start] + text[end + 2 :], encoding="utf-8")
|
| 39 |
-
PY
|
| 40 |
-
python ops/implement_bound_placeholder_display_compaction.py
|
| 41 |
-
git config user.name "github-actions[bot]"
|
| 42 |
-
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
| 43 |
-
git add -A
|
| 44 |
-
git diff --cached --check
|
| 45 |
-
git commit -m "Integrate compact bound placeholder display"
|
| 46 |
-
git push origin HEAD:scrub-bound-placeholder-display-compaction
|
| 47 |
-
|
| 48 |
-
- name: Run frontend display and component tests
|
| 49 |
-
run: |
|
| 50 |
-
node --test tests/frontend/bound_placeholder_display.test.js
|
| 51 |
-
node --test tests/frontend/processed_text_selection_component_core.test.js
|
| 52 |
-
|
| 53 |
-
- name: Set up Python
|
| 54 |
-
uses: actions/setup-python@v5
|
| 55 |
-
with:
|
| 56 |
-
python-version: "3.10"
|
| 57 |
-
|
| 58 |
-
- name: Install test dependencies
|
| 59 |
-
run: |
|
| 60 |
-
python -m pip install --upgrade pip
|
| 61 |
-
python -m pip install pytest presidio-analyzer click==8.1.8 python-docx pymupdf reportlab pypdf
|
| 62 |
-
|
| 63 |
-
- name: Run full regression
|
| 64 |
-
run: python -m pytest -q tests
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
frontend/processed_text_selection_component/component.js
CHANGED
|
@@ -19,6 +19,8 @@
|
|
| 19 |
let currentArgs = {};
|
| 20 |
let processedText = "";
|
| 21 |
let highlightSpans = [];
|
|
|
|
|
|
|
| 22 |
let currentSelection = null;
|
| 23 |
let lastMenuPosition = { x: 16, y: 16 };
|
| 24 |
let lastRenderedInspectionToken = "";
|
|
@@ -39,18 +41,28 @@
|
|
| 39 |
|
| 40 |
function renderProcessedText(text, spans) {
|
| 41 |
clearElement(processedPane);
|
| 42 |
-
Core.
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
});
|
| 55 |
}
|
| 56 |
|
|
@@ -85,10 +97,34 @@
|
|
| 85 |
if (!nodeWithin(rootElement, container)) {
|
| 86 |
throw new Error("selection endpoint is outside processed text");
|
| 87 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
const range = document.createRange();
|
| 89 |
-
range.selectNodeContents(
|
| 90 |
range.setEnd(container, offset);
|
| 91 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
}
|
| 93 |
|
| 94 |
function readProcessedSelection() {
|
|
@@ -108,7 +144,7 @@
|
|
| 108 |
} catch (_error) {
|
| 109 |
return null;
|
| 110 |
}
|
| 111 |
-
return Core.selectionFromOffsets(processedText,
|
| 112 |
}
|
| 113 |
|
| 114 |
function updateSelectionState() {
|
|
@@ -358,9 +394,18 @@
|
|
| 358 |
|
| 359 |
renderPlainText(sourcePane, currentArgs.source_text);
|
| 360 |
renderProcessedText(processedText, highlightSpans);
|
| 361 |
-
|
| 362 |
-
|
| 363 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 364 |
|
| 365 |
const restoreSource = currentArgs.restore_source_scroll_ratio;
|
| 366 |
const restoreProcessed = currentArgs.restore_processed_scroll_ratio;
|
|
|
|
| 19 |
let currentArgs = {};
|
| 20 |
let processedText = "";
|
| 21 |
let highlightSpans = [];
|
| 22 |
+
let renderedProcessedSegments = [];
|
| 23 |
+
let selectionProtectedSpans = [];
|
| 24 |
let currentSelection = null;
|
| 25 |
let lastMenuPosition = { x: 16, y: 16 };
|
| 26 |
let lastRenderedInspectionToken = "";
|
|
|
|
| 41 |
|
| 42 |
function renderProcessedText(text, spans) {
|
| 43 |
clearElement(processedPane);
|
| 44 |
+
renderedProcessedSegments = Core.buildDisplayTextSegments(text, spans);
|
| 45 |
+
selectionProtectedSpans = Core.protectedSpansFromDisplaySegments(renderedProcessedSegments);
|
| 46 |
+
renderedProcessedSegments.forEach(function (segment, index) {
|
| 47 |
+
const element = document.createElement(segment.marked ? "mark" : "span");
|
| 48 |
+
element.className = segment.marked
|
| 49 |
+
? "sp-highlight sp-processed-segment"
|
| 50 |
+
: "sp-processed-segment";
|
| 51 |
+
if (segment.compacted) {
|
| 52 |
+
element.classList.add("sp-compact-placeholder");
|
| 53 |
+
element.title = `Volledige gebonden placeholder: ${segment.full_placeholder}`;
|
| 54 |
+
element.setAttribute(
|
| 55 |
+
"aria-label",
|
| 56 |
+
`Gebonden placeholder, compact weergegeven als ${segment.display_text}`,
|
| 57 |
+
);
|
| 58 |
+
} else if (segment.marked) {
|
| 59 |
+
element.setAttribute("aria-label", "gemarkeerde vervanging");
|
| 60 |
}
|
| 61 |
+
element.dataset.segmentIndex = String(index);
|
| 62 |
+
element.dataset.startUtf16 = String(segment.start_utf16);
|
| 63 |
+
element.dataset.endUtf16 = String(segment.end_utf16);
|
| 64 |
+
appendSafeText(element, segment.display_text);
|
| 65 |
+
processedPane.appendChild(element);
|
| 66 |
});
|
| 67 |
}
|
| 68 |
|
|
|
|
| 97 |
if (!nodeWithin(rootElement, container)) {
|
| 98 |
throw new Error("selection endpoint is outside processed text");
|
| 99 |
}
|
| 100 |
+
if (container === rootElement) {
|
| 101 |
+
if (!Number.isInteger(offset) || offset < 0 || offset > rootElement.childNodes.length) {
|
| 102 |
+
throw new Error("selection endpoint is outside processed text");
|
| 103 |
+
}
|
| 104 |
+
if (offset === 0) {
|
| 105 |
+
return 0;
|
| 106 |
+
}
|
| 107 |
+
if (offset >= renderedProcessedSegments.length) {
|
| 108 |
+
return processedText.length;
|
| 109 |
+
}
|
| 110 |
+
return renderedProcessedSegments[offset - 1].end_utf16;
|
| 111 |
+
}
|
| 112 |
+
const candidate = container.nodeType === Node.TEXT_NODE ? container.parentElement : container;
|
| 113 |
+
const segmentElement = candidate && candidate.closest
|
| 114 |
+
? candidate.closest("[data-segment-index]")
|
| 115 |
+
: null;
|
| 116 |
+
if (!segmentElement || !rootElement.contains(segmentElement)) {
|
| 117 |
+
throw new Error("selection endpoint has no source segment");
|
| 118 |
+
}
|
| 119 |
+
const segmentIndex = Number(segmentElement.dataset.segmentIndex);
|
| 120 |
const range = document.createRange();
|
| 121 |
+
range.selectNodeContents(segmentElement);
|
| 122 |
range.setEnd(container, offset);
|
| 123 |
+
return Core.utf16OffsetFromDisplaySegments(
|
| 124 |
+
renderedProcessedSegments,
|
| 125 |
+
segmentIndex,
|
| 126 |
+
range.toString().length,
|
| 127 |
+
);
|
| 128 |
}
|
| 129 |
|
| 130 |
function readProcessedSelection() {
|
|
|
|
| 144 |
} catch (_error) {
|
| 145 |
return null;
|
| 146 |
}
|
| 147 |
+
return Core.selectionFromOffsets(processedText, selectionProtectedSpans, start, end);
|
| 148 |
}
|
| 149 |
|
| 150 |
function updateSelectionState() {
|
|
|
|
| 394 |
|
| 395 |
renderPlainText(sourcePane, currentArgs.source_text);
|
| 396 |
renderProcessedText(processedText, highlightSpans);
|
| 397 |
+
const compactedCount = renderedProcessedSegments.filter(function (segment) {
|
| 398 |
+
return segment.compacted;
|
| 399 |
+
}).length;
|
| 400 |
+
if (highlightSpans.length && compactedCount) {
|
| 401 |
+
processedLegend.textContent = "Geel = vervangen; documentcode compact weergegeven";
|
| 402 |
+
} else if (highlightSpans.length) {
|
| 403 |
+
processedLegend.textContent = "Geel = vervangen of gemaskeerde waarde";
|
| 404 |
+
} else if (compactedCount) {
|
| 405 |
+
processedLegend.textContent = "Documentcode compact weergegeven";
|
| 406 |
+
} else {
|
| 407 |
+
processedLegend.textContent = "Verwerkte tekst";
|
| 408 |
+
}
|
| 409 |
|
| 410 |
const restoreSource = currentArgs.restore_source_scroll_ratio;
|
| 411 |
const restoreProcessed = currentArgs.restore_processed_scroll_ratio;
|
ops/implement_bound_placeholder_display_compaction.py
DELETED
|
@@ -1,83 +0,0 @@
|
|
| 1 |
-
from __future__ import annotations
|
| 2 |
-
|
| 3 |
-
from pathlib import Path
|
| 4 |
-
|
| 5 |
-
ROOT = Path(__file__).resolve().parents[1]
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
def patch(path: str, old: str, new: str) -> None:
|
| 9 |
-
target = ROOT / path
|
| 10 |
-
text = target.read_text(encoding="utf-8")
|
| 11 |
-
if old not in text:
|
| 12 |
-
raise RuntimeError(f"Required anchor missing in {path}: {old[:120]!r}")
|
| 13 |
-
target.write_text(text.replace(old, new, 1), encoding="utf-8")
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
patch(
|
| 17 |
-
"frontend/processed_text_selection_component/component.js",
|
| 18 |
-
""" let processedText = \"\";\n let highlightSpans = [];\n let currentSelection = null;\n""",
|
| 19 |
-
""" let processedText = \"\";\n let highlightSpans = [];\n let renderedProcessedSegments = [];\n let selectionProtectedSpans = [];\n let currentSelection = null;\n""",
|
| 20 |
-
)
|
| 21 |
-
|
| 22 |
-
patch(
|
| 23 |
-
"frontend/processed_text_selection_component/component.js",
|
| 24 |
-
""" function renderProcessedText(text, spans) {\n clearElement(processedPane);\n Core.buildTextSegments(text, spans).forEach(function (segment) {\n if (segment.marked) {\n const marker = document.createElement(\"mark\");\n marker.className = \"sp-highlight\";\n marker.setAttribute(\"aria-label\", \"gemarkeerde vervanging\");\n marker.dataset.startUtf16 = String(segment.start_utf16);\n marker.dataset.endUtf16 = String(segment.end_utf16);\n appendSafeText(marker, segment.text);\n processedPane.appendChild(marker);\n } else {\n appendSafeText(processedPane, segment.text);\n }\n });\n }\n""",
|
| 25 |
-
""" function renderProcessedText(text, spans) {\n clearElement(processedPane);\n renderedProcessedSegments = Core.buildDisplayTextSegments(text, spans);\n selectionProtectedSpans = Core.protectedSpansFromDisplaySegments(renderedProcessedSegments);\n renderedProcessedSegments.forEach(function (segment, index) {\n const element = document.createElement(segment.marked ? \"mark\" : \"span\");\n element.className = segment.marked\n ? \"sp-highlight sp-processed-segment\"\n : \"sp-processed-segment\";\n if (segment.compacted) {\n element.classList.add(\"sp-compact-placeholder\");\n element.title = `Volledige gebonden placeholder: ${segment.full_placeholder}`;\n element.setAttribute(\n \"aria-label\",\n `Gebonden placeholder, compact weergegeven als ${segment.display_text}`,\n );\n } else if (segment.marked) {\n element.setAttribute(\"aria-label\", \"gemarkeerde vervanging\");\n }\n element.dataset.segmentIndex = String(index);\n element.dataset.startUtf16 = String(segment.start_utf16);\n element.dataset.endUtf16 = String(segment.end_utf16);\n appendSafeText(element, segment.display_text);\n processedPane.appendChild(element);\n });\n }\n""",
|
| 26 |
-
)
|
| 27 |
-
|
| 28 |
-
patch(
|
| 29 |
-
"frontend/processed_text_selection_component/component.js",
|
| 30 |
-
""" function domUtf16Offset(rootElement, container, offset) {\n if (!nodeWithin(rootElement, container)) {\n throw new Error(\"selection endpoint is outside processed text\");\n }\n const range = document.createRange();\n range.selectNodeContents(rootElement);\n range.setEnd(container, offset);\n return range.toString().length;\n }\n""",
|
| 31 |
-
""" function domUtf16Offset(rootElement, container, offset) {\n if (!nodeWithin(rootElement, container)) {\n throw new Error(\"selection endpoint is outside processed text\");\n }\n if (container === rootElement) {\n if (!Number.isInteger(offset) || offset < 0 || offset > rootElement.childNodes.length) {\n throw new Error(\"selection endpoint is outside processed text\");\n }\n if (offset === 0) {\n return 0;\n }\n if (offset >= renderedProcessedSegments.length) {\n return processedText.length;\n }\n return renderedProcessedSegments[offset - 1].end_utf16;\n }\n const candidate = container.nodeType === Node.TEXT_NODE ? container.parentElement : container;\n const segmentElement = candidate && candidate.closest\n ? candidate.closest(\"[data-segment-index]\")\n : null;\n if (!segmentElement || !rootElement.contains(segmentElement)) {\n throw new Error(\"selection endpoint has no source segment\");\n }\n const segmentIndex = Number(segmentElement.dataset.segmentIndex);\n const range = document.createRange();\n range.selectNodeContents(segmentElement);\n range.setEnd(container, offset);\n return Core.utf16OffsetFromDisplaySegments(\n renderedProcessedSegments,\n segmentIndex,\n range.toString().length,\n );\n }\n""",
|
| 32 |
-
)
|
| 33 |
-
|
| 34 |
-
patch(
|
| 35 |
-
"frontend/processed_text_selection_component/component.js",
|
| 36 |
-
""" return Core.selectionFromOffsets(processedText, highlightSpans, start, end);\n""",
|
| 37 |
-
""" return Core.selectionFromOffsets(processedText, selectionProtectedSpans, start, end);\n""",
|
| 38 |
-
)
|
| 39 |
-
|
| 40 |
-
patch(
|
| 41 |
-
"frontend/processed_text_selection_component/component.js",
|
| 42 |
-
""" processedLegend.textContent = highlightSpans.length\n ? \"Geel = vervangen of gemaskeerde waarde\"\n : \"Verwerkte tekst\";\n""",
|
| 43 |
-
""" const compactedCount = renderedProcessedSegments.filter(function (segment) {\n return segment.compacted;\n }).length;\n if (highlightSpans.length && compactedCount) {\n processedLegend.textContent = \"Geel = vervangen; documentcode compact weergegeven\";\n } else if (highlightSpans.length) {\n processedLegend.textContent = \"Geel = vervangen of gemaskeerde waarde\";\n } else if (compactedCount) {\n processedLegend.textContent = \"Documentcode compact weergegeven\";\n } else {\n processedLegend.textContent = \"Verwerkte tekst\";\n }\n""",
|
| 44 |
-
)
|
| 45 |
-
|
| 46 |
-
patch(
|
| 47 |
-
"side_by_side_review_panel_ui.py",
|
| 48 |
-
"""import streamlit.components.v1 as components\n\nfrom processed_text_selection_component import (\n""",
|
| 49 |
-
"""import streamlit.components.v1 as components\n\nfrom bound_placeholder_display import build_bound_placeholder_display_segments\nfrom processed_text_selection_component import (\n""",
|
| 50 |
-
)
|
| 51 |
-
|
| 52 |
-
patch(
|
| 53 |
-
"side_by_side_review_panel_ui.py",
|
| 54 |
-
""".sp-side-by-side-highlight-token {\n background: #fef3c7;\n border: 1px solid #f59e0b;\n border-radius: 0.25rem;\n padding: 0.05rem 0.15rem;\n}\n""",
|
| 55 |
-
""".sp-side-by-side-highlight-token {\n background: #fef3c7;\n border: 1px solid #f59e0b;\n border-radius: 0.25rem;\n padding: 0.05rem 0.15rem;\n}\n.sp-compact-placeholder {\n border-bottom: 1px dotted #94a3b8;\n cursor: help;\n}\n""",
|
| 56 |
-
)
|
| 57 |
-
|
| 58 |
-
patch(
|
| 59 |
-
"side_by_side_review_panel_ui.py",
|
| 60 |
-
'''def _highlighted_processed_inner_html(\n processed_text: str,\n highlight_spans: list[tuple[int, int]],\n) -> str:\n """Return escaped inner HTML for the static fallback processed pane."""\n\n parts: list[str] = []\n cursor = 0\n for start, end in highlight_spans:\n parts.append(escape(processed_text[cursor:start]))\n parts.append(\n '<mark class="sp-side-by-side-highlight-token" aria-label="gemarkeerde vervanging">'\n f"{escape(processed_text[start:end])}"\n "</mark>"\n )\n cursor = end\n parts.append(escape(processed_text[cursor:]))\n return "".join(parts)\n''',
|
| 61 |
-
'''def _highlighted_processed_inner_html(\n processed_text: str,\n highlight_spans: list[tuple[int, int]],\n) -> str:\n """Return lossless compact display HTML for the static fallback pane."""\n\n parts: list[str] = []\n for segment in build_bound_placeholder_display_segments(processed_text, highlight_spans):\n display_text = escape(str(segment["display_text"]))\n full_placeholder = str(segment["full_placeholder"])\n compact_attributes = ""\n compact_class = ""\n if segment["compacted"]:\n compact_class = " sp-compact-placeholder"\n compact_attributes = (\n f' title="Volledige gebonden placeholder: {escape(full_placeholder)}"'\n f' aria-label="Gebonden placeholder, compact weergegeven als {display_text}"'\n )\n if segment["highlighted"]:\n parts.append(\n f'<mark class="sp-side-by-side-highlight-token{compact_class}"'\n f'{compact_attributes}>{display_text}</mark>'\n )\n elif segment["compacted"]:\n parts.append(\n f'<span class="sp-compact-placeholder"{compact_attributes}>'\n f'{display_text}</span>'\n )\n else:\n parts.append(display_text)\n return "".join(parts)\n''',
|
| 62 |
-
)
|
| 63 |
-
|
| 64 |
-
patch(
|
| 65 |
-
"side_by_side_review_panel_ui.py",
|
| 66 |
-
""" processed_legend = \"Geel = vervangen of gemaskeerde waarde\" if show_markers else \"Verwerkte tekst\"\n""",
|
| 67 |
-
""" compacted = any(\n segment[\"compacted\"]\n for segment in build_bound_placeholder_display_segments(processed_text, [])\n )\n if show_markers and compacted:\n processed_legend = \"Geel = vervangen; documentcode compact weergegeven\"\n elif show_markers:\n processed_legend = \"Geel = vervangen of gemaskeerde waarde\"\n elif compacted:\n processed_legend = \"Documentcode compact weergegeven\"\n else:\n processed_legend = \"Verwerkte tekst\"\n""",
|
| 68 |
-
)
|
| 69 |
-
|
| 70 |
-
patch(
|
| 71 |
-
"side_by_side_review_panel_ui.py",
|
| 72 |
-
""" processed_html = (\n _highlighted_processed_inner_html(processed_text, highlight_spans)\n if show_markers and highlight_spans\n else escape(processed_text)\n )\n""",
|
| 73 |
-
""" processed_html = _highlighted_processed_inner_html(\n processed_text,\n highlight_spans if show_markers else [],\n )\n""",
|
| 74 |
-
)
|
| 75 |
-
|
| 76 |
-
patch(
|
| 77 |
-
"side_by_side_review_panel_ui.py",
|
| 78 |
-
""" \"component_environment_switch\": INTERACTIVE_COMPONENT_ENV,\n \"pane_height\": SIDE_BY_SIDE_REVIEW_PANE_HEIGHT,\n""",
|
| 79 |
-
""" \"component_environment_switch\": INTERACTIVE_COMPONENT_ENV,\n \"bound_placeholder_display_compaction\": True,\n \"bound_placeholder_source_tokens_unchanged\": True,\n \"bound_placeholder_binding_entropy_changed\": False,\n \"pane_height\": SIDE_BY_SIDE_REVIEW_PANE_HEIGHT,\n""",
|
| 80 |
-
)
|
| 81 |
-
|
| 82 |
-
(ROOT / "ops/implement_bound_placeholder_display_compaction.py").unlink()
|
| 83 |
-
(ROOT / ".github/workflows/bound_placeholder_display_compaction.yml").unlink()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
side_by_side_review_panel_ui.py
CHANGED
|
@@ -15,6 +15,7 @@ from typing import Any, Mapping
|
|
| 15 |
import streamlit as st
|
| 16 |
import streamlit.components.v1 as components
|
| 17 |
|
|
|
|
| 18 |
from processed_text_selection_component import (
|
| 19 |
render_processed_text_selection_component,
|
| 20 |
)
|
|
@@ -96,19 +97,32 @@ def _highlighted_processed_inner_html(
|
|
| 96 |
processed_text: str,
|
| 97 |
highlight_spans: list[tuple[int, int]],
|
| 98 |
) -> str:
|
| 99 |
-
"""Return
|
| 100 |
|
| 101 |
parts: list[str] = []
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
"
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 112 |
return "".join(parts)
|
| 113 |
|
| 114 |
|
|
@@ -122,7 +136,18 @@ def _side_by_side_sync_scroll_html(
|
|
| 122 |
"""Build escaped HTML for the synchronized static fallback."""
|
| 123 |
|
| 124 |
source_html = escape(source_text)
|
| 125 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
return f"""
|
| 127 |
{_SYNC_SCROLL_COMPONENT_CSS}
|
| 128 |
<div class="sp-sync-scroll-wrapper">
|
|
@@ -195,10 +220,9 @@ def _render_static_fallback(
|
|
| 195 |
highlight_spans: list[tuple[int, int]],
|
| 196 |
show_markers: bool,
|
| 197 |
) -> None:
|
| 198 |
-
processed_html = (
|
| 199 |
-
|
| 200 |
-
if show_markers
|
| 201 |
-
else escape(processed_text)
|
| 202 |
)
|
| 203 |
components.html(
|
| 204 |
_side_by_side_sync_scroll_html(
|
|
@@ -352,6 +376,9 @@ def render_side_by_side_review_panel(
|
|
| 352 |
"static_fallback_used": static_fallback_used,
|
| 353 |
"component_error": component_error,
|
| 354 |
"component_environment_switch": INTERACTIVE_COMPONENT_ENV,
|
|
|
|
|
|
|
|
|
|
| 355 |
"pane_height": SIDE_BY_SIDE_REVIEW_PANE_HEIGHT,
|
| 356 |
"compact_legend": compact_legend,
|
| 357 |
"model": model,
|
|
|
|
| 15 |
import streamlit as st
|
| 16 |
import streamlit.components.v1 as components
|
| 17 |
|
| 18 |
+
from bound_placeholder_display import build_bound_placeholder_display_segments
|
| 19 |
from processed_text_selection_component import (
|
| 20 |
render_processed_text_selection_component,
|
| 21 |
)
|
|
|
|
| 97 |
processed_text: str,
|
| 98 |
highlight_spans: list[tuple[int, int]],
|
| 99 |
) -> str:
|
| 100 |
+
"""Return lossless compact display HTML for the static fallback pane."""
|
| 101 |
|
| 102 |
parts: list[str] = []
|
| 103 |
+
for segment in build_bound_placeholder_display_segments(processed_text, highlight_spans):
|
| 104 |
+
display_text = escape(str(segment["display_text"]))
|
| 105 |
+
full_placeholder = str(segment["full_placeholder"])
|
| 106 |
+
compact_attributes = ""
|
| 107 |
+
compact_class = ""
|
| 108 |
+
if segment["compacted"]:
|
| 109 |
+
compact_class = " sp-compact-placeholder"
|
| 110 |
+
compact_attributes = (
|
| 111 |
+
f' title="Volledige gebonden placeholder: {escape(full_placeholder)}"'
|
| 112 |
+
f' aria-label="Gebonden placeholder, compact weergegeven als {display_text}"'
|
| 113 |
+
)
|
| 114 |
+
if segment["highlighted"]:
|
| 115 |
+
parts.append(
|
| 116 |
+
f'<mark class="sp-side-by-side-highlight-token{compact_class}"'
|
| 117 |
+
f'{compact_attributes}>{display_text}</mark>'
|
| 118 |
+
)
|
| 119 |
+
elif segment["compacted"]:
|
| 120 |
+
parts.append(
|
| 121 |
+
f'<span class="sp-compact-placeholder"{compact_attributes}>'
|
| 122 |
+
f'{display_text}</span>'
|
| 123 |
+
)
|
| 124 |
+
else:
|
| 125 |
+
parts.append(display_text)
|
| 126 |
return "".join(parts)
|
| 127 |
|
| 128 |
|
|
|
|
| 136 |
"""Build escaped HTML for the synchronized static fallback."""
|
| 137 |
|
| 138 |
source_html = escape(source_text)
|
| 139 |
+
compacted = any(
|
| 140 |
+
segment["compacted"]
|
| 141 |
+
for segment in build_bound_placeholder_display_segments(processed_text, [])
|
| 142 |
+
)
|
| 143 |
+
if show_markers and compacted:
|
| 144 |
+
processed_legend = "Geel = vervangen; documentcode compact weergegeven"
|
| 145 |
+
elif show_markers:
|
| 146 |
+
processed_legend = "Geel = vervangen of gemaskeerde waarde"
|
| 147 |
+
elif compacted:
|
| 148 |
+
processed_legend = "Documentcode compact weergegeven"
|
| 149 |
+
else:
|
| 150 |
+
processed_legend = "Verwerkte tekst"
|
| 151 |
return f"""
|
| 152 |
{_SYNC_SCROLL_COMPONENT_CSS}
|
| 153 |
<div class="sp-sync-scroll-wrapper">
|
|
|
|
| 220 |
highlight_spans: list[tuple[int, int]],
|
| 221 |
show_markers: bool,
|
| 222 |
) -> None:
|
| 223 |
+
processed_html = _highlighted_processed_inner_html(
|
| 224 |
+
processed_text,
|
| 225 |
+
highlight_spans if show_markers else [],
|
|
|
|
| 226 |
)
|
| 227 |
components.html(
|
| 228 |
_side_by_side_sync_scroll_html(
|
|
|
|
| 376 |
"static_fallback_used": static_fallback_used,
|
| 377 |
"component_error": component_error,
|
| 378 |
"component_environment_switch": INTERACTIVE_COMPONENT_ENV,
|
| 379 |
+
"bound_placeholder_display_compaction": True,
|
| 380 |
+
"bound_placeholder_source_tokens_unchanged": True,
|
| 381 |
+
"bound_placeholder_binding_entropy_changed": False,
|
| 382 |
"pane_height": SIDE_BY_SIDE_REVIEW_PANE_HEIGHT,
|
| 383 |
"compact_legend": compact_legend,
|
| 384 |
"model": model,
|