salience-editor/python3/salience/static/index.html

179 lines
5.7 KiB
HTML
Raw Normal View History

2025-10-30 16:26:48 -07:00
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf8" />
<title>Salience</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/11.8.0/math.js" integrity="sha512-VW8/i4IZkHxdD8OlqNdF7fGn3ba0+lYqag+Uy4cG6BtJ/LIr8t23s/vls70pQ41UasHH0tL57GQfKDApqc9izA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<style>
body {
display: flex;
flex-direction: column;
height: 100vh;
margin: 0;
}
p {
width: 700px;
margin: 1em auto;
color: #4d4d4d;
font-family: sans-serif;
font-size: 15px;
line-height: 1.33em;
flex: 1;
overflow-y: scroll;
}
h1 {
width: 700px;
text-align: left;
margin: 15px auto;
margin-bottom: 0;
color: #000;
font-family: sans-serif;
font-size: 24px;
}
h1 span {
display: block;
font-size: 0.7em;
font-weight: normal;
color: #a0a0a0;
}
2025-10-30 16:26:48 -07:00
.controls {
width: 700px;
margin: 15px auto;
font-family: sans-serif;
}
.controls label {
margin-right: 10px;
color: #4d4d4d;
}
.controls select {
padding: 5px 10px;
font-size: 14px;
border: 1px solid #ccc;
border-radius: 4px;
background-color: white;
cursor: pointer;
}
span.sentence {
--salience: 1;
background-color: rgba(249, 239, 104, var(--salience));
}
span.highlight {
background-color: rgb(185, 225, 244);
}
::selection {
background: transparent;
}
</style>
</head>
<body>
<h1>
Salience
2025-10-30 16:26:48 -07:00
<span>sentence highlights based on their significance to the document</span>
</h1>
2025-10-30 16:26:48 -07:00
<div class="controls">
<label for="model-select">Model:</label>
<select id="model-select">
<option value="">Loading...</option>
</select>
</div>
<p id="content"></p>
<script type="text/javascript">
const content = document.querySelector('#content')
2025-10-30 16:26:48 -07:00
const modelSelect = document.querySelector('#model-select')
let adjacency = null
2025-10-30 16:26:48 -07:00
let currentModel = 'all-mpnet-base-v2'
function scale(score) {
return Math.max(0, Math.min(1, score ** 3 - 0.95))
}
2025-10-30 16:26:48 -07:00
let exponent = 5
2025-10-30 16:26:48 -07:00
const redraw = () => {
if (!adjacency) return
const sentences = document.querySelectorAll('span.sentence')
if (!window.getSelection().isCollapsed) {
const sel = window.getSelection()
const fromNode = sel.anchorNode.parentNode
const toNode = sel.extentNode.parentNode
const fromIdx = Array.from(sentences).indexOf(fromNode)
const toIdx = Array.from(sentences).indexOf(toNode)
const range = [fromIdx, toIdx]
console.log('range', range)
range.sort((a, b) => a - b)
const vec = adjacency.map((x, i) => (i >= range[0] && i <= range[1]) ? 1 : 0)
const vec_sum = vec.reduce((a, x) => a + x, 0)
const scores = math.multiply(vec, adjacency).map(x => x * adjacency.length / vec_sum)
Array.from(sentences).forEach((node, i) => {
node.style.setProperty('--salience', scale(scores[i]))
if (i >= range[0] && i <= range[1]) node.classList.add('highlight')
else node.classList.remove('highlight')
})
} else {
const initial = adjacency.map(() => 1)
const scores = math.multiply(initial, math.pow(adjacency, exponent))
Array.from(sentences).forEach((node, i) => {
node.style.setProperty('--salience', scale(scores[i]))
node.classList.remove('highlight')
})
}
}
2025-10-30 16:26:48 -07:00
function loadSalience(model) {
// Clear existing content
content.innerHTML = ''
adjacency = null
fetch(`/salience?model=${encodeURIComponent(model)}`).then(async res => {
const data = await res.json()
console.log(data)
const source = data.source
const intervals = data.intervals
const tokens = intervals.map(([start, end]) => source.substr(start, end - start))
adjacency = data.adjacency
tokens.forEach((t, i) => {
const token = document.createElement('span')
token.innerText = t
token.classList.add('sentence')
content.appendChild(token)
if (tokens[i+1] && intervals[i+1][0] > intervals[i][1]) {
const intervening = document.createElement('span')
const start = intervals[i][1]
intervening.innerText = source.substr(start, intervals[i+1][0] - start)
content.appendChild(intervening)
}
})
redraw()
})
}
// Load available models and populate dropdown
fetch('/models').then(async res => {
const models = await res.json()
modelSelect.innerHTML = ''
models.forEach(model => {
const option = document.createElement('option')
option.value = model
option.textContent = model
if (model === currentModel) {
option.selected = true
}
2025-10-30 16:26:48 -07:00
modelSelect.appendChild(option)
})
})
2025-10-30 16:26:48 -07:00
// Handle model selection change
modelSelect.addEventListener('change', (e) => {
currentModel = e.target.value
loadSalience(currentModel)
})
// Disabled functionality to center highlights on a selected fragment
// document.addEventListener('mousemove', redraw)
// document.addEventListener('mouseup', redraw)
// Load initial salience data
loadSalience(currentModel)
</script>
</body>
</html>