Changes to mitosite.powerhouse.wiki

Breck Yunits
Breck Yunits
4 days ago
readme.scroll
Changed around line 9: website generated by Claude from prompt: MitoSite. You visit Mitosite and your w
-
+ [] captured images should save automatically
+ [] add video capture
Breck Yunits
Breck Yunits
4 days ago
.gitignore
Changed around line 1
+ .*
+ *.html
readme.scroll
Changed around line 8: website generated by Claude from prompt: MitoSite. You visit Mitosite and your w
+ [] add point cloud generation
+
Breck Yunits
Breck Yunits
4 days ago
updated readme.scroll
readme.scroll
Changed around line 4: website generated by Claude from prompt: MitoSite. You visit Mitosite and your w
- [] Fix width/height
+ [] Fix width/height
+ [] including frame stitching
+ [] extended depth of focus
+ [] annotations measurements
+
Breck Yunits
Breck Yunits
4 days ago
updated readme.scroll
readme.scroll
Changed around line 1
- website generated by Claude from prompt: MitoSite. You visit Mitosite and your webcam takes up nearly the full screen, with a cool border around it. Then, there's a very cool HUD. We are going to plug my webcam into my optical microscope, which can zoom in to 40x 250x and 400x. The Hud Shows the current zoom, and then also shows a scale which shows in micrometers we are looking at. so that is the HUD.
+ website generated by Claude from prompt: MitoSite. You visit Mitosite and your webcam takes up nearly the full screen, with a cool border around it. Then, there's a very cool HUD. We are going to plug my webcam into my optical microscope, which can zoom in to 40x 250x and 400x. The Hud Shows the current zoom, and then also shows a scale which shows in micrometers we are looking at. so that is the HUD.
+
+ # Todo:
+
+ [] Fix width/height
Breck Yunits
Breck Yunits
9 days ago
mitosite.js
Changed around line 4: class MitoSiteApp {
+ this.minimaps = [] // Array to store minimap objects
Changed around line 175: m createMinimapCommand Overlay
- let minimap = document.querySelector(".minimap")
- if (minimap) {
- minimap.remove()
- return
+ // Create a new minimap object
+ const minimap = {
+ canvas: document.createElement("canvas"),
+ zoomLevel: this.currentZoomLevel,
+ imageData: null,
- minimap = document.createElement("canvas")
- minimap.className = "minimap"
- minimap.width = this.side
- minimap.height = this.side
- minimap.style.cssText = `
+ // Set up canvas properties
+ minimap.canvas.className = "minimap"
+ minimap.canvas.width = this.side
+ minimap.canvas.height = this.side
+
+ // Calculate position based on number of existing minimaps
+ const offset = this.minimaps.length * 220 // 200px width + 20px spacing
+ minimap.canvas.style.cssText = `
- right: 10px;
+ right: ${10 + offset}px;
Changed around line 200: m createMinimapCommand Overlay
- document.body.appendChild(minimap)
- this.minimapZoom = "40x"
+ // Add zoom level label
+ const label = document.createElement("div")
+ label.style.cssText = `
+ position: fixed;
+ right: ${10 + offset}px;
+ top: 220px;
+ width: 200px;
+ text-align: center;
+ color: white;
+ font-size: 14px;
+ background: rgba(0, 0, 0, 0.7);
+ padding: 4px;
+ border-radius: 4px;
+ `
+ label.textContent = `${this.currentZoomLevel} View`
+ minimap.label = label
- const ctx = minimap.getContext("2d")
- ctx.drawImage(data.video, 0, 0, minimap.width, minimap.height)
- this.minimapImageData = ctx.getImageData(
+ // Draw initial image
+ const ctx = minimap.canvas.getContext("2d")
+ ctx.drawImage(data.video, 0, 0, minimap.canvas.width, minimap.canvas.height)
+ minimap.imageData = ctx.getImageData(
- minimap.width,
- minimap.height,
+ minimap.canvas.width,
+ minimap.canvas.height,
- this.updateMinimap()
+ // Add close button
+ const closeButton = document.createElement("button")
+ closeButton.style.cssText = `
+ position: absolute;
+ top: 5px;
+ right: 5px;
+ background: rgba(255, 255, 255, 0.3);
+ border: none;
+ color: white;
+ width: 20px;
+ height: 20px;
+ border-radius: 50%;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 12px;
+ `
+ closeButton.textContent = "×"
+ closeButton.onclick = (e) => {
+ e.stopPropagation()
+ this.removeMinimapByIndex(this.minimaps.indexOf(minimap))
+ }
+ minimap.canvas.appendChild(closeButton)
+
+ // Add to DOM and store in minimaps array
+ document.body.appendChild(minimap.canvas)
+ document.body.appendChild(minimap.label)
+ this.minimaps.push(minimap)
+
+ this.updateAllMinimaps()
+ }
+
+ removeMinimapByIndex(index) {
+ if (index >= 0 && index < this.minimaps.length) {
+ const minimap = this.minimaps[index]
+ minimap.canvas.remove()
+ minimap.label.remove()
+ this.minimaps.splice(index, 1)
+
+ // Reposition remaining minimaps
+ this.minimaps.forEach((map, idx) => {
+ const offset = idx * 220
+ map.canvas.style.right = `${10 + offset}px`
+ map.label.style.right = `${10 + offset}px`
+ })
+ }
- updateMinimap() {
- // first, clear any red boxes in the minimap
- // then calculate the red box side length.
- // the box side length will be side * minimapZoom/currentZoomLevel
- // draw a red box in the minimap, centered around the center
- // so if minimap at 40x and the new zoom is 400x, and the side was 1000, then the red
- // box should be 40x on each side, positioned around the center.
- const minimap = document.querySelector(".minimap")
- const ctx = minimap.getContext("2d")
- ctx.clearRect(0, 0, minimap.width, minimap.height)
+ updateAllMinimaps() {
+ this.minimaps.forEach((minimap) => {
+ const ctx = minimap.canvas.getContext("2d")
+ ctx.clearRect(0, 0, minimap.canvas.width, minimap.canvas.height)
- // now restore the minimap image
- ctx.putImageData(this.minimapImageData, 0, 0)
+ // Restore the original image
+ ctx.putImageData(minimap.imageData, 0, 0)
- // Calculate box size based on zoom levels
- const zoomRatio =
- parseInt(this.minimapZoom) / parseInt(this.currentZoomLevel)
- const boxSize = this.side * zoomRatio
+ // Calculate box size based on zoom levels
+ const zoomRatio =
+ parseInt(minimap.zoomLevel) / parseInt(this.currentZoomLevel)
+ const boxSize = this.side * zoomRatio
- // Draw red box centered in minimap
- ctx.strokeStyle = "red"
- ctx.lineWidth = 2
- const x = (minimap.width - boxSize) / 2
- const y = (minimap.height - boxSize) / 2
- ctx.strokeRect(x, y, boxSize, boxSize)
+ // Draw red box centered in minimap
+ ctx.strokeStyle = "red"
+ ctx.lineWidth = 2
+ const x = (minimap.canvas.width - boxSize) / 2
+ const y = (minimap.canvas.height - boxSize) / 2
+ ctx.strokeRect(x, y, boxSize, boxSize)
+ })
Changed around line 531: m createMinimapCommand Overlay
- this.updateMinimap()
+ this.updateAllMinimaps()
Breck Yunits
Breck Yunits
9 days ago
minimap v1
brev.js
Changed around line 0
- const fs = require("fs");
- const decompress = require("decompress");
-
- const nvai_url =
- "https://ai.api.nvidia.com/v1/cv/nvidia/retail-object-detection";
- const header_auth = `Bearer $API_KEY_REQUIRED_IF_EXECUTING_OUTSIDE_NGC`;
-
- async function _upload_asset(input, description) {
- const assets_url = "https://api.nvcf.nvidia.com/v2/nvcf/assets";
-
- const headers = {
- Authorization: header_auth,
- "Content-Type": "application/json",
- accept: "application/json",
- };
-
- const s3_headers = {
- "x-amz-meta-nvcf-asset-description": description,
- "content-type": "video/mp4",
- };
-
- const payload = {
- contentType: "video/mp4",
- description: description,
- };
-
- const response = await fetch(assets_url, {
- method: "POST",
- body: JSON.stringify(payload),
- headers: headers,
- });
-
- const data = await response.json();
-
- const asset_url = data["uploadUrl"];
- const asset_id = data["assetId"];
-
- const fileData = fs.readFileSync(input);
-
- await fetch(asset_url, {
- method: "PUT",
- body: fileData,
- headers: s3_headers,
- });
-
- return asset_id.toString();
- }
-
- (async () => {
- if (process.argv.length != 4) {
- console.log("Usage: node test.js ");
- process.exit(1);
- }
-
- // Upload specified user asset
- const asset_id = await _upload_asset(`${process.argv[2]}`, "Input Video");
-
- // Metadata for the request
- const inputs = { input_video: asset_id, threshold: 0.9 };
- const asset_list = asset_id;
- const headers = {
- "Content-Type": "application/json",
- "NVCF-INPUT-ASSET-REFERENCES": asset_list,
- "NVCF-FUNCTION-ASSET-IDS": asset_list,
- Authorization: header_auth,
- };
-
- // Make the request to nvcf
- const response = await fetch(nvai_url, {
- method: "POST",
- body: JSON.stringify(inputs),
- headers: headers,
- });
-
- // Gather the binary response data
- const arrayBuffer = await response.arrayBuffer();
- const buffer = Buffer.from(arrayBuffer);
-
- const zipname = `${process.argv[3]}.zip`;
- fs.writeFileSync(zipname, buffer);
-
- // Unzip the response synchronously
- await decompress(zipname, process.argv[3]);
-
- // Log the output directory and its contents
- console.log(`Response saved to ${process.argv[3]}`);
- console.log(fs.readdirSync(process.argv[3]));
- })();
index.scroll
Changed around line 6: powerhouseButton
-
Changed around line 24: h1 Mitosite
- webcam 800 600
+ webcam 700 700
- hud Swift SW200DL 100x
+ hud Microscope
mitosite.js
Changed around line 16: class MitoSiteApp {
+ this.updateZoomDisplay()
Changed around line 169: h flipHorizontalCommand Camera
+ m createMinimapCommand Overlay
+ createMinimapCommand(container) {
+ const data = this.instances.get(container)
+ let minimap = document.querySelector(".minimap")
+
+ if (minimap) {
+ minimap.remove()
+ return
+ }
+
+ minimap = document.createElement("canvas")
+ minimap.className = "minimap"
+ minimap.width = this.side
+ minimap.height = this.side
+ minimap.style.cssText = `
+ position: fixed;
+ right: 10px;
+ top: 10px;
+ width: 200px;
+ height: 200px;
+ border: 2px solid white;
+ background: black;
+ display: block;
+ `
+ document.body.appendChild(minimap)
+
+ this.minimapZoom = "40x"
+
+ const ctx = minimap.getContext("2d")
+ ctx.drawImage(data.video, 0, 0, minimap.width, minimap.height)
+ this.minimapImageData = ctx.getImageData(
+ 0,
+ 0,
+ minimap.width,
+ minimap.height,
+ )
+
+ this.updateMinimap()
+ }
+
+ updateMinimap() {
+ // first, clear any red boxes in the minimap
+ // then calculate the red box side length.
+ // the box side length will be side * minimapZoom/currentZoomLevel
+ // draw a red box in the minimap, centered around the center
+ // so if minimap at 40x and the new zoom is 400x, and the side was 1000, then the red
+ // box should be 40x on each side, positioned around the center.
+ const minimap = document.querySelector(".minimap")
+ const ctx = minimap.getContext("2d")
+ ctx.clearRect(0, 0, minimap.width, minimap.height)
+
+ // now restore the minimap image
+ ctx.putImageData(this.minimapImageData, 0, 0)
+
+ // Calculate box size based on zoom levels
+ const zoomRatio =
+ parseInt(this.minimapZoom) / parseInt(this.currentZoomLevel)
+ const boxSize = this.side * zoomRatio
+
+ // Draw red box centered in minimap
+ ctx.strokeStyle = "red"
+ ctx.lineWidth = 2
+ const x = (minimap.width - boxSize) / 2
+ const y = (minimap.height - boxSize) / 2
+ ctx.strokeRect(x, y, boxSize, boxSize)
+ }
+
Changed around line 469: z changeZoomLevelDisplayCommand Overlay
+ this.updateZoomDisplay()
+ this.updateMinimap()
+ }
+ updateZoomDisplay() {
- let zoomDisplay = container.querySelector(".top-left")
+ let zoomDisplay = document.querySelector(".top-left")
- currentZoomLevel = "100x"
+ currentZoomLevel = "40x"
Changed around line 637: z changeZoomLevelDisplayCommand Overlay
- select.style.display = "block"
+ select.style.display = "none"
Changed around line 672: z changeZoomLevelDisplayCommand Overlay
+ side = 700
+
+ const { side } = this
Changed around line 686: z changeZoomLevelDisplayCommand Overlay
- width: parseInt(container.dataset.width) || 640,
- height: parseInt(container.dataset.height) || 480,
+ width: parseInt(container.dataset.width) || side,
+ height: parseInt(container.dataset.height) || side,
Changed around line 942: document.addEventListener("DOMContentLoaded", async () => {
+ window.app = app
style.css
Changed around line 52: body {
- left: 50%;
+ right: 50%;
Breck Yunits
Breck Yunits
9 days ago
code.parsers
Changed around line 44: hudElementParser
- return `
${this.content}
`
+ return `
${this.content}
`
mitosite.js
Changed around line 15: class MitoSiteApp {
-
+ // Modified toggleHelpCommand method
+ toggleHelpCommand(container) {
+ let helpModal = container.querySelector(".help-modal")
+
+ if (helpModal) {
+ // If modal exists, toggle its visibility
+ const isVisible = helpModal.style.display !== "none"
+ helpModal.style.display = isVisible ? "none" : "block"
+ return
+ }
+
+ // Create help modal
+ helpModal = document.createElement("div")
+ helpModal.className = "help-modal"
+ helpModal.style.cssText = `
+ position: fixed;
+ top: 0;
+ right: 0;
+ width: 300px;
+ height: 100vh;
+ background: rgba(0, 0, 0, 0.85);
+ color: white;
+ padding: 20px;
+ overflow-y: auto;
+ z-index: 1000;
+ box-shadow: -2px 0 10px rgba(0, 0, 0, 0.3);
+ `
+
+ // Create heading
+ const heading = document.createElement("h2")
+ heading.textContent = "Keyboard Shortcuts"
+ heading.style.marginBottom = "20px"
+ helpModal.appendChild(heading)
+
+ // Parse shortcuts and create clickable elements
+ const shortcuts = this.keyboardShortcuts
+ .split("\n")
+ .slice(1) // Skip header row
+ .map((line) => {
+ const [key, command, category] = line.split(" ")
+ return { key, command, category }
+ })
+
+ // Group shortcuts by category
+ const categorizedShortcuts = shortcuts.reduce(
+ (acc, { key, command, category }) => {
+ if (!acc[category]) {
+ acc[category] = []
+ }
+ acc[category].push({ key, command })
+ return acc
+ },
+ {},
+ )
+
+ // Create sections for each category
+ Object.entries(categorizedShortcuts).forEach(([category, shortcuts]) => {
+ const categorySection = document.createElement("div")
+ categorySection.style.marginBottom = "20px"
+
+ const categoryHeading = document.createElement("h3")
+ categoryHeading.textContent = category
+ categoryHeading.style.marginBottom = "10px"
+ categorySection.appendChild(categoryHeading)
+
+ shortcuts.forEach(({ key, command }) => {
+ const shortcutElement = document.createElement("div")
+ shortcutElement.style.cssText = `
+ display: flex;
+ align-items: center;
+ margin-bottom: 10px;
+ cursor: pointer;
+ padding: 5px;
+ border-radius: 4px;
+ `
+
+ // Add hover effect
+ shortcutElement.addEventListener("mouseenter", () => {
+ shortcutElement.style.background = "rgba(255, 255, 255, 0.1)"
+ })
+ shortcutElement.addEventListener("mouseleave", () => {
+ shortcutElement.style.background = "none"
+ })
+
+ const keyElement = document.createElement("kbd")
+ keyElement.textContent = key
+ keyElement.style.cssText = `
+ background: rgba(255, 255, 255, 0.2);
+ padding: 2px 6px;
+ border-radius: 3px;
+ margin-right: 10px;
+ min-width: 20px;
+ text-align: center;
+ `
+
+ const commandName = command.replace("Command", "")
+ const displayName = commandName
+ .split(/(?=[A-Z])/)
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
+ .join(" ")
+
+ const descElement = document.createElement("span")
+ descElement.textContent = displayName
+
+ shortcutElement.appendChild(keyElement)
+ shortcutElement.appendChild(descElement)
+
+ // Add click handler to trigger the command
+ shortcutElement.addEventListener("click", () => {
+ if (typeof this[command] === "function") {
+ this[command](container)
+ }
+ })
+
+ categorySection.appendChild(shortcutElement)
+ })
+
+ helpModal.appendChild(categorySection)
+ })
+
+ // Add close button
+ const closeButton = document.createElement("button")
+ closeButton.textContent = "×"
+ closeButton.style.cssText = `
+ position: absolute;
+ top: 10px;
+ right: 10px;
+ background: none;
+ border: none;
+ color: white;
+ font-size: 24px;
+ cursor: pointer;
+ padding: 5px;
+ `
+ closeButton.addEventListener("click", () => {
+ helpModal.style.display = "none"
+ })
+ helpModal.appendChild(closeButton)
+
+ // Add to document body instead of container
+ document.body.appendChild(helpModal)
+ }
+
+ keyboardShortcuts = `key command category
+ d toggleDetectionCommand Detection
+ t drawRandomBoxesCommand Detection
+ h flipHorizontalCommand Camera
+ v flipVerticalCommand Camera
+ c takeSnapshotCommand Camera
+ z changeZoomLevelDisplayCommand Overlay
+ ? toggleHelpCommand Overlay`
+
+ setupKeyboardShortcuts(container) {
+ // Parse the keyboard shortcuts string into a map
+ const shortcuts = new Map(
+ this.keyboardShortcuts
+ .split("\n")
+ .slice(1) // Skip header row
+ .map((line) => {
+ const [key, command] = line.split(" ")
+ return [key, command]
+ }),
+ )
+
+ // Add keyboard event listener
+ document.addEventListener("keydown", (e) => {
+ // Ignore if user is typing in an input field
+ if (e.target.tagName.toLowerCase() === "input") return
+
+ const command = shortcuts.get(e.key.toLowerCase())
+ if (command && typeof this[command] === "function") {
+ e.preventDefault()
+ this[command](container)
+ }
+ })
+ }
+
+ toggleDetectionCommand(container) {
+ this.isDetecting = !this.isDetecting
+ if (this.isDetecting) {
+ this.startDetection(container)
+ console.log("Detection enabled")
+ } else {
+ // Clear all boxes when detection is disabled
+ const data = this.instances.get(container)
+ data.boxes = []
+ this.clearCanvas(container)
+
+ // Update cell counter
+ const hud = container.querySelector(".scrollWebcamHud")
+ const cellCounter = hud.querySelector(
+ '.scrollWebcamHudElement[data-type="cellCount"]',
+ )
+ if (cellCounter) {
+ cellCounter.textContent = "Cells Counted: 0"
+ }
+
+ console.log("Detection disabled")
+ }
+ }
+
+ flipHorizontalCommand(container) {
+ const data = this.instances.get(container)
+ const { video } = data
+
+ // Toggle the transform style
+ const currentTransform = video.style.transform || ""
+ if (currentTransform.includes("scaleX(-1)")) {
+ video.style.transform = currentTransform.replace("scaleX(-1)", "")
+ } else {
+ video.style.transform = `${currentTransform} scaleX(-1)`
+ }
+
+ // Also flip the canvas to match
+ const { canvas } = data
+ if (currentTransform.includes("scaleX(-1)")) {
+ canvas.style.transform = currentTransform.replace("scaleX(-1)", "")
+ } else {
+ canvas.style.transform = `${currentTransform} scaleX(-1)`
+ }
+ }
+
+ flipVerticalCommand(container) {
+ const data = this.instances.get(container)
+ const { video } = data
+
+ // Toggle the transform style
+ const currentTransform = video.style.transform || ""
+ if (currentTransform.includes("scaleY(-1)")) {
+ video.style.transform = currentTransform.replace("scaleY(-1)", "")
+ } else {
+ video.style.transform = `${currentTransform} scaleY(-1)`
+ }
+
+ // Also flip the canvas to match
+ const { canvas } = data
+ if (currentTransform.includes("scaleY(-1)")) {
+ canvas.style.transform = currentTransform.replace("scaleY(-1)", "")
+ } else {
+ canvas.style.transform = `${currentTransform} scaleY(-1)`
+ }
+ }
+
+ takeSnapshotCommand(container) {
+ const data = this.instances.get(container)
+ const { video, canvas } = data
+
+ // Create a temporary canvas to capture the full state
+ const tempCanvas = document.createElement("canvas")
+ tempCanvas.width = video.videoWidth
+ tempCanvas.height = video.videoHeight
+ const tempCtx = tempCanvas.getContext("2d")
+
+ // 1. Draw the video frame
+ tempCtx.drawImage(video, 0, 0)
+
+ // 2. Draw the detection boxes from the canvas
+ tempCtx.drawImage(canvas, 0, 0)
+
+ // 3. Draw HUD elements
+ const hud = container.querySelector(".scrollWebcamHud")
+ if (hud) {
+ // Get all HUD elements
+ const hudElements = hud.querySelectorAll(".scrollWebcamHudElement")
+
+ hudElements.forEach((element) => {
+ // Get computed styles
+ const style = window.getComputedStyle(element)
+ const rect = element.getBoundingClientRect()
+ const containerRect = container.getBoundingClientRect()
+
+ // Calculate relative position within container
+ const x = rect.left - containerRect.left
+ const y = rect.top - containerRect.top
+
+ // Set up text styling
+ tempCtx.font = style.font || "30px Arial"
+ tempCtx.fillStyle = style.color || "white"
+ tempCtx.textBaseline = "top"
+
+ // Apply text shadow if present
+ if (style.textShadow && style.textShadow !== "none") {
+ tempCtx.shadowColor = "black"
+ tempCtx.shadowBlur = 2
+ tempCtx.shadowOffsetX = 1
+ tempCtx.shadowOffsetY = 1
+ }
+
+ // Draw the text
+ tempCtx.fillText(
+ element.textContent,
+ (x / containerRect.width) * video.videoWidth,
+ (y / containerRect.height) * video.videoHeight,
+ )
+
+ // Reset shadow
+ tempCtx.shadowColor = "transparent"
+ tempCtx.shadowBlur = 0
+ tempCtx.shadowOffsetX = 0
+ tempCtx.shadowOffsetY = 0
+ })
+ }
+
+ // 4. Draw zoom level indicator
+ const zoomDisplay = container.querySelector(".top-left")
+ if (zoomDisplay) {
+ const style = window.getComputedStyle(zoomDisplay)
+ tempCtx.font = style.font || "16px Arial"
+ tempCtx.fillStyle = style.color || "white"
+ tempCtx.textBaseline = "top"
+ tempCtx.fillText(zoomDisplay.textContent, 10, 10)
+ }
+
+ // Create thumbnail container if it doesn't exist
+ let thumbContainer = container.querySelector(".snapshot-thumbnails")
+ if (!thumbContainer) {
+ thumbContainer = document.createElement("div")
+ thumbContainer.className = "snapshot-thumbnails"
+ thumbContainer.style.position = "fixed"
+ thumbContainer.style.left = "10px"
+ thumbContainer.style.top = "10px"
+ thumbContainer.style.width = "120px"
+ thumbContainer.style.zIndex = "1000"
+ thumbContainer.style.maxHeight = "100vh"
+ thumbContainer.style.overflowY = "auto"
+ thumbContainer.style.padding = "10px"
+ thumbContainer.style.background = "rgba(0, 0, 0, 0.5)"
+ thumbContainer.style.borderRadius = "5px"
+ container.appendChild(thumbContainer)
+ }
+
+ // Create and add thumbnail
+ const thumbnail = document.createElement("div")
+ thumbnail.style.marginBottom = "10px"
+ thumbnail.style.cursor = "pointer"
+ thumbnail.style.position = "relative"
+
+ // Add timestamp to thumbnail
+ const timestamp = document.createElement("div")
+ timestamp.style.position = "absolute"
+ timestamp.style.bottom = "0"
+ timestamp.style.left = "0"
+ timestamp.style.right = "0"
+ timestamp.style.background = "rgba(0,0,0,0.5)"
+ timestamp.style.color = "white"
+ timestamp.style.fontSize = "10px"
+ timestamp.style.padding = "2px"
+ timestamp.textContent = new Date().toLocaleTimeString()
+
+ const img = document.createElement("img")
+ img.src = tempCanvas.toDataURL("image/png")
+ img.style.width = "100%"
+ img.style.border = "2px solid white"
+
+ thumbnail.appendChild(img)
+ thumbnail.appendChild(timestamp)
+ thumbContainer.appendChild(thumbnail)
+
+ // Add click handler to open full size in new window
+ thumbnail.addEventListener("click", () => {
+ const win = window.open()
+ win.document.write(`
+
+
+ Snapshot - ${new Date().toLocaleString()}
+
+
+
+ Snapshot
+
+
+ `)
+ })
+ }
+
+ changeZoomLevelDisplayCommand(container) {
+ // Get next zoom level
+ const currentIndex = this.zoomLevels.indexOf(this.currentZoomLevel)
+ const nextIndex = (currentIndex + 1) % this.zoomLevels.length
+ this.currentZoomLevel = this.zoomLevels[nextIndex]
+
+ // Update or create zoom display element
+ let zoomDisplay = container.querySelector(".top-left")
+ // Update display text
+ zoomDisplay.textContent = `${this.microscopeName} ${this.currentZoomLevel}`
+ }
+
+ microscopeName = "Swift SW200DL"
+ zoomLevels = ["40x", "100x", "400x"]
+ currentZoomLevel = "100x"
+
Changed around line 714: class MitoSiteApp {
- setupKeyboardShortcuts(container) {
- document.addEventListener("keydown", (e) => {
- const data = this.instances.get(container)
-
- if (e.key === "Escape") {
- data.isDrawing = false
- this.redrawBoxes(container)
- } else if (e.key === "z" && e.ctrlKey) {
- data.boxes.pop()
- this.redrawBoxes(container)
- } else if (e.key === "Delete") {
- this.clearAllBoxes(container)
- } else if (e.key.toLowerCase() === "r") {
- // Toggle automatic box drawing
- if (data.autoDrawInterval) {
- clearInterval(data.autoDrawInterval)
- data.autoDrawInterval = null
- console.log("Stopped auto drawing")
+ drawRandomBoxesCommand(container) {
+ const data = this.instances.get(container)
+ // Toggle automatic box drawing
+ if (data.autoDrawInterval) {
+ clearInterval(data.autoDrawInterval)
+ data.autoDrawInterval = null
+ console.log("Stopped auto drawing")
+ } else {
+ console.log("Started auto drawing")
+ let max = 253
+ data.autoDrawInterval = setInterval(() => {
+ if (max) {
+ this.drawRandomBox(container)
+ max--
- console.log("Started auto drawing")
- let max = 253
- data.autoDrawInterval = setInterval(() => {
- if (max) {
- this.drawRandomBox(container)
- max--
- } else {
- if (Math.random() > 0.92) this.drawRandomBox(container)
- }
- }, 25) // Draw every 500ms
+ if (Math.random() > 0.92) this.drawRandomBox(container)
- }
- })
+ }, 25) // Draw every 500ms
+ }
ffff:207.243.92.34
ffff:207.243.92.34
13 days ago
updated index.scroll
index.scroll
Changed around line 3: buildHtml
+ editButton /edit.html
ffff:207.243.92.34
ffff:207.243.92.34
13 days ago
updated index.scroll
index.scroll
Changed around line 1
+
+ https://hub.powerhouse.wiki/powerhouse.parsers
+
+ powerhouseButton
+
root
root
13 days ago
Deleted powerhouse.parsers
powerhouse.parsers