Skip to content

Commit c41cdd7

Browse files
cubapthehabesCopilot
authored
Transcription alt (#325)
* simple transcription * aligning the images * Update index.js * Support simple transcription interface in tools and filters Updated PageTool, QuickTypeTool, and WorkspaceTools to support both standard and simple transcription interfaces by detecting and handling 'tpen-simple-transcription' elements. Added event dispatching for drawer open/close to allow interfaces to recalculate layout. Refactored filter and magnifier logic to apply to both interface types. SimpleTranscriptionInterface now listens for drawer events and exposes transition helpers for smoother UI updates. * magnifier tool * active line for history tool * Update index.js * Update index.js * Fix duplicate event listener accumulation in workspace-tools magnifier button (#335) * Initial plan * Fix duplicate event listener issue for magnifier button Co-authored-by: cubap <1119165+cubap@users.noreply.github.com> * Add null safety checks for magnifier button Co-authored-by: cubap <1119165+cubap@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: cubap <1119165+cubap@users.noreply.github.com> * Extract duplicate filter construction logic into helper function (#336) * Initial plan * Extract duplicate filter logic into helper function Co-authored-by: cubap <1119165+cubap@users.noreply.github.com> * Add documentation to clarify when each filter method should be used Co-authored-by: cubap <1119165+cubap@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: cubap <1119165+cubap@users.noreply.github.com> * Fix event listener memory leak in loadRightPaneContent (#337) * Initial plan * Fix event listener memory leak in simple-transcription tool loading Co-authored-by: cubap <1119165+cubap@users.noreply.github.com> * Simplify listener storage by storing function directly Co-authored-by: cubap <1119165+cubap@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: cubap <1119165+cubap@users.noreply.github.com> * cleanup events and logs * saving QuickType fixed --------- Co-authored-by: Bryan Haberberger <bryan.j.haberberger@slu.edu> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: cubap <1119165+cubap@users.noreply.github.com>
1 parent dc58eec commit c41cdd7

11 files changed

Lines changed: 1024 additions & 56 deletions

File tree

components/line-history/index.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,17 +72,14 @@ class TPENLineHistory extends HTMLElement {
7272
* @param {Object} lineData - The line data object
7373
*/
7474
async fetchLineHistory(lineData) {
75-
console.log('fetchLineHistory called with:', lineData)
7675
// If the line has a URI, fetch its history using RerumHistoryData
7776
const uri = lineData.id ?? lineData['@id']
7877
if (!uri) {
79-
console.log('No URI found in line data, using fallback')
8078
// No URI, just show current state
8179
this.historyData = [lineData]
8280
this.historyGraph = null
8381
return
8482
}
85-
console.log('Using URI for history fetch:', uri)
8683
try {
8784
// Clean up previous history data instance
8885
if (this.rerumHistoryData) {

components/line-image/index.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ class TpenLineImage extends HTMLElement {
5252
const localIiifManifest = this.#canvasPanel.closest('[iiif-manifest]')?.getAttribute('iiif-manifest') ?? this.closest('[iiif-manifest]')?.getAttribute('iiif-manifest')
5353
if (localIiifContent) {
5454
this.line = decodeContentState(localIiifContent)
55-
console.log(localIiifContent)
5655
}
5756
if (localIiifManifest) {
5857
this.manifest = localIiifManifest

components/magnifier-tool/index.js

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@ export class MagnifierTool extends HTMLElement {
5353
if (!magnifier || !img) return
5454
const magnifierSize = magnifier.offsetWidth || 200
5555
const halfSize = magnifierSize / 2
56-
magnifier.style.backgroundSize = `${img.width * zoomLevel}px ${img.height * zoomLevel}px`
56+
// Use actual on-screen size (accounts for CSS transforms like scale)
57+
const imgRect = img.getBoundingClientRect()
58+
magnifier.style.backgroundSize = `${imgRect.width * zoomLevel}px ${imgRect.height * zoomLevel}px`
5759
magnifier.style.backgroundPosition = `${-centerX * zoomLevel + halfSize}px ${-centerY * zoomLevel + halfSize}px`
5860
}
5961

@@ -101,9 +103,9 @@ export class MagnifierTool extends HTMLElement {
101103
y = Math.max(minY, Math.min(y, imgRect.height + this.boundsOffset - halfSize))
102104

103105
let rightPane = null
104-
const tpenInterface = document.querySelector('tpen-transcription-interface')
105-
if (tpenInterface?.shadowRoot) {
106-
rightPane = tpenInterface.shadowRoot.querySelector('.right-pane')
106+
const iface = document.querySelector('tpen-transcription-interface') || document.querySelector('tpen-simple-transcription')
107+
if (iface?.shadowRoot) {
108+
rightPane = iface.shadowRoot.querySelector('.right-pane')
107109
}
108110

109111
if (rightPane && rightPane.offsetParent !== null) {
@@ -137,19 +139,36 @@ export class MagnifierTool extends HTMLElement {
137139
const img = this.imageElem
138140
if (!magnifier || !img) return
139141

140-
const magnifierSize = magnifier.offsetWidth || 200
141-
magnifier.style.width = `${magnifierSize}px`
142-
magnifier.style.height = `${magnifierSize}px`
142+
const baseSize = parseFloat(getComputedStyle(magnifier).width) || 200
143+
magnifier.style.width = `${Math.round(baseSize)}px`
144+
magnifier.style.height = `${Math.round(baseSize)}px`
143145

144146
magnifier.style.display = 'block'
145147
magnifier.style.backgroundImage = `url(${img.src})`
146148
magnifier.style.backgroundRepeat = 'no-repeat'
147-
magnifier.style.backgroundSize = `${img.width * this.zoomLevel}px ${img.height * this.zoomLevel}px`
148-
const halfSize = magnifierSize / 2
149+
// Use on-screen size for background sizing to match visual scale
150+
const imgRect = img.getBoundingClientRect()
151+
magnifier.style.backgroundSize = `${imgRect.width * this.zoomLevel}px ${imgRect.height * this.zoomLevel}px`
152+
const halfSize = (parseFloat(getComputedStyle(magnifier).width) || baseSize) / 2
153+
154+
// Determine a visible anchor area to place the magnifier initially
155+
// Prefer the visible container around the image (e.g., #imgTop), fall back to the image rect
156+
let anchorRect = imgRect
157+
const hostContainer = img.closest('#imgTop, #imgBottom')
158+
if (hostContainer) {
159+
const rc = hostContainer.getBoundingClientRect()
160+
// If container has no area (hidden), fallback to imgRect
161+
if (rc.width > 0 && rc.height > 0) anchorRect = rc
162+
}
163+
164+
// Clear conflicting positioning
165+
magnifier.style.removeProperty('right')
149166

150-
magnifier.style.left = `${img.offsetLeft}px`
151-
magnifier.style.top = `${img.offsetTop}px`
152-
magnifier.style.backgroundPosition = `-${halfSize * this.zoomLevel * img.width}px -${halfSize * this.zoomLevel}px`
167+
// Center the magnifier within the visible container
168+
const initLeft = anchorRect.left + (anchorRect.width / 2) - halfSize
169+
const initTop = anchorRect.top + (anchorRect.height / 2) - halfSize
170+
magnifier.style.left = `${Math.round(initLeft)}px`
171+
magnifier.style.top = `${Math.round(initTop)}px`
153172

154173
this.updateMagnifier()
155174
this.isMagnifierVisible = true
@@ -191,7 +210,7 @@ export class MagnifierTool extends HTMLElement {
191210
pointer-events: all;
192211
box-shadow: 0 0 12px rgba(0,0,0,0.3);
193212
user-select: none;
194-
z-index: 20;
213+
z-index: 10000;
195214
top: 60px;
196215
right: 20px;
197216
}

components/page-tool/index.js

Lines changed: 84 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,15 @@ export default class PageTool extends HTMLElement {
1212
this.drawerPosition = 'right'
1313
this.contrast = 100
1414
this.brightness = 100
15+
this.grayscaleActive = false
16+
this.invertActive = false
17+
}
18+
19+
// Helper to find the transcription interface (supports both standard and simple versions)
20+
getTranscriptionInterface() {
21+
let iface = document.querySelector('tpen-transcription-interface')
22+
if (!iface) iface = document.querySelector('tpen-simple-transcription')
23+
return iface
1524
}
1625

1726
get drawerContent() {
@@ -58,27 +67,58 @@ export default class PageTool extends HTMLElement {
5867
openDrawer() {
5968
const drawer = this.shadowRoot.querySelector('.drawer')
6069
if (!drawer) return
61-
const container = document.querySelector('tpen-transcription-interface').shadowRoot.querySelector('.container')
62-
if(document.querySelector('tpen-transcription-interface').shadowRoot.querySelector('.container.active-splitscreen .right-pane')){
70+
71+
const iface = this.getTranscriptionInterface()
72+
if (!iface?.shadowRoot) return
73+
74+
const container = iface.shadowRoot.querySelector('.container')
75+
if (!container) return
76+
77+
if (iface.shadowRoot.querySelector('.container.active-splitscreen .right-pane')) {
6378
container.classList.remove('active-splitscreen')
6479
container.classList.add('no-splitscreen')
6580
}
81+
82+
// Dispatch event before changing width so interface can prepare
83+
iface.dispatchEvent(new CustomEvent('drawer-opening', { bubbles: true, composed: true }))
84+
6685
container.style.width = 'calc(100% - 320px)'
6786
drawer.classList.add('open')
6887
this.isDrawerOpen = true
6988
this.drawerToggleBtn.focus()
89+
90+
// Dispatch event after width change to trigger recalc
91+
setTimeout(() => {
92+
iface.dispatchEvent(new CustomEvent('drawer-opened', { bubbles: true, composed: true }))
93+
}, 0)
7094
}
7195

7296
closeDrawer() {
7397
const drawer = this.shadowRoot.querySelector('.drawer')
7498
if (!drawer) return
75-
const container = document.querySelector('tpen-transcription-interface').shadowRoot.querySelector('.container')
76-
container.style.width = '100%'
99+
100+
const iface = this.getTranscriptionInterface()
101+
if (!iface?.shadowRoot) return
102+
103+
const container = iface.shadowRoot.querySelector('.container')
104+
if (!container) return
105+
106+
// Dispatch event before changing width
107+
iface.dispatchEvent(new CustomEvent('drawer-closing', { bubbles: true, composed: true }))
108+
109+
// Remove the inline width style to return to CSS defaults
110+
container.style.width = ''
77111
drawer.classList.remove('open')
78112
this.isDrawerOpen = false
79113
this.drawerToggleBtn.blur()
114+
115+
// Dispatch event after width change
116+
setTimeout(() => {
117+
iface.dispatchEvent(new CustomEvent('drawer-closed', { bubbles: true, composed: true }))
118+
}, 0)
80119
}
81120

121+
// Apply filters to standard transcription interface images (uses CSS classes on imageEl)
82122
updateMainImageFilters(imageEl) {
83123
if (!imageEl) return
84124

@@ -92,13 +132,36 @@ export default class PageTool extends HTMLElement {
92132
imageEl.style.filter = filters.join(' ')
93133
}
94134

135+
// Apply filters to simple transcription interface images (uses component state)
136+
applyFiltersToImage(imageEl) {
137+
if (!imageEl) return
138+
139+
const filters = []
140+
if (this.grayscaleActive) filters.push('grayscale(100%)')
141+
if (this.invertActive) filters.push('invert(100%)')
142+
filters.push(`contrast(${this.contrast}%)`)
143+
filters.push(`brightness(${this.brightness}%)`)
144+
145+
imageEl.style.transition = 'filter 250ms ease'
146+
imageEl.style.filter = filters.join(' ')
147+
}
148+
95149
applyFilters() {
96-
const transcriptionInterface = document.querySelector('tpen-transcription-interface')?.shadowRoot
150+
const iface = this.getTranscriptionInterface()
151+
const transcriptionInterface = iface?.shadowRoot
97152
if (!transcriptionInterface) return
98153

154+
// Handle standard transcription interface with image fragments
99155
const imageEl = transcriptionInterface.querySelector('tpen-image-fragment')?.shadowRoot?.querySelector('img')
100156
if (imageEl) this.updateMainImageFilters(imageEl)
101157

158+
// Handle simple transcription interface - apply filters to both top and bottom images
159+
const imgTopImg = transcriptionInterface.querySelector('#imgTop img')
160+
const imgBottomImg = transcriptionInterface.querySelector('#imgBottom img')
161+
162+
this.applyFiltersToImage(imgTopImg)
163+
this.applyFiltersToImage(imgBottomImg)
164+
102165
const canvasPanel = transcriptionInterface.querySelector('tpen-line-image')?.shadowRoot?.querySelector('canvas-panel')?.shadowRoot
103166
if (!canvasPanel) return
104167

@@ -124,15 +187,27 @@ export default class PageTool extends HTMLElement {
124187
}
125188

126189
toggleFilter(type) {
127-
const transcriptionInterface = document.querySelector('tpen-transcription-interface')?.shadowRoot
190+
const iface = this.getTranscriptionInterface()
191+
const transcriptionInterface = iface?.shadowRoot
128192
if (!transcriptionInterface) return
193+
194+
// Track filter state
195+
if (type === 'grayscale') {
196+
this.grayscaleActive = !this.grayscaleActive
197+
} else if (type === 'invert') {
198+
this.invertActive = !this.invertActive
199+
}
200+
201+
// Standard interface with image fragments
129202
const imageFragment = transcriptionInterface.querySelector('tpen-image-fragment')?.shadowRoot
130203
const imageEl = imageFragment?.querySelector('img')
131204

132205
if (imageEl) {
133206
imageEl.classList.toggle(type)
134-
this.applyFilters()
135207
}
208+
209+
// Apply filters (handles both standard and simple interfaces)
210+
this.applyFilters()
136211

137212
const btnClass = type === 'grayscale' ? '.grayscale-btn' : '.invert-btn'
138213
const btn = this.shadowRoot.querySelector(btnClass)
@@ -165,7 +240,8 @@ export default class PageTool extends HTMLElement {
165240
if (contrastSlider) contrastSlider.value = 100
166241
if (brightnessSlider) brightnessSlider.value = 100
167242

168-
const transcriptionInterface = document.querySelector('tpen-transcription-interface')?.shadowRoot
243+
const iface = this.getTranscriptionInterface()
244+
const transcriptionInterface = iface?.shadowRoot
169245
const imageEl = transcriptionInterface
170246
?.querySelector('tpen-image-fragment')?.shadowRoot?.querySelector('img')
171247

components/quicktype-tool/index.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ class QuickTypeTool extends HTMLElement {
5656
this.shadowRoot.querySelectorAll('.char-button').forEach(btn => {
5757
btn.addEventListener('click', () => {
5858
const char = btn.textContent
59-
let textAreaContent = document.querySelector('tpen-transcription-interface').shadowRoot.querySelector('tpen-transcription-block').shadowRoot.querySelector('.transcription-input')
59+
const iface = document.querySelector('tpen-transcription-interface') || document.querySelector('tpen-simple-transcription')
60+
const block = iface?.shadowRoot?.querySelector('tpen-transcription-block')?.shadowRoot
61+
let textAreaContent = block?.querySelector('.transcription-input')
6062

6163
if (textAreaContent && textAreaContent instanceof HTMLInputElement) {
6264
const start = textAreaContent.selectionStart
@@ -307,7 +309,13 @@ class QuickTypeToolButton extends HTMLElement {
307309
const quicktypeBtn = this.shadowRoot.querySelector('.quicktype-btn')
308310

309311
quicktypeBtn.addEventListener('click', () => {
310-
const charPanel = document.querySelector('tpen-transcription-interface').shadowRoot.querySelector('tpen-workspace-tools').shadowRoot.querySelector('tpen-quicktype-tool').shadowRoot.querySelector('.char-panel')
312+
const iface = document.querySelector('tpen-transcription-interface') || document.querySelector('tpen-simple-transcription')
313+
const charPanel = iface?.shadowRoot
314+
?.querySelector('tpen-workspace-tools')?.shadowRoot
315+
?.querySelector('tpen-quicktype-tool')?.shadowRoot
316+
?.querySelector('.char-panel')
317+
318+
if (!charPanel) return
311319

312320
if (charPanel.classList.contains('show')) {
313321
// Close animation

components/quicktype-tool/quicktype-editor-dialog.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -732,7 +732,7 @@ class QuickTypeEditorDialog extends HTMLElement {
732732

733733
getQuicktypeItemMarkup(item, index, isNewlyAdded) {
734734
const evaluation = evaluateEntry(item)
735-
const safeItem = escapeHTML(item)
735+
const safeItem = escapeHtml(item)
736736
const shortcut = this.generateShortcut(index)
737737
const invalidClass = evaluation.valid ? '' : ' invalid'
738738
const ariaInvalid = evaluation.valid ? '' : ' aria-invalid="true"'

0 commit comments

Comments
 (0)