|
|
<!DOCTYPE html> |
|
|
<html lang="en"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>hf_kernels_rotary</title> |
|
|
|
|
|
<link rel="preconnect" href="https://fonts.googleapis.com"> |
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> |
|
|
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&display=swap" rel="stylesheet"> |
|
|
|
|
|
<script> |
|
|
|
|
|
(function() { |
|
|
const isIframe = window.self !== window.top; |
|
|
if (!isIframe) return; |
|
|
|
|
|
|
|
|
const hash = window.location.hash; |
|
|
if (hash && hash.startsWith('#/')) { |
|
|
const targetPath = hash.slice(2); |
|
|
const currentPath = window.location.pathname.split('/').pop(); |
|
|
|
|
|
|
|
|
if (targetPath !== currentPath) { |
|
|
window.location.href = targetPath; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
document.addEventListener('click', function(e) { |
|
|
const link = e.target.closest('a'); |
|
|
if (!link) return; |
|
|
|
|
|
const href = link.getAttribute('href'); |
|
|
|
|
|
|
|
|
if (!href || href.startsWith('#') || href.startsWith('http') || href.startsWith('javascript:')) { |
|
|
return; |
|
|
} |
|
|
|
|
|
e.preventDefault(); |
|
|
|
|
|
|
|
|
const url = new URL(href, window.location.href); |
|
|
let fullPath = url.pathname; |
|
|
|
|
|
|
|
|
if (fullPath.startsWith('/')) { |
|
|
fullPath = fullPath.slice(1); |
|
|
} |
|
|
|
|
|
|
|
|
window.location.hash = '#/' + fullPath; |
|
|
|
|
|
|
|
|
if (fullPath.endsWith('.html') || fullPath.endsWith('/')) { |
|
|
const pathParts = fullPath.split('/').filter(p => p); |
|
|
const targetFile = pathParts[pathParts.length - 1] || 'index.html'; |
|
|
window.location.href = targetFile; |
|
|
} else { |
|
|
|
|
|
window.open(href, '_blank'); |
|
|
} |
|
|
}); |
|
|
})(); |
|
|
|
|
|
|
|
|
(function() { |
|
|
const configTheme = 'dark'; |
|
|
const hasConfigUi = false; |
|
|
const configUi = hasConfigUi ? 'None' : null; |
|
|
const hasWidgetsConfig = false; |
|
|
const widgetsOn = hasWidgetsConfig ? false : true; |
|
|
let theme; |
|
|
if (configTheme === 'auto') { |
|
|
theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; |
|
|
} else { |
|
|
theme = localStorage.getItem('uvnote-theme') || configTheme; |
|
|
} |
|
|
document.documentElement.setAttribute('data-theme', theme); |
|
|
|
|
|
|
|
|
let ui = hasConfigUi ? configUi : (localStorage.getItem('uvnote-ui') || 'default'); |
|
|
if (ui !== 'default' && ui !== 'none' && ui !== 'monocolor') { ui = 'default'; } |
|
|
document.documentElement.setAttribute('data-ui', ui); |
|
|
|
|
|
|
|
|
document.documentElement.setAttribute('data-widgets', widgetsOn ? 'on' : 'off'); |
|
|
})(); |
|
|
</script> |
|
|
<style> |
|
|
:root[data-theme="light"] { |
|
|
--bg-primary: #ffffff; |
|
|
--bg-secondary: #f6f8fa; |
|
|
--bg-tertiary: #f8f9fa; |
|
|
--bg-code: #f8f9fa; |
|
|
--bg-error: #fdf2f2; |
|
|
--bg-artifact: #e6f3ff; |
|
|
--bg-artifact-hover: #d0e7ff; |
|
|
|
|
|
--text-primary: #333; |
|
|
--text-secondary: #656d76; |
|
|
--text-error: #c53030; |
|
|
--text-link: #0969da; |
|
|
|
|
|
--border-primary: #e1e5e9; |
|
|
--border-error: #e53e3e; |
|
|
--border-cell-failed: #d73a49; |
|
|
|
|
|
--shadow: rgba(0, 0, 0, 0.1); |
|
|
} |
|
|
|
|
|
:root[data-theme="dark"] { |
|
|
--bg-primary: #0a0a0a; |
|
|
--bg-secondary: #121212; |
|
|
--bg-tertiary: #181818; |
|
|
--bg-code: #0d0d0d; |
|
|
--bg-error: #1a0f0f; |
|
|
--bg-artifact: #151515; |
|
|
--bg-artifact-hover: #1a1a1a; |
|
|
|
|
|
--text-primary: #e0e0e0; |
|
|
--text-secondary: #888888; |
|
|
--text-error: #ff6b6b; |
|
|
--text-link: #64b5f6; |
|
|
|
|
|
--border-primary: #2a2a2a; |
|
|
--border-error: #ff6b6b; |
|
|
--border-cell-failed: #ff6b6b; |
|
|
|
|
|
--shadow: rgba(255, 255, 255, 0.05); |
|
|
} |
|
|
|
|
|
|
|
|
:root[data-ui="monocolor"] { |
|
|
--mono-color: #0a66ff; |
|
|
} |
|
|
|
|
|
:root[data-ui="monocolor"][data-theme="light"] { |
|
|
--bg-primary: #ffffff; |
|
|
} |
|
|
|
|
|
:root[data-ui="monocolor"][data-theme="dark"] { |
|
|
--bg-primary: #000000; |
|
|
} |
|
|
|
|
|
:root[data-ui="monocolor"] { |
|
|
--bg-secondary: var(--bg-primary); |
|
|
--bg-tertiary: var(--bg-primary); |
|
|
--bg-code: var(--bg-primary); |
|
|
--bg-error: var(--bg-primary); |
|
|
--bg-artifact: var(--bg-primary); |
|
|
--bg-artifact-hover: var(--bg-primary); |
|
|
|
|
|
--text-primary: var(--mono-color); |
|
|
--text-secondary: var(--mono-color); |
|
|
--text-error: var(--mono-color); |
|
|
--text-link: var(--mono-color); |
|
|
|
|
|
--border-primary: var(--mono-color); |
|
|
--border-error: var(--mono-color); |
|
|
--border-cell-failed: var(--mono-color); |
|
|
|
|
|
--shadow: none; |
|
|
} |
|
|
|
|
|
:root[data-ui="monocolor"] a { |
|
|
color: var(--mono-color); |
|
|
} |
|
|
|
|
|
:root[data-ui="monocolor"] .menu-button, |
|
|
:root[data-ui="monocolor"] .theme-toggle, |
|
|
:root[data-ui="monocolor"] .reset-toggle, |
|
|
:root[data-ui="monocolor"] .back-button { |
|
|
background: var(--bg-primary); |
|
|
color: var(--mono-color); |
|
|
border-color: var(--mono-color); |
|
|
} |
|
|
|
|
|
:root[data-ui="monocolor"] .menu-button:hover, |
|
|
:root[data-ui="monocolor"] .theme-toggle:hover, |
|
|
:root[data-ui="monocolor"] .reset-toggle:hover, |
|
|
:root[data-ui="monocolor"] .back-button:hover { |
|
|
background: var(--bg-primary); |
|
|
color: var(--mono-color); |
|
|
border-color: var(--mono-color); |
|
|
} |
|
|
|
|
|
:root[data-ui="monocolor"] .menu-dropdown { |
|
|
background: var(--bg-primary); |
|
|
border-color: var(--mono-color); |
|
|
box-shadow: none; |
|
|
} |
|
|
|
|
|
:root[data-ui="monocolor"] .menu-item { |
|
|
color: var(--mono-color); |
|
|
border-bottom-color: var(--mono-color); |
|
|
} |
|
|
|
|
|
:root[data-ui="monocolor"] .system-info { |
|
|
background: var(--bg-primary); |
|
|
border-color: var(--mono-color); |
|
|
} |
|
|
|
|
|
:root[data-ui="monocolor"] .cell { |
|
|
border-color: var(--mono-color); |
|
|
background: var(--bg-primary); |
|
|
} |
|
|
|
|
|
:root[data-ui="monocolor"] .cell-header { |
|
|
background: var(--bg-primary); |
|
|
border-bottom-color: var(--mono-color); |
|
|
} |
|
|
|
|
|
:root[data-ui="monocolor"] .artifact { |
|
|
background: var(--bg-primary); |
|
|
border-color: var(--mono-color); |
|
|
color: var(--mono-color); |
|
|
} |
|
|
|
|
|
:root[data-ui="monocolor"] .artifact:hover { |
|
|
background: var(--bg-primary); |
|
|
} |
|
|
|
|
|
:root[data-ui="monocolor"] .artifact-preview img, |
|
|
:root[data-ui="monocolor"] .artifact-preview svg { |
|
|
border-color: var(--mono-color); |
|
|
} |
|
|
|
|
|
:root[data-ui="monocolor"] .status-widget { |
|
|
background: var(--bg-primary); |
|
|
border-color: var(--mono-color); |
|
|
color: var(--mono-color); |
|
|
} |
|
|
|
|
|
:root[data-ui="monocolor"] .minimap, |
|
|
:root[data-ui="monocolor"] .file-explorer, |
|
|
:root[data-ui="monocolor"] .tools-widget { |
|
|
background: var(--bg-primary); |
|
|
border-color: var(--mono-color); |
|
|
color: var(--mono-color); |
|
|
} |
|
|
|
|
|
:root[data-ui="monocolor"] .cell-code { |
|
|
background: var(--bg-primary); |
|
|
border-bottom-color: var(--mono-color); |
|
|
} |
|
|
|
|
|
:root[data-ui="monocolor"] .tools-title, |
|
|
:root[data-ui="monocolor"] .file-explorer-section-title, |
|
|
:root[data-ui="monocolor"] .minimap-title { |
|
|
color: var(--mono-color); |
|
|
border-bottom-color: var(--mono-color); |
|
|
} |
|
|
|
|
|
:root[data-ui="monocolor"] .tool-button { |
|
|
background: var(--bg-primary); |
|
|
border-color: var(--mono-color); |
|
|
color: var(--mono-color); |
|
|
} |
|
|
|
|
|
:root[data-ui="monocolor"] .tool-button.active { |
|
|
border-color: var(--mono-color); |
|
|
} |
|
|
|
|
|
:root[data-ui="monocolor"] .file-explorer-item, |
|
|
:root[data-ui="monocolor"] .minimap-item { |
|
|
color: var(--mono-color); |
|
|
} |
|
|
|
|
|
|
|
|
:root[data-ui="monocolor"] .highlight { |
|
|
background: var(--bg-primary) !important; |
|
|
color: var(--mono-color) !important; |
|
|
} |
|
|
|
|
|
:root[data-ui="monocolor"] .highlight *, |
|
|
:root[data-ui="monocolor"] .highlight .hll { |
|
|
color: var(--mono-color) !important; |
|
|
background: transparent !important; |
|
|
border-color: var(--mono-color) !important; |
|
|
} |
|
|
|
|
|
|
|
|
:root { |
|
|
--code-font-size: 0.95rem; |
|
|
--code-line-height: 1.5; |
|
|
--code-pad-y: 0.75rem; |
|
|
} |
|
|
|
|
|
|
|
|
:root[data-ui="none"] { |
|
|
--bg-primary: #ffffff; |
|
|
--bg-secondary: transparent; |
|
|
--bg-tertiary: transparent; |
|
|
--bg-code: #f9f9f9; |
|
|
--bg-error: #fff0f0; |
|
|
--bg-artifact: #f0f7ff; |
|
|
--bg-artifact-hover: #e5f1ff; |
|
|
|
|
|
--text-primary: #000000; |
|
|
--text-secondary: #222222; |
|
|
--text-error: #a00000; |
|
|
--text-link: #0000ee; |
|
|
|
|
|
--border-primary: #cccccc; |
|
|
--border-error: #cc0000; |
|
|
--border-cell-failed: #cc0000; |
|
|
|
|
|
--shadow: none; |
|
|
} |
|
|
|
|
|
html { |
|
|
overscroll-behavior: none; |
|
|
} |
|
|
|
|
|
body { |
|
|
font-family: 'Cascadia Mono', 'Cascadia Code', 'JetBrains Mono', 'SF Mono', Monaco, 'Consolas', monospace; |
|
|
line-height: 1.4; |
|
|
max-width: 1000px; |
|
|
margin: 0 auto; |
|
|
padding: 15px; |
|
|
color: var(--text-primary); |
|
|
background: var(--bg-primary); |
|
|
transition: background-color 0.2s ease, color 0.2s ease; |
|
|
overscroll-behavior: none; |
|
|
} |
|
|
|
|
|
|
|
|
:root[data-ui="none"] body { |
|
|
font-family: 'Times New Roman', Times, serif; |
|
|
line-height: 1.5; |
|
|
max-width: 860px; |
|
|
padding: 12px; |
|
|
background: #ffffff; |
|
|
color: #000000; |
|
|
transition: none; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.controls { |
|
|
position: fixed; |
|
|
top: 20px; |
|
|
right: 20px; |
|
|
display: flex; |
|
|
flex-direction: column; |
|
|
align-items: flex-end; |
|
|
gap: 0.25rem; |
|
|
z-index: 1000; |
|
|
} |
|
|
|
|
|
.controls-buttons { |
|
|
display: flex; |
|
|
gap: 0.5rem; |
|
|
} |
|
|
|
|
|
.menu-button { |
|
|
position: relative; |
|
|
background: var(--bg-secondary); |
|
|
border: 1px solid var(--border-primary); |
|
|
padding: 8px 12px; |
|
|
border-radius: 2px; |
|
|
color: var(--text-secondary); |
|
|
cursor: pointer; |
|
|
font-family: inherit; |
|
|
font-size: 0.9rem; |
|
|
user-select: none; |
|
|
} |
|
|
|
|
|
|
|
|
:root[data-ui="none"][data-widgets="on"] .menu-button, |
|
|
:root[data-ui="none"][data-widgets="on"] .theme-toggle, |
|
|
:root[data-ui="none"][data-widgets="on"] .reset-toggle, |
|
|
:root[data-ui="none"][data-widgets="on"] .back-button { |
|
|
background: #f6f6f6; |
|
|
border: 1px solid #cccccc; |
|
|
color: #222222; |
|
|
} |
|
|
|
|
|
.menu-button:hover { |
|
|
color: var(--text-primary); |
|
|
background: var(--bg-tertiary); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.status-widget { |
|
|
position: fixed; |
|
|
right: 20px; |
|
|
bottom: 20px; |
|
|
width: auto; |
|
|
max-width: 260px; |
|
|
background: var(--bg-secondary); |
|
|
border: 1px solid var(--border-primary); |
|
|
border-radius: 2px; |
|
|
padding: 6px 8px; |
|
|
font-size: 0.8rem; |
|
|
color: var(--text-secondary); |
|
|
z-index: 100; |
|
|
} |
|
|
|
|
|
.status-widget strong { |
|
|
color: var(--text-primary); |
|
|
} |
|
|
|
|
|
:root[data-ui="none"][data-widgets="on"] .status-widget { |
|
|
background: #f6f6f6; |
|
|
border-color: #ccc; |
|
|
color: #222; |
|
|
} |
|
|
|
|
|
:root[data-ui="none"][data-widgets="on"] .menu-button:hover, |
|
|
:root[data-ui="none"][data-widgets="on"] .theme-toggle:hover, |
|
|
:root[data-ui="none"][data-widgets="on"] .reset-toggle:hover, |
|
|
:root[data-ui="none"][data-widgets="on"] .back-button:hover { |
|
|
background: #ededed; |
|
|
border-color: #bbbbbb; |
|
|
color: #000000; |
|
|
} |
|
|
|
|
|
.menu-dropdown { |
|
|
position: absolute; |
|
|
top: 100%; |
|
|
right: 0; |
|
|
background: var(--bg-secondary); |
|
|
border: 1px solid var(--border-primary); |
|
|
border-radius: 4px; |
|
|
box-shadow: 0 4px 12px var(--shadow); |
|
|
min-width: 160px; |
|
|
opacity: 0; |
|
|
visibility: hidden; |
|
|
transform: translateY(-8px); |
|
|
transition: all 0.2s ease; |
|
|
z-index: 1001; |
|
|
margin-top: 4px; |
|
|
} |
|
|
|
|
|
:root[data-ui="none"][data-widgets="on"] .menu-dropdown { |
|
|
background: #ffffff; |
|
|
border: 1px solid #cccccc; |
|
|
box-shadow: none; |
|
|
} |
|
|
|
|
|
.menu-button.active .menu-dropdown { |
|
|
opacity: 1; |
|
|
visibility: visible; |
|
|
transform: translateY(0); |
|
|
} |
|
|
|
|
|
.menu-item { |
|
|
display: block; |
|
|
padding: 8px 12px; |
|
|
color: var(--text-secondary); |
|
|
text-decoration: none; |
|
|
font-size: 0.85rem; |
|
|
border-bottom: 1px solid var(--border-primary); |
|
|
cursor: pointer; |
|
|
} |
|
|
|
|
|
:root[data-ui="none"] .menu-item { |
|
|
color: #000; |
|
|
border-bottom: 1px solid #eee; |
|
|
} |
|
|
|
|
|
.menu-item:last-child { |
|
|
border-bottom: none; |
|
|
} |
|
|
|
|
|
.menu-item:hover { |
|
|
background: var(--bg-tertiary); |
|
|
color: var(--text-primary); |
|
|
} |
|
|
|
|
|
.menu-checkbox { |
|
|
display: inline-block; |
|
|
width: 16px; |
|
|
font-family: monospace; |
|
|
color: var(--text-link); |
|
|
} |
|
|
|
|
|
.theme-toggle, |
|
|
.reset-toggle, |
|
|
.back-button { |
|
|
background: var(--bg-secondary); |
|
|
border: 1px solid var(--border-primary); |
|
|
padding: 8px 12px; |
|
|
border-radius: 4px; |
|
|
color: var(--text-secondary); |
|
|
cursor: pointer; |
|
|
font-family: inherit; |
|
|
font-size: 0.9rem; |
|
|
user-select: none; |
|
|
} |
|
|
|
|
|
.back-button { |
|
|
text-decoration: none; |
|
|
display: inline-block; |
|
|
} |
|
|
|
|
|
.theme-toggle:hover, |
|
|
.reset-toggle:hover, |
|
|
.back-button:hover { |
|
|
color: var(--text-primary); |
|
|
background: var(--bg-tertiary); |
|
|
} |
|
|
|
|
|
.system-info { |
|
|
background: var(--bg-secondary); |
|
|
border: 1px solid var(--border-primary); |
|
|
border-radius: 4px; |
|
|
padding: 8px 12px; |
|
|
margin-bottom: 16px; |
|
|
font-size: 0.85em; |
|
|
color: var(--text-secondary); |
|
|
} |
|
|
|
|
|
.system-info-header { |
|
|
font-weight: 600; |
|
|
color: var(--text-primary); |
|
|
margin-bottom: 2px; |
|
|
} |
|
|
|
|
|
.system-info-content { |
|
|
font-family: monospace; |
|
|
} |
|
|
|
|
|
.theme-toggle, |
|
|
.reset-toggle { |
|
|
background: var(--bg-secondary); |
|
|
border: 1px solid var(--border-primary); |
|
|
border-radius: 2px; |
|
|
|
|
|
cursor: pointer; |
|
|
font-family: inherit; |
|
|
font-size: 0.8rem; |
|
|
color: var(--text-secondary); |
|
|
user-select: none; |
|
|
transition: all 0.2s ease; |
|
|
text-transform: lowercase; |
|
|
letter-spacing: 0; |
|
|
} |
|
|
|
|
|
.theme-toggle:hover, |
|
|
.reset-toggle:hover { |
|
|
background: var(--bg-tertiary); |
|
|
border-color: var(--text-secondary); |
|
|
color: var(--text-primary); |
|
|
} |
|
|
|
|
|
.minimap { |
|
|
position: fixed; |
|
|
bottom: 20px; |
|
|
right: 20px; |
|
|
width: 220px; |
|
|
max-height: 400px; |
|
|
background: var(--bg-secondary); |
|
|
border: 1px solid var(--border-primary); |
|
|
border-radius: 2px; |
|
|
padding: 0.5rem; |
|
|
font-size: 0.7rem; |
|
|
overflow-y: auto; |
|
|
z-index: 100; |
|
|
opacity: 0.9; |
|
|
transition: opacity 0.2s ease; |
|
|
} |
|
|
|
|
|
|
|
|
:root[data-widgets="off"] .controls, |
|
|
:root[data-widgets="off"] .minimap, |
|
|
:root[data-widgets="off"] .file-explorer, |
|
|
:root[data-widgets="off"] .tools-widget, |
|
|
:root[data-widgets="off"] .status-widget { |
|
|
display: none !important; |
|
|
} |
|
|
|
|
|
.file-explorer { |
|
|
position: fixed; |
|
|
bottom: 20px; |
|
|
|
|
|
right: 20px; |
|
|
left: auto; |
|
|
top: auto; |
|
|
width: 220px; |
|
|
max-height: 400px; |
|
|
background: var(--bg-secondary); |
|
|
border: 1px solid var(--border-primary); |
|
|
border-radius: 2px; |
|
|
padding: 0.5rem; |
|
|
font-size: 0.7rem; |
|
|
overflow-y: auto; |
|
|
z-index: 100; |
|
|
opacity: 0.9; |
|
|
transition: opacity 0.2s ease; |
|
|
} |
|
|
|
|
|
|
|
|
.draw-overlay { |
|
|
position: fixed; |
|
|
top: 0; |
|
|
left: 0; |
|
|
width: 100vw; |
|
|
height: 100vh; |
|
|
z-index: 80; |
|
|
|
|
|
display: block; |
|
|
pointer-events: none; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
.tools-widget { |
|
|
position: fixed; |
|
|
bottom: 20px; |
|
|
|
|
|
right: 20px; |
|
|
left: auto; |
|
|
top: auto; |
|
|
width: 220px; |
|
|
background: var(--bg-secondary); |
|
|
border: 1px solid var(--border-primary); |
|
|
border-radius: 2px; |
|
|
padding: 0.5rem; |
|
|
font-size: 0.7rem; |
|
|
z-index: 100; |
|
|
opacity: 0.95; |
|
|
} |
|
|
|
|
|
.tools-title { |
|
|
font-weight: bold; |
|
|
color: var(--text-secondary); |
|
|
margin-bottom: 0.5rem; |
|
|
padding-bottom: 0.25rem; |
|
|
border-bottom: 1px solid var(--border-primary); |
|
|
cursor: grab; |
|
|
user-select: none; |
|
|
} |
|
|
|
|
|
.tools-row { |
|
|
display: flex; |
|
|
gap: 0.4rem; |
|
|
flex-wrap: wrap; |
|
|
} |
|
|
|
|
|
.tool-button { |
|
|
background: var(--bg-tertiary); |
|
|
border: 1px solid var(--border-primary); |
|
|
border-radius: 2px; |
|
|
padding: 0.25rem 0.4rem; |
|
|
cursor: pointer; |
|
|
color: var(--text-secondary); |
|
|
font-family: inherit; |
|
|
font-size: 0.75rem; |
|
|
user-select: none; |
|
|
} |
|
|
|
|
|
.tool-button:hover { |
|
|
color: var(--text-primary); |
|
|
} |
|
|
|
|
|
.tool-button.active { |
|
|
color: var(--text-primary); |
|
|
border-color: var(--text-secondary); |
|
|
background: var(--bg-secondary); |
|
|
} |
|
|
|
|
|
.minimap:hover, |
|
|
.file-explorer:hover { |
|
|
opacity: 1; |
|
|
} |
|
|
|
|
|
.minimap-title { |
|
|
font-weight: bold; |
|
|
color: var(--text-secondary); |
|
|
margin-bottom: 0.5rem; |
|
|
padding-bottom: 0.25rem; |
|
|
border-bottom: 1px solid var(--border-primary); |
|
|
cursor: grab; |
|
|
|
|
|
user-select: none; |
|
|
} |
|
|
|
|
|
.minimap-item { |
|
|
display: block; |
|
|
color: var(--text-secondary); |
|
|
text-decoration: none; |
|
|
padding: 0.15rem 0; |
|
|
border-left: 2px solid transparent; |
|
|
padding-left: 0.5rem; |
|
|
transition: all 0.2s ease; |
|
|
cursor: pointer; |
|
|
} |
|
|
|
|
|
.minimap-item:hover { |
|
|
color: var(--text-primary); |
|
|
border-left-color: var(--text-secondary); |
|
|
} |
|
|
|
|
|
.minimap-item.active { |
|
|
color: var(--text-primary); |
|
|
border-left-color: var(--text-link); |
|
|
} |
|
|
|
|
|
.minimap-heading { |
|
|
font-weight: normal; |
|
|
} |
|
|
|
|
|
.minimap-heading.h1 { |
|
|
padding-left: 0.5rem; |
|
|
} |
|
|
|
|
|
.minimap-heading.h2 { |
|
|
padding-left: 1rem; |
|
|
} |
|
|
|
|
|
.minimap-heading.h3 { |
|
|
padding-left: 1.5rem; |
|
|
} |
|
|
|
|
|
.minimap-heading.h4 { |
|
|
padding-left: 2rem; |
|
|
} |
|
|
|
|
|
.minimap-heading.h5 { |
|
|
padding-left: 2.5rem; |
|
|
} |
|
|
|
|
|
.minimap-heading.h6 { |
|
|
padding-left: 3rem; |
|
|
} |
|
|
|
|
|
.minimap-cell { |
|
|
color: var(--text-link); |
|
|
opacity: 0.8; |
|
|
font-style: italic; |
|
|
} |
|
|
|
|
|
.minimap-cell:hover { |
|
|
opacity: 1; |
|
|
} |
|
|
|
|
|
.file-explorer-title { |
|
|
font-weight: bold; |
|
|
color: var(--text-secondary); |
|
|
margin-bottom: 0.5rem; |
|
|
padding-bottom: 0.25rem; |
|
|
border-bottom: 1px solid var(--border-primary); |
|
|
cursor: grab; |
|
|
|
|
|
user-select: none; |
|
|
} |
|
|
|
|
|
.file-explorer-section { |
|
|
margin-bottom: 0.75rem; |
|
|
} |
|
|
|
|
|
.file-explorer-section-title { |
|
|
font-weight: bold; |
|
|
color: var(--text-secondary); |
|
|
font-size: 0.65rem; |
|
|
margin-bottom: 0.25rem; |
|
|
text-transform: uppercase; |
|
|
letter-spacing: 0.5px; |
|
|
} |
|
|
|
|
|
.file-explorer-item { |
|
|
display: block; |
|
|
color: var(--text-secondary); |
|
|
text-decoration: none; |
|
|
padding: 0.1rem 0; |
|
|
margin-left: 0.5rem; |
|
|
transition: color 0.2s ease; |
|
|
cursor: pointer; |
|
|
font-family: monospace; |
|
|
} |
|
|
|
|
|
.file-explorer-item:hover { |
|
|
color: var(--text-primary); |
|
|
} |
|
|
|
|
|
.file-explorer-item.script { |
|
|
color: var(--text-link); |
|
|
} |
|
|
|
|
|
.file-explorer-item.artifact { |
|
|
color: var(--text-secondary); |
|
|
opacity: 0.8; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@media (max-width: 768px) { |
|
|
|
|
|
.minimap, |
|
|
.file-explorer, |
|
|
.tools-widget { |
|
|
display: none; |
|
|
} |
|
|
} |
|
|
|
|
|
.cell { |
|
|
margin: 1rem 0; |
|
|
border: 1px solid var(--border-primary); |
|
|
border-radius: 2px; |
|
|
overflow: hidden; |
|
|
background: var(--bg-secondary); |
|
|
} |
|
|
|
|
|
:root[data-ui="none"] .cell { |
|
|
margin: 1em 0; |
|
|
border: none; |
|
|
background: transparent; |
|
|
} |
|
|
|
|
|
.cell-header { |
|
|
background: var(--bg-secondary); |
|
|
padding: 0.5rem 1rem; |
|
|
border-bottom: 1px solid var(--border-primary); |
|
|
font-family: inherit; |
|
|
font-size: 0.85rem; |
|
|
} |
|
|
|
|
|
:root[data-ui="none"] .cell-header { |
|
|
background: transparent; |
|
|
border: none; |
|
|
padding: 0; |
|
|
font-weight: bold; |
|
|
} |
|
|
|
|
|
:root[data-ui="none"] .cell-content { |
|
|
padding: 0; |
|
|
} |
|
|
|
|
|
:root[data-ui="none"] .copy-button, |
|
|
:root[data-ui="none"] .collapse-indicators, |
|
|
:root[data-ui="none"] .cell-meta, |
|
|
:root[data-ui="none"] .cell-outputs-header { |
|
|
display: none !important; |
|
|
} |
|
|
|
|
|
:root[data-ui="none"] pre, |
|
|
:root[data-ui="none"] code { |
|
|
font-family: Menlo, Monaco, 'Courier New', monospace; |
|
|
} |
|
|
|
|
|
:root[data-ui="none"] .code-content pre { |
|
|
background: #f9f9f9; |
|
|
border: 1px solid #ddd; |
|
|
padding: 8px; |
|
|
} |
|
|
|
|
|
:root[data-ui="none"] .output { |
|
|
background: transparent; |
|
|
border: none; |
|
|
padding: 0.25em 0; |
|
|
} |
|
|
|
|
|
color: var(--text-secondary); |
|
|
cursor: pointer; |
|
|
user-select: none; |
|
|
transition: background-color 0.2s ease; |
|
|
} |
|
|
|
|
|
.cell-header:hover { |
|
|
background: var(--bg-tertiary); |
|
|
} |
|
|
|
|
|
.collapse-indicators { |
|
|
color: var(--text-secondary); |
|
|
font-size: 0.8rem; |
|
|
opacity: 0.7; |
|
|
} |
|
|
|
|
|
.collapse-indicators span:hover { |
|
|
color: var(--text-primary); |
|
|
opacity: 1; |
|
|
} |
|
|
|
|
|
.cell-code { |
|
|
display: block; |
|
|
background: var(--bg-code); |
|
|
} |
|
|
|
|
|
.cell-code.collapsed { |
|
|
display: none; |
|
|
} |
|
|
|
|
|
.cell-code pre { |
|
|
margin: 0; |
|
|
padding: 0.75rem; |
|
|
background: var(--bg-code); |
|
|
overflow-x: auto; |
|
|
color: var(--text-primary); |
|
|
} |
|
|
|
|
|
.cell-output { |
|
|
padding: 0.75rem; |
|
|
|
|
|
background: var(--bg-secondary); |
|
|
} |
|
|
|
|
|
.cell-output.collapsed { |
|
|
display: none; |
|
|
} |
|
|
|
|
|
.cell-stdout { |
|
|
background: var(--bg-tertiary); |
|
|
padding: 0.75rem; |
|
|
border-radius: 1px; |
|
|
|
|
|
font-family: inherit; |
|
|
font-size: 0.9rem; |
|
|
white-space: pre-wrap; |
|
|
color: var(--text-primary); |
|
|
} |
|
|
|
|
|
.cell-stdout { |
|
|
background: var(--bg-tertiary); |
|
|
padding: 0.75rem; |
|
|
border-radius: 1px; |
|
|
font-family: inherit; |
|
|
font-size: 0.9rem; |
|
|
color: var(--text-primary); |
|
|
|
|
|
|
|
|
overflow: auto; |
|
|
|
|
|
max-width: 100%; |
|
|
|
|
|
} |
|
|
|
|
|
.cell-stdout .stdout-text { |
|
|
margin: 0; |
|
|
|
|
|
white-space: pre; |
|
|
|
|
|
display: inline-block; |
|
|
|
|
|
min-width: max-content; |
|
|
|
|
|
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; |
|
|
tab-size: 2; |
|
|
} |
|
|
|
|
|
.cell-stderr { |
|
|
background: var(--bg-error); |
|
|
border-left: 2px solid var(--border-error); |
|
|
padding: 1rem; |
|
|
margin: 0.5rem 0; |
|
|
font-family: inherit; |
|
|
font-size: 0.9rem; |
|
|
color: var(--text-error); |
|
|
white-space: pre-wrap; |
|
|
} |
|
|
|
|
|
.uv-install-logs { |
|
|
margin: 0.5rem 0; |
|
|
} |
|
|
|
|
|
.uv-logs-header { |
|
|
cursor: pointer; |
|
|
padding: 0.75rem; |
|
|
border-left: 3px solid var(--border-color); |
|
|
font-family: inherit; |
|
|
font-size: 0.85rem; |
|
|
color: var(--text-secondary); |
|
|
user-select: none; |
|
|
} |
|
|
|
|
|
.uv-logs-content { |
|
|
background: var(--bg-secondary); |
|
|
padding: 1rem; |
|
|
border-left: 3px solid var(--border-color); |
|
|
white-space: pre-wrap; |
|
|
font-family: monospace; |
|
|
font-size: 0.85rem; |
|
|
color: var(--text-secondary); |
|
|
overflow-x: auto; |
|
|
} |
|
|
|
|
|
.cell-artifacts { |
|
|
margin: 1rem 0; |
|
|
} |
|
|
|
|
|
.cell-artifacts h4 { |
|
|
margin: 0 0 0.5rem 0; |
|
|
color: var(--text-secondary); |
|
|
font-size: 0.9rem; |
|
|
} |
|
|
|
|
|
.artifact { |
|
|
display: inline-block; |
|
|
background: var(--bg-artifact); |
|
|
padding: 0.25rem 0.5rem; |
|
|
border-radius: 1px; |
|
|
margin: 0.25rem 0.5rem 0.25rem 0; |
|
|
font-family: inherit; |
|
|
font-size: 0.8rem; |
|
|
color: var(--text-link); |
|
|
text-decoration: none; |
|
|
transition: background-color 0.2s ease; |
|
|
border: 1px solid var(--border-primary); |
|
|
} |
|
|
|
|
|
.artifact:hover { |
|
|
background: var(--bg-artifact-hover); |
|
|
} |
|
|
|
|
|
.artifact-preview { |
|
|
margin-top: 1rem; |
|
|
} |
|
|
|
|
|
.artifact-preview img { |
|
|
max-width: 100%; |
|
|
height: auto; |
|
|
border: 1px solid var(--border-primary); |
|
|
border-radius: 1px; |
|
|
} |
|
|
|
|
|
.artifact-preview svg { |
|
|
max-width: 100%; |
|
|
height: auto; |
|
|
border: 1px solid var(--border-primary); |
|
|
border-radius: 1px; |
|
|
display: block; |
|
|
} |
|
|
|
|
|
|
|
|
.artifact-preview svg g { |
|
|
fill: var(--text-primary) !important; |
|
|
} |
|
|
|
|
|
|
|
|
.artifact-preview svg { |
|
|
background: transparent; |
|
|
} |
|
|
|
|
|
|
|
|
:root[data-theme="dark"] .artifact-preview img[src$=".svg"] { |
|
|
filter: invert(0.9) hue-rotate(180deg); |
|
|
} |
|
|
|
|
|
|
|
|
:root[data-ui="monocolor"] .artifact-preview img[src$=".svg"] { |
|
|
filter: none; |
|
|
} |
|
|
|
|
|
|
|
|
.artifact-csv { |
|
|
margin-top: 1rem; |
|
|
overflow-x: auto; |
|
|
} |
|
|
|
|
|
.csv-table { |
|
|
width: 100%; |
|
|
border-collapse: collapse; |
|
|
font-size: 0.9rem; |
|
|
background: var(--bg-secondary); |
|
|
border: 1px solid var(--border-primary); |
|
|
border-radius: 1px; |
|
|
} |
|
|
|
|
|
.csv-table th, |
|
|
.csv-table td { |
|
|
padding: 0.5rem 0.75rem; |
|
|
text-align: left; |
|
|
border: 1px solid var(--border-primary); |
|
|
} |
|
|
|
|
|
.csv-table th { |
|
|
background: var(--bg-tertiary); |
|
|
font-weight: 600; |
|
|
color: var(--text-primary); |
|
|
} |
|
|
|
|
|
.csv-table tbody tr:hover { |
|
|
background: var(--bg-artifact-hover); |
|
|
} |
|
|
|
|
|
.artifact-csv-error { |
|
|
margin-top: 1rem; |
|
|
padding: 1rem; |
|
|
background: var(--bg-error); |
|
|
color: var(--text-error); |
|
|
border: 1px solid var(--border-error); |
|
|
border-radius: 1px; |
|
|
} |
|
|
|
|
|
.cell-failed { |
|
|
border-color: var(--border-cell-failed); |
|
|
} |
|
|
|
|
|
.cell-failed .cell-header { |
|
|
background: var(--bg-error); |
|
|
color: var(--text-error); |
|
|
} |
|
|
|
|
|
.cell-commented { |
|
|
opacity: 0.6; |
|
|
border-style: dashed; |
|
|
} |
|
|
|
|
|
.cell-commented .cell-header { |
|
|
background: var(--bg-secondary); |
|
|
color: var(--text-secondary); |
|
|
font-style: italic; |
|
|
} |
|
|
|
|
|
.run-btn { |
|
|
background: var(--bg-tertiary); |
|
|
border: 1px solid var(--border-primary); |
|
|
padding: 2px 6px; |
|
|
border-radius: 2px; |
|
|
color: var(--text-secondary); |
|
|
cursor: pointer; |
|
|
font-size: 0.75em; |
|
|
font-family: inherit; |
|
|
margin-left: 4px; |
|
|
} |
|
|
|
|
|
.run-btn:hover { |
|
|
color: var(--text-primary); |
|
|
background: var(--bg-primary); |
|
|
} |
|
|
|
|
|
.run-btn:disabled { |
|
|
opacity: 0.6; |
|
|
cursor: not-allowed; |
|
|
} |
|
|
|
|
|
.copy-btn { |
|
|
background: var(--bg-tertiary); |
|
|
border: 1px solid var(--border-primary); |
|
|
padding: 2px 6px; |
|
|
border-radius: 2px; |
|
|
color: var(--text-secondary); |
|
|
cursor: pointer; |
|
|
font-size: 0.75em; |
|
|
font-family: inherit; |
|
|
margin-left: 4px; |
|
|
} |
|
|
|
|
|
.copy-btn:hover { |
|
|
color: var(--text-primary); |
|
|
background: var(--bg-primary); |
|
|
} |
|
|
|
|
|
.copy-btn:disabled { |
|
|
opacity: 0.6; |
|
|
cursor: not-allowed; |
|
|
} |
|
|
|
|
|
.copy-btn.copied { |
|
|
color: #4caf50; |
|
|
background: var(--bg-primary); |
|
|
border-color: #4caf50; |
|
|
transition: all 0.2s ease; |
|
|
} |
|
|
|
|
|
.raw-btn { |
|
|
background: var(--bg-tertiary); |
|
|
border: 1px solid var(--border-primary); |
|
|
padding: 2px 6px; |
|
|
border-radius: 2px; |
|
|
color: var(--text-secondary); |
|
|
cursor: pointer; |
|
|
font-size: 0.75em; |
|
|
font-family: inherit; |
|
|
margin-left: 4px; |
|
|
text-decoration: none; |
|
|
display: inline-block; |
|
|
} |
|
|
|
|
|
.raw-btn:hover { |
|
|
color: var(--text-primary); |
|
|
background: var(--bg-primary); |
|
|
text-decoration: none; |
|
|
} |
|
|
|
|
|
.github-btn { |
|
|
background: var(--bg-tertiary); |
|
|
border: 1px solid var(--border-primary); |
|
|
padding: 2px 6px; |
|
|
border-radius: 2px; |
|
|
color: var(--text-secondary); |
|
|
cursor: pointer; |
|
|
font-size: 0.75em; |
|
|
font-family: inherit; |
|
|
margin-left: 4px; |
|
|
text-decoration: none; |
|
|
display: inline-block; |
|
|
} |
|
|
|
|
|
.github-btn:hover { |
|
|
color: var(--text-primary); |
|
|
background: var(--bg-primary); |
|
|
text-decoration: none; |
|
|
} |
|
|
|
|
|
.hf-btn { |
|
|
background: var(--bg-tertiary); |
|
|
border: 1px solid var(--border-primary); |
|
|
padding: 2px 6px; |
|
|
border-radius: 2px; |
|
|
color: var(--text-secondary); |
|
|
cursor: pointer; |
|
|
font-size: 0.75em; |
|
|
font-family: inherit; |
|
|
margin-left: 4px; |
|
|
text-decoration: none; |
|
|
display: inline-block; |
|
|
} |
|
|
|
|
|
.hf-btn:hover { |
|
|
color: var(--text-primary); |
|
|
background: var(--bg-primary); |
|
|
text-decoration: none; |
|
|
} |
|
|
|
|
|
.output-stale { |
|
|
opacity: 0.5; |
|
|
position: relative; |
|
|
} |
|
|
|
|
|
.output-stale::after { |
|
|
content: '⏳ updating...'; |
|
|
position: absolute; |
|
|
top: 8px; |
|
|
right: 8px; |
|
|
background: var(--bg-secondary); |
|
|
padding: 4px 8px; |
|
|
border-radius: 2px; |
|
|
font-size: 0.75em; |
|
|
color: var(--text-secondary); |
|
|
border: 1px solid var(--border-primary); |
|
|
} |
|
|
|
|
|
h1, |
|
|
h2, |
|
|
h3, |
|
|
h4, |
|
|
h5, |
|
|
h6 { |
|
|
margin-top: 1.5rem; |
|
|
margin-bottom: 0.75rem; |
|
|
color: var(--text-primary); |
|
|
} |
|
|
|
|
|
h1 { |
|
|
margin-top: 0; |
|
|
margin-bottom: 1rem; |
|
|
} |
|
|
|
|
|
p { |
|
|
margin: 0.75rem 0; |
|
|
color: var(--text-primary); |
|
|
} |
|
|
|
|
|
a { |
|
|
color: var(--text-link); |
|
|
} |
|
|
|
|
|
img { |
|
|
max-width: 100%; |
|
|
height: auto; |
|
|
border-radius: 1px; |
|
|
box-shadow: none; |
|
|
} |
|
|
|
|
|
pre, |
|
|
code { |
|
|
font-family: 'Cascadia Mono', 'Cascadia Code', 'JetBrains Mono', 'SF Mono', Monaco, 'Consolas', monospace; |
|
|
font-size: var(--code-font-size); |
|
|
} |
|
|
|
|
|
.code-wrap { |
|
|
position: relative; |
|
|
} |
|
|
|
|
|
.code-line-highlight { |
|
|
display: none; |
|
|
position: absolute; |
|
|
left: 0; |
|
|
right: 0; |
|
|
height: 1.5em; |
|
|
background: rgba(255, 235, 170, 0.35); |
|
|
pointer-events: none; |
|
|
border-left: 3px solid #f4c542; |
|
|
} |
|
|
|
|
|
.line-number { |
|
|
cursor: pointer; |
|
|
text-decoration: none; |
|
|
color: var(--text-secondary); |
|
|
padding: 0 0.25rem; |
|
|
} |
|
|
|
|
|
.line-number.selected { |
|
|
background: rgba(255, 235, 170, 0.4); |
|
|
color: var(--text-primary); |
|
|
} |
|
|
|
|
|
|
|
|
.highlight-with-lines { |
|
|
display: flex; |
|
|
} |
|
|
|
|
|
.line-numbers { |
|
|
background: var(--bg-tertiary); |
|
|
padding: var(--code-pad-y) 0.5rem; |
|
|
font-family: 'Cascadia Mono', 'Cascadia Code', 'JetBrains Mono', 'SF Mono', Monaco, 'Consolas', monospace; |
|
|
font-size: var(--code-font-size); |
|
|
line-height: var(--code-line-height); |
|
|
color: var(--text-secondary); |
|
|
user-select: none; |
|
|
text-align: right; |
|
|
border-right: 1px solid var(--border-primary); |
|
|
} |
|
|
|
|
|
.line-numbers .line-number { |
|
|
display: block; |
|
|
line-height: var(--code-line-height); |
|
|
} |
|
|
|
|
|
.highlight-with-lines .highlight { |
|
|
flex: 1; |
|
|
} |
|
|
|
|
|
.highlight .hll { |
|
|
background-color: transparent; |
|
|
} |
|
|
|
|
|
|
|
|
.highlight pre { |
|
|
white-space: pre; |
|
|
margin: 0; |
|
|
padding: var(--code-pad-y) 0.75rem; |
|
|
line-height: var(--code-line-height); |
|
|
} |
|
|
|
|
|
|
|
|
.cell-code.collapsed { |
|
|
display: none; |
|
|
} |
|
|
|
|
|
.cell-code.expanded { |
|
|
display: block; |
|
|
} |
|
|
|
|
|
{ |
|
|
% if config.collapse_code % |
|
|
} |
|
|
|
|
|
.cell-code { |
|
|
display: none; |
|
|
} |
|
|
|
|
|
{ |
|
|
% else % |
|
|
} |
|
|
|
|
|
.cell-code { |
|
|
display: block; |
|
|
border-bottom: 1px solid var(--border-primary); |
|
|
} |
|
|
|
|
|
{ |
|
|
% endif % |
|
|
} |
|
|
|
|
|
{ |
|
|
{ |
|
|
pygments_css |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.highlight pre { |
|
|
white-space: pre; |
|
|
margin: 0; |
|
|
padding: var(--code-pad-y) 0.75rem !important; |
|
|
line-height: var(--code-line-height) !important; |
|
|
font-size: var(--code-font-size) !important; |
|
|
font-family: 'Cascadia Mono', 'Cascadia Code', 'JetBrains Mono', 'SF Mono', Monaco, 'Consolas', monospace !important; |
|
|
border: none; |
|
|
} |
|
|
|
|
|
.line-numbers { |
|
|
line-height: var(--code-line-height) !important; |
|
|
} |
|
|
|
|
|
.line-numbers .line-number { |
|
|
line-height: var(--code-line-height) !important; |
|
|
} |
|
|
|
|
|
|
|
|
{ |
|
|
{ |
|
|
config.custom_css |
|
|
} |
|
|
} |
|
|
|
|
|
{ |
|
|
# Override code font size from frontmatter (accept number as px) # |
|
|
} |
|
|
|
|
|
{ |
|
|
% if config.code_font_size is not none % |
|
|
} |
|
|
|
|
|
{ |
|
|
% if config.code_font_size is string % |
|
|
} |
|
|
|
|
|
:root { |
|
|
--code-font-size: { |
|
|
{ |
|
|
config.code_font_size |
|
|
} |
|
|
} |
|
|
|
|
|
; |
|
|
} |
|
|
|
|
|
{ |
|
|
% else % |
|
|
} |
|
|
|
|
|
:root { |
|
|
--code-font-size: { |
|
|
{ |
|
|
config.code_font_size |
|
|
} |
|
|
} |
|
|
|
|
|
px; |
|
|
} |
|
|
|
|
|
{ |
|
|
% endif % |
|
|
} |
|
|
|
|
|
{ |
|
|
% endif % |
|
|
} |
|
|
|
|
|
|
|
|
body[data-tool="arrow"] .main-content { |
|
|
cursor: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="%23e53935" stroke-width="2"><path d="M12 19l7-7 3 3-7 7-3-3z"/><path d="M18 13l-1.5-7.5L2 2l3.5 14.5L13 18l5-5z"/><path d="M2 2l7.586 7.586"/><circle cx="11" cy="11" r="2"/></svg>') 12 12, crosshair; |
|
|
} |
|
|
|
|
|
body[data-tool="pen"] .main-content { |
|
|
cursor: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="%23e53935" stroke-width="2"><path d="M12 19l7-7 3 3-7 7-3-3z"/><path d="M18 13l-1.5-7.5L2 2l3.5 14.5L13 18l5-5z"/><circle cx="4" cy="20" r="2" fill="%23e53935"/></svg>') 4 20, pointer; |
|
|
} |
|
|
|
|
|
body[data-tool="eraser"] .main-content { |
|
|
cursor: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="%23e53935" stroke-width="2"><path d="M20 20H7l-7-7 7-7h13v14z"/><path d="M13 13l7-7"/><path d="M13 13L9 9"/></svg>') 12 12, auto; |
|
|
} |
|
|
|
|
|
|
|
|
.tools-section-title { |
|
|
font-weight: bold; |
|
|
color: var(--text-secondary); |
|
|
font-size: 0.65rem; |
|
|
margin: 0.75rem 0 0.5rem 0; |
|
|
text-transform: uppercase; |
|
|
letter-spacing: 0.5px; |
|
|
} |
|
|
|
|
|
.color-row { |
|
|
display: grid; |
|
|
grid-template-columns: repeat(6, 1fr); |
|
|
gap: 0.25rem; |
|
|
margin-bottom: 0.5rem; |
|
|
} |
|
|
|
|
|
.color-swatch { |
|
|
width: 18px; |
|
|
height: 18px; |
|
|
border: 2px solid var(--border-primary); |
|
|
border-radius: 3px; |
|
|
cursor: pointer; |
|
|
transition: all 0.2s ease; |
|
|
position: relative; |
|
|
} |
|
|
|
|
|
.color-swatch:hover { |
|
|
transform: scale(1.1); |
|
|
border-color: var(--text-secondary); |
|
|
} |
|
|
|
|
|
.color-swatch.selected { |
|
|
border-color: var(--text-primary); |
|
|
box-shadow: 0 0 0 2px var(--text-link); |
|
|
} |
|
|
|
|
|
.color-swatch.selected::after { |
|
|
content: '✓'; |
|
|
position: absolute; |
|
|
top: 50%; |
|
|
left: 50%; |
|
|
transform: translate(-50%, -50%); |
|
|
color: white; |
|
|
font-size: 10px; |
|
|
font-weight: bold; |
|
|
text-shadow: 1px 1px 1px black; |
|
|
} |
|
|
|
|
|
.color-input { |
|
|
width: 24px; |
|
|
height: 24px; |
|
|
border: 2px solid var(--border-primary); |
|
|
border-radius: 3px; |
|
|
cursor: pointer; |
|
|
background: none; |
|
|
padding: 0; |
|
|
grid-column: span 2; |
|
|
justify-self: center; |
|
|
} |
|
|
|
|
|
.color-input:hover { |
|
|
border-color: var(--text-secondary); |
|
|
} |
|
|
|
|
|
|
|
|
.thickness-row { |
|
|
display: flex; |
|
|
align-items: center; |
|
|
gap: 0.5rem; |
|
|
margin-top: 0.75rem; |
|
|
} |
|
|
|
|
|
.thickness-slider { |
|
|
flex: 1; |
|
|
-webkit-appearance: none; |
|
|
appearance: none; |
|
|
height: 4px; |
|
|
background: var(--border-primary); |
|
|
border-radius: 2px; |
|
|
outline: none; |
|
|
opacity: 0.7; |
|
|
transition: opacity 0.2s; |
|
|
} |
|
|
|
|
|
.thickness-slider:hover { |
|
|
opacity: 1; |
|
|
} |
|
|
|
|
|
.thickness-slider::-webkit-slider-thumb { |
|
|
-webkit-appearance: none; |
|
|
appearance: none; |
|
|
width: 12px; |
|
|
height: 12px; |
|
|
background: var(--text-link); |
|
|
border-radius: 50%; |
|
|
cursor: pointer; |
|
|
} |
|
|
|
|
|
.thickness-slider::-moz-range-thumb { |
|
|
width: 12px; |
|
|
height: 12px; |
|
|
background: var(--text-link); |
|
|
border-radius: 50%; |
|
|
cursor: pointer; |
|
|
border: none; |
|
|
} |
|
|
|
|
|
.thickness-value { |
|
|
font-size: 0.7rem; |
|
|
color: var(--text-secondary); |
|
|
min-width: 20px; |
|
|
text-align: right; |
|
|
} |
|
|
|
|
|
.highlight { |
|
|
background: none !important; |
|
|
} |
|
|
|
|
|
|
|
|
.loading-spinner { |
|
|
display: inline-block; |
|
|
width: 16px; |
|
|
height: 16px; |
|
|
border: 2px solid var(--border-primary); |
|
|
border-radius: 50%; |
|
|
border-top-color: var(--text-link); |
|
|
animation: spin 1s linear infinite; |
|
|
margin-right: 8px; |
|
|
vertical-align: middle; |
|
|
} |
|
|
|
|
|
@keyframes spin { |
|
|
to { |
|
|
transform: rotate(360deg); |
|
|
} |
|
|
} |
|
|
|
|
|
.loading-skeleton { |
|
|
display: inline-block; |
|
|
background: var(--bg-tertiary); |
|
|
background: linear-gradient(90deg, |
|
|
var(--bg-tertiary) 25%, |
|
|
var(--bg-secondary) 50%, |
|
|
var(--bg-tertiary) 75%); |
|
|
background-size: 200% 100%; |
|
|
animation: loading-shimmer 2s ease-in-out infinite; |
|
|
border-radius: 2px; |
|
|
height: 1em; |
|
|
width: 80px; |
|
|
vertical-align: middle; |
|
|
} |
|
|
|
|
|
@keyframes loading-shimmer { |
|
|
0% { |
|
|
background-position: -200% 0; |
|
|
} |
|
|
|
|
|
100% { |
|
|
background-position: 200% 0; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.cell-output:has(.loading-spinner) { |
|
|
opacity: 0.7; |
|
|
background: var(--bg-secondary); |
|
|
|
|
|
} |
|
|
</style> |
|
|
<script> |
|
|
|
|
|
function clamp(val, min, max) { return Math.max(min, Math.min(max, val)); } |
|
|
|
|
|
function restorePosition(el, storageKey) { |
|
|
try { |
|
|
const raw = localStorage.getItem(storageKey); |
|
|
if (!raw) return; |
|
|
const pos = JSON.parse(raw); |
|
|
if (typeof pos.left === 'number' && typeof pos.top === 'number') { |
|
|
el.style.left = pos.left + 'px'; |
|
|
el.style.top = pos.top + 'px'; |
|
|
el.style.right = 'auto'; |
|
|
el.style.bottom = 'auto'; |
|
|
} |
|
|
} catch (_) {} |
|
|
} |
|
|
|
|
|
function savePosition(el, storageKey) { |
|
|
try { |
|
|
const left = parseFloat(el.style.left || 'NaN'); |
|
|
const top = parseFloat(el.style.top || 'NaN'); |
|
|
if (!Number.isNaN(left) && !Number.isNaN(top)) { |
|
|
localStorage.setItem(storageKey, JSON.stringify({ left, top })); |
|
|
} |
|
|
} catch (_) {} |
|
|
} |
|
|
|
|
|
|
|
|
function makeDraggable(el, storageKey, handleEl) { |
|
|
let dragging = false; |
|
|
let startX = 0, startY = 0; |
|
|
let origLeft = 0, origTop = 0; |
|
|
|
|
|
const onMove = (e) => { |
|
|
if (!dragging) return; |
|
|
const clientX = e.touches ? e.touches[0].clientX : e.clientX; |
|
|
const clientY = e.touches ? e.touches[0].clientY : e.clientY; |
|
|
const dx = clientX - startX; |
|
|
const dy = clientY - startY; |
|
|
const w = el.offsetWidth; |
|
|
const h = el.offsetHeight; |
|
|
const maxX = window.innerWidth - w; |
|
|
const maxY = window.innerHeight - h; |
|
|
const newLeft = clamp(origLeft + dx, 0, maxX); |
|
|
const newTop = clamp(origTop + dy, 0, maxY); |
|
|
el.style.left = newLeft + 'px'; |
|
|
el.style.top = newTop + 'px'; |
|
|
el.style.right = 'auto'; |
|
|
el.style.bottom = 'auto'; |
|
|
}; |
|
|
|
|
|
const endDrag = () => { |
|
|
if (!dragging) return; |
|
|
dragging = false; |
|
|
document.removeEventListener('mousemove', onMove); |
|
|
document.removeEventListener('mouseup', endDrag); |
|
|
document.removeEventListener('touchmove', onMove); |
|
|
document.removeEventListener('touchend', endDrag); |
|
|
handleEl && (handleEl.style.cursor = 'grab'); |
|
|
savePosition(el, storageKey); |
|
|
|
|
|
try { layoutWidgetsStackedBottomRight(); } catch (_) {} |
|
|
}; |
|
|
|
|
|
const startDrag = (e) => { |
|
|
|
|
|
const elRect = el.getBoundingClientRect(); |
|
|
el.style.left = elRect.left + 'px'; |
|
|
el.style.top = elRect.top + 'px'; |
|
|
el.style.right = 'auto'; |
|
|
el.style.bottom = 'auto'; |
|
|
|
|
|
dragging = true; |
|
|
startX = e.touches ? e.touches[0].clientX : e.clientX; |
|
|
startY = e.touches ? e.touches[0].clientY : e.clientY; |
|
|
origLeft = elRect.left; |
|
|
origTop = elRect.top; |
|
|
|
|
|
document.addEventListener('mousemove', onMove); |
|
|
document.addEventListener('mouseup', endDrag); |
|
|
document.addEventListener('touchmove', onMove, { passive: false }); |
|
|
document.addEventListener('touchend', endDrag); |
|
|
handleEl && (handleEl.style.cursor = 'grabbing'); |
|
|
e.preventDefault(); |
|
|
}; |
|
|
|
|
|
(handleEl || el).addEventListener('mousedown', startDrag); |
|
|
(handleEl || el).addEventListener('touchstart', startDrag, { passive: false }); |
|
|
|
|
|
|
|
|
restorePosition(el, storageKey); |
|
|
} |
|
|
function toggleCell(cellId) { |
|
|
const codeElement = document.getElementById('code-' + cellId); |
|
|
const outputElement = document.getElementById('output-' + cellId); |
|
|
|
|
|
if (codeElement) { |
|
|
codeElement.classList.toggle('collapsed'); |
|
|
} |
|
|
if (outputElement) { |
|
|
outputElement.classList.toggle('collapsed'); |
|
|
} |
|
|
|
|
|
updateIndicators(cellId); |
|
|
encodeToolStateToUrl(); |
|
|
} |
|
|
|
|
|
function toggleCode(cellId) { |
|
|
const codeElement = document.getElementById('code-' + cellId); |
|
|
if (codeElement) { |
|
|
codeElement.classList.toggle('collapsed'); |
|
|
updateIndicators(cellId); |
|
|
encodeToolStateToUrl(); |
|
|
} |
|
|
} |
|
|
|
|
|
function toggleOutput(cellId) { |
|
|
const outputElement = document.getElementById('output-' + cellId); |
|
|
if (outputElement) { |
|
|
outputElement.classList.toggle('collapsed'); |
|
|
updateIndicators(cellId); |
|
|
encodeToolStateToUrl(); |
|
|
} |
|
|
} |
|
|
|
|
|
function toggleUvLogs(headerElement) { |
|
|
const contentElement = headerElement.nextElementSibling; |
|
|
if (contentElement) { |
|
|
const isCollapsed = contentElement.style.display === 'none'; |
|
|
contentElement.style.display = isCollapsed ? 'block' : 'none'; |
|
|
headerElement.textContent = isCollapsed ? '▼ UV Install Logs' : '▶ UV Install Logs'; |
|
|
|
|
|
|
|
|
const uvLogsDiv = headerElement.parentElement; |
|
|
if (uvLogsDiv && uvLogsDiv.id && uvLogsDiv.id.startsWith('uv-logs-')) { |
|
|
const cellId = uvLogsDiv.id.replace('uv-logs-', ''); |
|
|
const indicatorElement = document.getElementById('uv-indicator-' + cellId); |
|
|
if (indicatorElement) { |
|
|
indicatorElement.textContent = isCollapsed ? '▼ uv-logs' : '▶ uv-logs'; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
function toggleUvLogsFromHeader(cellId) { |
|
|
const uvLogsElement = document.getElementById('uv-logs-' + cellId); |
|
|
const indicatorElement = document.getElementById('uv-indicator-' + cellId); |
|
|
if (uvLogsElement) { |
|
|
const headerElement = uvLogsElement.querySelector('.uv-logs-header'); |
|
|
const contentElement = uvLogsElement.querySelector('.uv-logs-content'); |
|
|
if (contentElement && headerElement) { |
|
|
const isCollapsed = contentElement.style.display === 'none'; |
|
|
contentElement.style.display = isCollapsed ? 'block' : 'none'; |
|
|
headerElement.textContent = isCollapsed ? '▼ UV Install Logs' : '▶ UV Install Logs'; |
|
|
if (indicatorElement) { |
|
|
indicatorElement.textContent = isCollapsed ? '▼ uv-logs' : '▶ uv-logs'; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
function updateIndicators(cellId) { |
|
|
const codeElement = document.getElementById('code-' + cellId); |
|
|
const outputElement = document.getElementById('output-' + cellId); |
|
|
const indicators = document.querySelector(`[onclick*="${cellId}"]`)?.closest('.cell-header')?.querySelector('.collapse-indicators'); |
|
|
|
|
|
if (indicators) { |
|
|
const codeCollapsed = codeElement && codeElement.classList.contains('collapsed'); |
|
|
const outputCollapsed = outputElement && outputElement.classList.contains('collapsed'); |
|
|
|
|
|
const codeIcon = codeCollapsed ? '▶' : '▼'; |
|
|
const outputIcon = outputCollapsed ? '▶' : '▼'; |
|
|
|
|
|
const codeSpan = indicators.querySelector('[onclick*="toggleCode"]'); |
|
|
const outputSpan = indicators.querySelector('[onclick*="toggleOutput"]'); |
|
|
|
|
|
if (codeSpan) codeSpan.innerHTML = `${codeIcon} code`; |
|
|
if (outputSpan) outputSpan.innerHTML = `${outputIcon} output`; |
|
|
} |
|
|
} |
|
|
|
|
|
function toggleTheme() { |
|
|
const html = document.documentElement; |
|
|
const currentTheme = html.getAttribute('data-theme'); |
|
|
const newTheme = currentTheme === 'dark' ? 'light' : 'dark'; |
|
|
html.setAttribute('data-theme', newTheme); |
|
|
localStorage.setItem('uvnote-theme', newTheme); |
|
|
updateThemeIcon(); |
|
|
updateUiDebug(); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function updateThemeIcon() { |
|
|
const theme = document.documentElement.getAttribute('data-theme'); |
|
|
const toggle = document.querySelector('.theme-toggle'); |
|
|
if (toggle) { |
|
|
toggle.textContent = theme === 'dark' ? 'light' : 'dark'; |
|
|
} |
|
|
} |
|
|
function setUiTheme(newUi) { |
|
|
if (newUi !== 'default' && newUi !== 'none' && newUi !== 'monocolor') return; |
|
|
const html = document.documentElement; |
|
|
html.setAttribute('data-ui', newUi); |
|
|
try { localStorage.setItem('uvnote-ui', newUi); } catch (_) {} |
|
|
updateUiMenu(); |
|
|
updateUiDebug(); |
|
|
} |
|
|
function updateUiMenu() { |
|
|
const ui = document.documentElement.getAttribute('data-ui') || 'default'; |
|
|
const checks = { |
|
|
default: document.getElementById('checkbox-ui-default'), |
|
|
none: document.getElementById('checkbox-ui-none'), |
|
|
monocolor: document.getElementById('checkbox-ui-monocolor') |
|
|
}; |
|
|
if (checks.default) checks.default.textContent = ui === 'default' ? '☑' : '☐'; |
|
|
if (checks.none) checks.none.textContent = ui === 'none' ? '☑' : '☐'; |
|
|
if (checks.monocolor) checks.monocolor.textContent = ui === 'monocolor' ? '☑' : '☐'; |
|
|
} |
|
|
|
|
|
function updateUiDebug() { |
|
|
const ui = document.documentElement.getAttribute('data-ui') || 'default'; |
|
|
const color = document.documentElement.getAttribute('data-theme') || 'light'; |
|
|
const el = document.getElementById('ui-debug'); |
|
|
if (el) { |
|
|
el.textContent = `UI: ${ui} | Color: ${color}`; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function clearLineSelections() { |
|
|
try { |
|
|
document.querySelectorAll('.code-line-highlight').forEach(el => { el.style.display = 'none'; }); |
|
|
document.querySelectorAll('.line-number.selected').forEach(el => el.classList.remove('selected')); |
|
|
} catch (_) {} |
|
|
} |
|
|
|
|
|
let _selection = null; |
|
|
|
|
|
function clearSelection(updateUrl) { |
|
|
clearLineSelections(); |
|
|
_selection = null; |
|
|
if (updateUrl) { |
|
|
try { |
|
|
const url = new URL(window.location.href); |
|
|
url.searchParams.delete('cell'); |
|
|
url.searchParams.delete('line'); |
|
|
history.replaceState(null, '', url.toString()); |
|
|
} catch (_) {} |
|
|
} |
|
|
updateStateIndicator(); |
|
|
} |
|
|
|
|
|
function selectCellLine(cellId, line, updateUrl) { |
|
|
try { |
|
|
|
|
|
clearLineSelections(); |
|
|
const codeBox = document.getElementById(`code-${cellId}`); |
|
|
if (!codeBox) return; |
|
|
const pre = codeBox.querySelector('.highlight pre'); |
|
|
const overlay = document.getElementById(`line-highlight-${cellId}`); |
|
|
const numbers = document.getElementById(`lines-${cellId}`); |
|
|
if (!pre || !overlay) return; |
|
|
|
|
|
|
|
|
const preStyle = getComputedStyle(pre); |
|
|
const padTop = parseFloat(preStyle.paddingTop || '0'); |
|
|
const lh = parseFloat(preStyle.lineHeight || '20'); |
|
|
|
|
|
|
|
|
overlay.style.display = 'block'; |
|
|
overlay.style.height = `${lh}px`; |
|
|
overlay.style.top = `${pre.offsetTop + padTop + (line - 1) * lh}px`; |
|
|
|
|
|
|
|
|
if (numbers) { |
|
|
numbers.querySelectorAll('.line-number').forEach(a => a.classList.remove('selected')); |
|
|
const sel = numbers.querySelector(`.line-number[data-line="${line}"]`); |
|
|
if (sel) sel.classList.add('selected'); |
|
|
} |
|
|
|
|
|
if (updateUrl) { |
|
|
const url = new URL(window.location.href); |
|
|
url.searchParams.set('cell', cellId); |
|
|
url.searchParams.set('line', String(line)); |
|
|
history.replaceState(null, '', url.toString()); |
|
|
} |
|
|
_selection = { cellId, a: line, b: line }; |
|
|
updateStateIndicator(); |
|
|
} catch (e) { console.warn('selectCellLine error', e); } |
|
|
} |
|
|
|
|
|
function selectCellLines(cellId, startLine, endLine, updateUrl) { |
|
|
try { |
|
|
|
|
|
const a = Math.min(startLine, endLine); |
|
|
const b = Math.max(startLine, endLine); |
|
|
clearLineSelections(); |
|
|
const codeBox = document.getElementById(`code-${cellId}`); |
|
|
if (!codeBox) return; |
|
|
const pre = codeBox.querySelector('.highlight pre'); |
|
|
const overlay = document.getElementById(`line-highlight-${cellId}`); |
|
|
const numbers = document.getElementById(`lines-${cellId}`); |
|
|
if (!pre || !overlay) return; |
|
|
|
|
|
const preStyle = getComputedStyle(pre); |
|
|
const padTop = parseFloat(preStyle.paddingTop || '0'); |
|
|
const lh = parseFloat(preStyle.lineHeight || '20'); |
|
|
|
|
|
overlay.style.display = 'block'; |
|
|
overlay.style.top = `${pre.offsetTop + padTop + (a - 1) * lh}px`; |
|
|
overlay.style.height = `${(b - a + 1) * lh}px`; |
|
|
|
|
|
if (numbers) { |
|
|
numbers.querySelectorAll('.line-number').forEach(a => a.classList.remove('selected')); |
|
|
for (let i = a; i <= b; i++) { |
|
|
const el = numbers.querySelector(`.line-number[data-line="${i}"]`); |
|
|
if (el) el.classList.add('selected'); |
|
|
} |
|
|
} |
|
|
|
|
|
if (updateUrl) { |
|
|
const url = new URL(window.location.href); |
|
|
url.searchParams.set('cell', cellId); |
|
|
if (a === b) url.searchParams.set('line', String(a)); |
|
|
else url.searchParams.set('line', `${a}-${b}`); |
|
|
history.replaceState(null, '', url.toString()); |
|
|
} |
|
|
_selection = { cellId, a, b }; |
|
|
updateStateIndicator(); |
|
|
} catch (e) { console.warn('selectCellLines error', e); } |
|
|
} |
|
|
|
|
|
|
|
|
let _lineDrag = { active: false, cellId: null, start: 0 }; |
|
|
function onLineNumberMouseDown(e) { |
|
|
const a = e.target.closest('.line-number'); |
|
|
if (!a) return; |
|
|
e.preventDefault(); |
|
|
const cellId = a.dataset.cell; |
|
|
const line = parseInt(a.dataset.line || '1', 10) || 1; |
|
|
|
|
|
const numbers = document.getElementById(`lines-${cellId}`); |
|
|
if (numbers) { |
|
|
const selected = Array.from(numbers.querySelectorAll('.line-number.selected')).map(n => parseInt(n.dataset.line||'0',10)).filter(Boolean); |
|
|
if (selected.length === 1 && selected[0] === line) { |
|
|
clearSelection(true); |
|
|
return; |
|
|
} |
|
|
} |
|
|
_lineDrag.active = true; |
|
|
_lineDrag.cellId = cellId; |
|
|
_lineDrag.start = line; |
|
|
selectCellLines(_lineDrag.cellId, _lineDrag.start, _lineDrag.start, false); |
|
|
} |
|
|
function onDocMouseMove(e) { |
|
|
if (!_lineDrag.active) return; |
|
|
const el = document.elementFromPoint(e.clientX, e.clientY); |
|
|
if (!el) return; |
|
|
const a = el.closest && el.closest('.line-number'); |
|
|
if (!a) return; |
|
|
if (a.dataset.cell !== _lineDrag.cellId) return; |
|
|
const cur = parseInt(a.dataset.line || '1', 10) || 1; |
|
|
selectCellLines(_lineDrag.cellId, _lineDrag.start, cur, false); |
|
|
} |
|
|
function onDocMouseUp(e) { |
|
|
if (!_lineDrag.active) return; |
|
|
const last = document.querySelector('.line-number.selected:last-of-type'); |
|
|
|
|
|
const numbers = document.getElementById(`lines-${_lineDrag.cellId}`); |
|
|
if (numbers) { |
|
|
const selected = Array.from(numbers.querySelectorAll('.line-number.selected')).map(n => parseInt(n.dataset.line||'0',10)).filter(Boolean); |
|
|
if (selected.length) { |
|
|
const a = Math.min(...selected); const b = Math.max(...selected); |
|
|
selectCellLines(_lineDrag.cellId, a, b, true); |
|
|
} |
|
|
} |
|
|
_lineDrag.active = false; _lineDrag.cellId = null; _lineDrag.start = 0; |
|
|
} |
|
|
|
|
|
function applyLocationFromUrl() { |
|
|
try { |
|
|
const url = new URL(window.location.href); |
|
|
const cell = url.searchParams.get('cell'); |
|
|
const lineParam = url.searchParams.get('line'); |
|
|
if (cell && lineParam) { |
|
|
if (lineParam.includes('-')) { |
|
|
const [a, b] = lineParam.split('-').map(x => parseInt(x, 10)); |
|
|
if (!Number.isNaN(a) && !Number.isNaN(b)) selectCellLines(cell, a, b, false); |
|
|
} else { |
|
|
const l = parseInt(lineParam, 10); |
|
|
if (!Number.isNaN(l)) selectCellLine(cell, l, false); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
applyToolsFromUrl(url.searchParams); |
|
|
|
|
|
|
|
|
const encodedCellStates = url.searchParams.get('cells'); |
|
|
console.log('Encoded cell states from URL:', encodedCellStates); |
|
|
} catch (_) {} |
|
|
} |
|
|
|
|
|
function applyToolsFromUrl(params) { |
|
|
try { |
|
|
|
|
|
const showTools = params.get('tools'); |
|
|
if (showTools === '1') { |
|
|
|
|
|
_urlLoadedTool = true; |
|
|
|
|
|
|
|
|
const color = params.get('color'); |
|
|
if (color && /^[0-9a-fA-F]{6}$/.test(color)) { |
|
|
setStoredArrowColor('#' + color); |
|
|
} |
|
|
|
|
|
|
|
|
const thickness = params.get('thickness'); |
|
|
if (thickness) { |
|
|
const value = parseInt(thickness, 10); |
|
|
if (value >= 1 && value <= 10) { |
|
|
setStoredLineThickness(value); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const encodedShapes = params.get('shapes'); |
|
|
if (encodedShapes) { |
|
|
const decodedShapes = decodeShapesFromUrl(encodedShapes); |
|
|
if (decodedShapes.length > 0) { |
|
|
_shapes = decodedShapes; |
|
|
saveShapes(); |
|
|
|
|
|
setTimeout(() => { |
|
|
renderOverlay(); |
|
|
}, 300); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
setTimeout(() => { |
|
|
const toolsWidget = document.querySelector('.tools-widget'); |
|
|
const checkbox = document.getElementById('checkbox-tools'); |
|
|
if (toolsWidget && checkbox) { |
|
|
toolsWidget.style.display = 'block'; |
|
|
checkbox.textContent = '☑'; |
|
|
localStorage.setItem('uvnote-widget-tools', 'visible'); |
|
|
} |
|
|
|
|
|
|
|
|
const activeTool = params.get('tool'); |
|
|
if (activeTool && ['arrow', 'pen', 'eraser', 'spotlight'].includes(activeTool)) { |
|
|
const toolBtn = Array.from(document.querySelectorAll('.tool-button')).find(btn => btn.textContent === activeTool); |
|
|
if (toolBtn) { |
|
|
toolBtn.click(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
layoutWidgetsStackedBottomRight(); |
|
|
}, 200); |
|
|
} |
|
|
} catch (_) {} |
|
|
} |
|
|
|
|
|
function captureInitialCellStates() { |
|
|
const cells = document.querySelectorAll('.cell'); |
|
|
cells.forEach(cell => { |
|
|
const cellId = cell.id.replace('cell-', ''); |
|
|
const codeEl = document.getElementById('code-' + cellId); |
|
|
const outputEl = document.getElementById('output-' + cellId); |
|
|
|
|
|
if (codeEl || outputEl) { |
|
|
const state = {}; |
|
|
if (codeEl) { |
|
|
state.c = codeEl.classList.contains('collapsed') ? 0 : 1; |
|
|
} |
|
|
if (outputEl) { |
|
|
state.o = outputEl.classList.contains('collapsed') ? 0 : 1; |
|
|
} |
|
|
_initialCellStates[cellId] = state; |
|
|
} |
|
|
}); |
|
|
console.log('Captured initial cell states:', _initialCellStates); |
|
|
} |
|
|
|
|
|
function encodeCellStatesToUrl() { |
|
|
|
|
|
const cells = document.querySelectorAll('.cell'); |
|
|
const cellStates = {}; |
|
|
|
|
|
console.log('Found cells:', cells.length); |
|
|
|
|
|
cells.forEach(cell => { |
|
|
const cellId = cell.id.replace('cell-', ''); |
|
|
const codeEl = document.getElementById('code-' + cellId); |
|
|
const outputEl = document.getElementById('output-' + cellId); |
|
|
const initialState = _initialCellStates[cellId] || {}; |
|
|
|
|
|
console.log(`Encoding cell ${cellId}:`, { |
|
|
codeEl: !!codeEl, |
|
|
outputEl: !!outputEl, |
|
|
codeCollapsed: codeEl ? codeEl.classList.contains('collapsed') : 'N/A', |
|
|
outputCollapsed: outputEl ? outputEl.classList.contains('collapsed') : 'N/A', |
|
|
initialState: initialState |
|
|
}); |
|
|
|
|
|
if (codeEl || outputEl) { |
|
|
const state = {}; |
|
|
let hasChanges = false; |
|
|
|
|
|
if (codeEl) { |
|
|
const currentCodeState = codeEl.classList.contains('collapsed') ? 0 : 1; |
|
|
const initialCodeState = initialState.c; |
|
|
|
|
|
if (initialCodeState !== undefined && currentCodeState !== initialCodeState) { |
|
|
state.c = currentCodeState; |
|
|
hasChanges = true; |
|
|
} |
|
|
} |
|
|
|
|
|
if (outputEl) { |
|
|
const currentOutputState = outputEl.classList.contains('collapsed') ? 0 : 1; |
|
|
const initialOutputState = initialState.o; |
|
|
|
|
|
if (initialOutputState !== undefined && currentOutputState !== initialOutputState) { |
|
|
state.o = currentOutputState; |
|
|
hasChanges = true; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (hasChanges) { |
|
|
cellStates[cellId] = state; |
|
|
console.log(`Added cell ${cellId}:`, state); |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
console.log('Final cell states to encode:', cellStates); |
|
|
|
|
|
|
|
|
if (Object.keys(cellStates).length === 0) return ''; |
|
|
|
|
|
|
|
|
const encoded = btoa(JSON.stringify(cellStates)); |
|
|
console.log('Encoded cell states:', encoded); |
|
|
return encoded; |
|
|
} |
|
|
|
|
|
function decodeCellStatesFromUrl(encodedStates) { |
|
|
if (!encodedStates) return {}; |
|
|
|
|
|
try { |
|
|
return JSON.parse(atob(encodedStates)); |
|
|
} catch (e) { |
|
|
console.error('Failed to decode cell states:', e); |
|
|
return {}; |
|
|
} |
|
|
} |
|
|
|
|
|
function applyCellStatesFromUrl(cellStates) { |
|
|
console.log('Applying cell states from URL:', cellStates); |
|
|
Object.entries(cellStates).forEach(([cellId, state]) => { |
|
|
const codeEl = document.getElementById('code-' + cellId); |
|
|
const outputEl = document.getElementById('output-' + cellId); |
|
|
|
|
|
console.log(`Cell ${cellId}:`, { |
|
|
codeEl: !!codeEl, |
|
|
outputEl: !!outputEl, |
|
|
state: state |
|
|
}); |
|
|
|
|
|
if (codeEl && state.c !== undefined) { |
|
|
if (state.c === 0) { |
|
|
codeEl.classList.add('collapsed'); |
|
|
console.log(`Collapsed code for cell ${cellId}`, { |
|
|
hasCollapsedClass: codeEl.classList.contains('collapsed'), |
|
|
computedDisplay: getComputedStyle(codeEl).display, |
|
|
classList: Array.from(codeEl.classList), |
|
|
elementId: codeEl.id |
|
|
}); |
|
|
} else { |
|
|
codeEl.classList.remove('collapsed'); |
|
|
codeEl.classList.add('expanded'); |
|
|
console.log(`Expanded code for cell ${cellId}`, { |
|
|
hasCollapsedClass: codeEl.classList.contains('collapsed'), |
|
|
hasExpandedClass: codeEl.classList.contains('expanded'), |
|
|
computedDisplay: getComputedStyle(codeEl).display, |
|
|
classList: Array.from(codeEl.classList), |
|
|
elementId: codeEl.id |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
if (outputEl && state.o !== undefined) { |
|
|
if (state.o === 0) { |
|
|
outputEl.classList.add('collapsed'); |
|
|
console.log(`Collapsed output for cell ${cellId}`); |
|
|
} else { |
|
|
outputEl.classList.remove('collapsed'); |
|
|
console.log(`Expanded output for cell ${cellId}`); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
try { |
|
|
updateIndicators(cellId); |
|
|
|
|
|
if (codeEl) { |
|
|
codeEl.offsetHeight; |
|
|
console.log(`After indicators update - code visible: ${getComputedStyle(codeEl).display !== 'none'}`); |
|
|
} |
|
|
if (outputEl) { |
|
|
outputEl.offsetHeight; |
|
|
console.log(`After indicators update - output visible: ${getComputedStyle(outputEl).display !== 'none'}`); |
|
|
} |
|
|
} catch (e) { |
|
|
console.error(`Error updating indicators for cell ${cellId}:`, e); |
|
|
} |
|
|
}); |
|
|
} |
|
|
|
|
|
function encodeShapesToUrl() { |
|
|
|
|
|
if (_shapes.length === 0) return ''; |
|
|
|
|
|
const shapeData = _shapes.map(shape => { |
|
|
const baseData = { |
|
|
ct: shape.createdAt, |
|
|
fo: shape.fadeoutTime || getFadeoutTime() |
|
|
}; |
|
|
|
|
|
if (shape.type === 'arrow') { |
|
|
return { |
|
|
...baseData, |
|
|
t: 'a', |
|
|
x1: Math.round(shape.x1), |
|
|
y1: Math.round(shape.y1), |
|
|
x2: Math.round(shape.x2), |
|
|
y2: Math.round(shape.y2), |
|
|
c: shape.color.substring(1), |
|
|
w: shape.width |
|
|
}; |
|
|
} else if (shape.type === 'pen') { |
|
|
return { |
|
|
...baseData, |
|
|
t: 'p', |
|
|
pts: shape.points.map(p => [Math.round(p.x), Math.round(p.y)]), |
|
|
c: shape.color.substring(1), |
|
|
w: shape.width |
|
|
}; |
|
|
} else if (shape.type === 'spotlight') { |
|
|
return { |
|
|
...baseData, |
|
|
t: 's', |
|
|
x: Math.round(shape.x), |
|
|
y: Math.round(shape.y), |
|
|
r: Math.round(shape.radius) |
|
|
}; |
|
|
} |
|
|
}).filter(Boolean); |
|
|
|
|
|
return btoa(JSON.stringify(shapeData)); |
|
|
} |
|
|
|
|
|
function decodeShapesFromUrl(encodedShapes) { |
|
|
if (!encodedShapes) return []; |
|
|
|
|
|
try { |
|
|
const shapeData = JSON.parse(atob(encodedShapes)); |
|
|
return shapeData.map(data => { |
|
|
const base = { |
|
|
createdAt: data.ct || Date.now(), |
|
|
fadeoutTime: data.fo || 0, |
|
|
opacity: 1.0 |
|
|
}; |
|
|
|
|
|
if (data.t === 'a') { |
|
|
return { |
|
|
...base, |
|
|
type: 'arrow', |
|
|
x1: data.x1, |
|
|
y1: data.y1, |
|
|
x2: data.x2, |
|
|
y2: data.y2, |
|
|
color: '#' + data.c, |
|
|
width: data.w |
|
|
}; |
|
|
} else if (data.t === 'p') { |
|
|
return { |
|
|
...base, |
|
|
type: 'pen', |
|
|
points: data.pts.map(([x, y]) => ({ x, y })), |
|
|
color: '#' + data.c, |
|
|
width: data.w |
|
|
}; |
|
|
} else if (data.t === 's') { |
|
|
return { |
|
|
...base, |
|
|
type: 'spotlight', |
|
|
x: data.x, |
|
|
y: data.y, |
|
|
radius: data.r, |
|
|
color: '#000000' |
|
|
}; |
|
|
} |
|
|
}).filter(Boolean); |
|
|
} catch (e) { |
|
|
console.error('Failed to decode shapes:', e); |
|
|
return []; |
|
|
} |
|
|
} |
|
|
|
|
|
function encodeToolStateToUrl() { |
|
|
|
|
|
if (_isInitializing) { |
|
|
return window.location.href; |
|
|
} |
|
|
|
|
|
const params = new URLSearchParams(window.location.search); |
|
|
|
|
|
|
|
|
const toolsWidget = document.querySelector('.tools-widget'); |
|
|
const activeTool = document.body.dataset.tool; |
|
|
const hasActiveTool = activeTool && activeTool !== 'none'; |
|
|
const toolsWidgetVisible = toolsWidget && getComputedStyle(toolsWidget).display !== 'none'; |
|
|
|
|
|
|
|
|
const encodedShapes = encodeShapesToUrl(); |
|
|
if (encodedShapes) { |
|
|
params.set('shapes', encodedShapes); |
|
|
} else { |
|
|
params.delete('shapes'); |
|
|
} |
|
|
|
|
|
|
|
|
const existingCellStates = params.get('cells'); |
|
|
if (existingCellStates) { |
|
|
|
|
|
params.set('cells', existingCellStates); |
|
|
} else { |
|
|
|
|
|
const encodedCellStates = encodeCellStatesToUrl(); |
|
|
if (encodedCellStates) { |
|
|
params.set('cells', encodedCellStates); |
|
|
} |
|
|
} |
|
|
|
|
|
if (toolsWidgetVisible && hasActiveTool) { |
|
|
|
|
|
params.set('tools', '1'); |
|
|
params.set('tool', activeTool); |
|
|
|
|
|
|
|
|
const color = getArrowColor(); |
|
|
if (color && color.startsWith('#')) { |
|
|
params.set('color', color.substring(1)); |
|
|
} |
|
|
|
|
|
|
|
|
const thickness = getLineThickness(); |
|
|
params.set('thickness', thickness.toString()); |
|
|
} else { |
|
|
|
|
|
params.delete('tools'); |
|
|
params.delete('tool'); |
|
|
params.delete('color'); |
|
|
params.delete('thickness'); |
|
|
params.delete('fadeout'); |
|
|
} |
|
|
|
|
|
|
|
|
const newUrl = window.location.pathname + (params.toString() ? '?' + params.toString() : '') + window.location.hash; |
|
|
window.history.replaceState(null, '', newUrl); |
|
|
|
|
|
return window.location.href; |
|
|
} |
|
|
|
|
|
function resetLayout() { |
|
|
try { |
|
|
|
|
|
const allKeys = Object.keys(localStorage); |
|
|
const uvnoteKeys = allKeys.filter(key => key.startsWith('uvnote-')); |
|
|
uvnoteKeys.forEach(k => localStorage.removeItem(k)); |
|
|
} catch (_) {} |
|
|
|
|
|
|
|
|
try { clearSelection(true); } catch(_) {} |
|
|
|
|
|
try { window.setActiveTool('none'); } catch(_) {} |
|
|
|
|
|
try { _shapes = []; saveShapes(); } catch(_) {} |
|
|
|
|
|
try { _urlLoadedTool = false; } catch(_) {} |
|
|
|
|
|
try { |
|
|
const cells = document.querySelectorAll('.cell'); |
|
|
cells.forEach(cell => { |
|
|
const cellId = cell.id.replace('cell-', ''); |
|
|
const codeEl = document.getElementById('code-' + cellId); |
|
|
const outputEl = document.getElementById('output-' + cellId); |
|
|
if (codeEl) codeEl.classList.remove('collapsed'); |
|
|
if (outputEl) outputEl.classList.remove('collapsed'); |
|
|
updateIndicators(cellId); |
|
|
}); |
|
|
} catch(_) {} |
|
|
|
|
|
|
|
|
try { |
|
|
const cleanUrl = window.location.pathname + window.location.hash; |
|
|
window.location.href = cleanUrl; |
|
|
} catch (_) { |
|
|
|
|
|
location.reload(); |
|
|
} |
|
|
} |
|
|
|
|
|
function toggleMenu() { |
|
|
const menuButton = document.querySelector('.menu-button'); |
|
|
if (menuButton) { |
|
|
menuButton.classList.toggle('active'); |
|
|
} |
|
|
} |
|
|
|
|
|
function toggleWidget(widgetName) { |
|
|
let widget; |
|
|
let checkbox; |
|
|
|
|
|
|
|
|
const menuButton = document.querySelector('.menu-button'); |
|
|
if (menuButton) { |
|
|
menuButton.classList.remove('active'); |
|
|
} |
|
|
|
|
|
switch(widgetName) { |
|
|
case 'tools': |
|
|
widget = document.querySelector('.tools-widget'); |
|
|
checkbox = document.getElementById('checkbox-tools'); |
|
|
break; |
|
|
case 'file-explorer': |
|
|
widget = document.querySelector('.file-explorer'); |
|
|
checkbox = document.getElementById('checkbox-file-explorer'); |
|
|
break; |
|
|
case 'minimap': |
|
|
widget = document.querySelector('.minimap'); |
|
|
checkbox = document.getElementById('checkbox-minimap'); |
|
|
break; |
|
|
case 'status': |
|
|
widget = document.querySelector('.status-widget'); |
|
|
checkbox = document.getElementById('checkbox-status'); |
|
|
break; |
|
|
default: |
|
|
return; |
|
|
} |
|
|
|
|
|
if (widget && checkbox) { |
|
|
const isVisible = getComputedStyle(widget).display !== 'none'; |
|
|
widget.style.display = isVisible ? 'none' : 'block'; |
|
|
checkbox.textContent = isVisible ? '☐' : '☑'; |
|
|
|
|
|
|
|
|
try { |
|
|
localStorage.setItem(`uvnote-widget-${widgetName}`, isVisible ? 'hidden' : 'visible'); |
|
|
} catch (_) {} |
|
|
|
|
|
|
|
|
try { |
|
|
layoutWidgetsStackedBottomRight(); |
|
|
} catch (_) {} |
|
|
|
|
|
|
|
|
if (widgetName === 'tools') { |
|
|
encodeToolStateToUrl(); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
function initializeWidgetVisibility() { |
|
|
const widgets = [ |
|
|
{ name: 'tools', selector: '.tools-widget' }, |
|
|
{ name: 'file-explorer', selector: '.file-explorer' }, |
|
|
{ name: 'minimap', selector: '.minimap' }, |
|
|
{ name: 'status', selector: '.status-widget' } |
|
|
]; |
|
|
|
|
|
widgets.forEach(({ name, selector }) => { |
|
|
const defaultState = name === 'status' ? 'visible' : 'hidden'; |
|
|
const savedState = localStorage.getItem(`uvnote-widget-${name}`) || defaultState; |
|
|
const widget = document.querySelector(selector); |
|
|
const checkbox = document.getElementById(`checkbox-${name}`); |
|
|
|
|
|
if (widget && checkbox) { |
|
|
const isVisible = savedState === 'visible'; |
|
|
widget.style.display = isVisible ? 'block' : 'none'; |
|
|
checkbox.textContent = isVisible ? '☑' : '☐'; |
|
|
} |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
document.addEventListener('click', function(event) { |
|
|
const menuButton = document.querySelector('.menu-button'); |
|
|
|
|
|
if (menuButton && !menuButton.contains(event.target)) { |
|
|
menuButton.classList.remove('active'); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
function hasCustomWidgetPositions() { |
|
|
try { |
|
|
return ( |
|
|
localStorage.getItem('uvnote-minimap-pos') || |
|
|
localStorage.getItem('uvnote-file-explorer-pos') || |
|
|
localStorage.getItem('uvnote-tools-pos') |
|
|
); |
|
|
} catch (_) { return false; } |
|
|
} |
|
|
|
|
|
function rectsOverlap(r1, r2) { |
|
|
return !(r1.right <= r2.left || r2.right <= r1.left || r1.bottom <= r2.top || r2.bottom <= r1.top); |
|
|
} |
|
|
|
|
|
function widgetsOverlap(widgets) { |
|
|
for (let i = 0; i < widgets.length; i++) { |
|
|
const a = widgets[i]; |
|
|
const ra = a.getBoundingClientRect(); |
|
|
for (let j = i + 1; j < widgets.length; j++) { |
|
|
const b = widgets[j]; |
|
|
const rb = b.getBoundingClientRect(); |
|
|
if (rectsOverlap(ra, rb)) return true; |
|
|
} |
|
|
} |
|
|
return false; |
|
|
} |
|
|
|
|
|
function applyStackLayout(widgets, order) { |
|
|
if (!widgets.length) return; |
|
|
|
|
|
const fixedWidth = 220; |
|
|
widgets.forEach(el => { el.style.width = fixedWidth + 'px'; }); |
|
|
|
|
|
|
|
|
const gap = 12; |
|
|
const available = Math.max(0, window.innerHeight - 40 - gap * (order.length - 1)); |
|
|
const eachMax = Math.floor(available / order.length); |
|
|
order.forEach(el => { |
|
|
el.style.maxHeight = eachMax + 'px'; |
|
|
el.style.overflowY = 'auto'; |
|
|
}); |
|
|
|
|
|
|
|
|
let bottomOffset = 20; |
|
|
order.forEach(el => { |
|
|
el.style.left = 'auto'; |
|
|
el.style.top = 'auto'; |
|
|
el.style.right = '20px'; |
|
|
el.style.bottom = bottomOffset + 'px'; |
|
|
bottomOffset += el.offsetHeight + gap; |
|
|
}); |
|
|
} |
|
|
|
|
|
function layoutWidgetsStackedBottomRight() { |
|
|
const minimap = document.querySelector('.minimap'); |
|
|
const fileExplorer = document.querySelector('.file-explorer'); |
|
|
const tools = document.querySelector('.tools-widget'); |
|
|
const status = document.querySelector('.status-widget'); |
|
|
const widgets = [minimap, fileExplorer, tools, status].filter(el => el && getComputedStyle(el).display !== 'none'); |
|
|
if (!widgets.length) return; |
|
|
|
|
|
const order = [minimap, fileExplorer, tools, status].filter(Boolean).filter(el => getComputedStyle(el).display !== 'none'); |
|
|
|
|
|
|
|
|
if (hasCustomWidgetPositions() && !widgetsOverlap(widgets)) return; |
|
|
|
|
|
applyStackLayout(widgets, order); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
let _minimapScrollContainer = null; |
|
|
let _minimapScrollHandler = null; |
|
|
function initMinimap() { |
|
|
|
|
|
const minimap = createMinimap(); |
|
|
document.body.appendChild(minimap); |
|
|
|
|
|
const mTitle = minimap.querySelector('.minimap-title'); |
|
|
makeDraggable(minimap, 'uvnote-minimap-pos', mTitle); |
|
|
|
|
|
|
|
|
_minimapScrollContainer = window; |
|
|
|
|
|
if (_minimapScrollContainer) { |
|
|
_minimapScrollHandler = () => updateMinimapActive(); |
|
|
if (_minimapScrollContainer === window) { |
|
|
window.addEventListener('scroll', _minimapScrollHandler); |
|
|
} else { |
|
|
_minimapScrollContainer.addEventListener('scroll', _minimapScrollHandler); |
|
|
} |
|
|
} |
|
|
updateMinimapActive(); |
|
|
} |
|
|
|
|
|
function teardownMinimap() { |
|
|
const minimap = document.querySelector('.minimap'); |
|
|
if (minimap && minimap.parentNode) minimap.parentNode.removeChild(minimap); |
|
|
if (_minimapScrollContainer && _minimapScrollHandler) { |
|
|
if (_minimapScrollContainer === window) { |
|
|
window.removeEventListener('scroll', _minimapScrollHandler); |
|
|
} else { |
|
|
_minimapScrollContainer.removeEventListener('scroll', _minimapScrollHandler); |
|
|
} |
|
|
} |
|
|
_minimapScrollContainer = null; |
|
|
_minimapScrollHandler = null; |
|
|
} |
|
|
|
|
|
function initFileExplorer() { |
|
|
|
|
|
const fileExplorer = createFileExplorer(); |
|
|
document.body.appendChild(fileExplorer); |
|
|
} |
|
|
|
|
|
function createMinimap() { |
|
|
const minimap = document.createElement('div'); |
|
|
minimap.className = 'minimap'; |
|
|
|
|
|
const title = document.createElement('div'); |
|
|
title.className = 'minimap-title'; |
|
|
title.textContent = 'navigation'; |
|
|
minimap.appendChild(title); |
|
|
|
|
|
|
|
|
const root = document.querySelector('.main-content') || document; |
|
|
const headings = root.querySelectorAll('h1, h2, h3, h4, h5, h6'); |
|
|
const cells = root.querySelectorAll('.cell'); |
|
|
|
|
|
|
|
|
const items = []; |
|
|
|
|
|
headings.forEach(heading => { |
|
|
const id = heading.id || generateId(heading.textContent); |
|
|
if (!heading.id) heading.id = id; |
|
|
|
|
|
items.push({ |
|
|
element: heading, |
|
|
type: 'heading', |
|
|
level: parseInt(heading.tagName.charAt(1)), |
|
|
text: heading.textContent.trim(), |
|
|
id: id, |
|
|
position: heading.getBoundingClientRect().top + window.scrollY |
|
|
}); |
|
|
}); |
|
|
|
|
|
cells.forEach(cell => { |
|
|
const header = cell.querySelector('.cell-header'); |
|
|
if (header) { |
|
|
const id = cell.id || `cell-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; |
|
|
if (!cell.id) cell.id = id; |
|
|
|
|
|
items.push({ |
|
|
element: cell, |
|
|
type: 'cell', |
|
|
text: header.textContent.trim(), |
|
|
id: id, |
|
|
position: cell.getBoundingClientRect().top + window.scrollY |
|
|
}); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
items.sort((a, b) => a.position - b.position); |
|
|
|
|
|
|
|
|
items.forEach(item => { |
|
|
const link = document.createElement('a'); |
|
|
link.className = `minimap-item ${item.type === 'heading' ? 'minimap-heading' : 'minimap-cell'}`; |
|
|
if (item.type === 'heading') { |
|
|
link.classList.add(`h${item.level}`); |
|
|
} |
|
|
link.textContent = item.text.length > 25 ? item.text.substring(0, 22) + '...' : item.text; |
|
|
link.href = `#${item.id}`; |
|
|
link.onclick = function(e) { |
|
|
e.preventDefault(); |
|
|
item.element.scrollIntoView({ behavior: 'smooth', block: 'start' }); |
|
|
}; |
|
|
minimap.appendChild(link); |
|
|
}); |
|
|
|
|
|
return minimap; |
|
|
} |
|
|
|
|
|
function generateId(text) { |
|
|
return text.toLowerCase() |
|
|
.replace(/[^a-z0-9]+/g, '-') |
|
|
.replace(/^-+|-+$/g, '') |
|
|
.substring(0, 20); |
|
|
} |
|
|
|
|
|
function updateMinimapActive() { |
|
|
const minimapItems = document.querySelectorAll('.minimap-item'); |
|
|
const container = _minimapScrollContainer || window; |
|
|
const containerRect = container === window ? null : container.getBoundingClientRect(); |
|
|
const scrollPos = (container === window ? window.scrollY : container.scrollTop) + 100; |
|
|
|
|
|
let activeItem = null; |
|
|
minimapItems.forEach(item => { |
|
|
const targetId = item.getAttribute('href').substring(1); |
|
|
const target = document.getElementById(targetId); |
|
|
|
|
|
if (target) { |
|
|
const rectTop = target.getBoundingClientRect().top; |
|
|
const targetPos = (container === window) |
|
|
? rectTop + window.scrollY |
|
|
: rectTop - containerRect.top + container.scrollTop; |
|
|
if (targetPos <= scrollPos) { |
|
|
activeItem = item; |
|
|
} |
|
|
} |
|
|
|
|
|
item.classList.remove('active'); |
|
|
}); |
|
|
|
|
|
if (activeItem) { |
|
|
activeItem.classList.add('active'); |
|
|
} |
|
|
} |
|
|
|
|
|
function createFileExplorer() { |
|
|
const fileExplorer = document.createElement('div'); |
|
|
fileExplorer.className = 'file-explorer'; |
|
|
|
|
|
const title = document.createElement('div'); |
|
|
title.className = 'file-explorer-title'; |
|
|
title.textContent = 'files'; |
|
|
fileExplorer.appendChild(title); |
|
|
|
|
|
makeDraggable(fileExplorer, 'uvnote-file-explorer-pos', title); |
|
|
|
|
|
|
|
|
const scriptsSection = document.createElement('div'); |
|
|
scriptsSection.className = 'file-explorer-section'; |
|
|
|
|
|
const scriptsTitle = document.createElement('div'); |
|
|
scriptsTitle.className = 'file-explorer-section-title'; |
|
|
scriptsTitle.textContent = 'scripts'; |
|
|
scriptsSection.appendChild(scriptsTitle); |
|
|
|
|
|
|
|
|
const root = document.querySelector('.main-content') || document; |
|
|
const cells = root.querySelectorAll('.cell'); |
|
|
cells.forEach(cell => { |
|
|
const header = cell.querySelector('.cell-header'); |
|
|
if (header) { |
|
|
const cellText = header.textContent.trim(); |
|
|
const cellMatch = cellText.match(/Cell: ([a-zA-Z_][a-zA-Z0-9_]*)/); |
|
|
if (cellMatch) { |
|
|
const cellId = cellMatch[1]; |
|
|
const scriptItem = document.createElement('div'); |
|
|
scriptItem.className = 'file-explorer-item script'; |
|
|
scriptItem.textContent = `${cellId}.py`; |
|
|
scriptItem.onclick = function() { |
|
|
cell.scrollIntoView({ behavior: 'smooth', block: 'start' }); |
|
|
}; |
|
|
scriptsSection.appendChild(scriptItem); |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
fileExplorer.appendChild(scriptsSection); |
|
|
|
|
|
|
|
|
const artifactsSection = document.createElement('div'); |
|
|
artifactsSection.className = 'file-explorer-section'; |
|
|
|
|
|
const artifactsTitle = document.createElement('div'); |
|
|
artifactsTitle.className = 'file-explorer-section-title'; |
|
|
artifactsTitle.textContent = 'artifacts'; |
|
|
artifactsSection.appendChild(artifactsTitle); |
|
|
|
|
|
|
|
|
const artifactsRoot = document.querySelector('.main-content') || document; |
|
|
const artifacts = artifactsRoot.querySelectorAll('.artifact'); |
|
|
if (artifacts.length === 0) { |
|
|
const noArtifacts = document.createElement('div'); |
|
|
noArtifacts.className = 'file-explorer-item artifact'; |
|
|
noArtifacts.textContent = '(none)'; |
|
|
noArtifacts.style.opacity = '0.5'; |
|
|
artifactsSection.appendChild(noArtifacts); |
|
|
} else { |
|
|
artifacts.forEach(artifact => { |
|
|
const artifactItem = document.createElement('div'); |
|
|
artifactItem.className = 'file-explorer-item artifact'; |
|
|
artifactItem.textContent = artifact.textContent; |
|
|
artifactItem.onclick = function() { |
|
|
artifact.click(); |
|
|
}; |
|
|
artifactsSection.appendChild(artifactItem); |
|
|
}); |
|
|
} |
|
|
|
|
|
fileExplorer.appendChild(artifactsSection); |
|
|
|
|
|
return fileExplorer; |
|
|
} |
|
|
|
|
|
function initStatusWidget() { |
|
|
let el = document.querySelector('.status-widget'); |
|
|
if (!el) { |
|
|
el = document.createElement('div'); |
|
|
el.className = 'status-widget'; |
|
|
el.id = 'status-widget'; |
|
|
el.textContent = 'ready — Esc'; |
|
|
document.body.appendChild(el); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
let _cursorX = 0; |
|
|
let _cursorY = 0; |
|
|
let _cursorVisible = false; |
|
|
|
|
|
function setActiveTool(tool) { |
|
|
if (!tool || tool === 'none') { |
|
|
document.body.dataset.tool = 'none'; |
|
|
localStorage.setItem('uvnote-active-tool', 'none'); |
|
|
setOverlayActive(false); |
|
|
_cursorVisible = false; |
|
|
|
|
|
const toolButtons = document.querySelectorAll('.tools-widget .tool-button'); |
|
|
toolButtons.forEach(btn => btn.classList.remove('active')); |
|
|
updateStateIndicator(); |
|
|
encodeToolStateToUrl(); |
|
|
return; |
|
|
} |
|
|
document.body.dataset.tool = tool; |
|
|
localStorage.setItem('uvnote-active-tool', tool); |
|
|
setOverlayActive(true); |
|
|
_cursorVisible = true; |
|
|
updateStateIndicator(); |
|
|
encodeToolStateToUrl(); |
|
|
} |
|
|
|
|
|
|
|
|
window.setActiveTool = setActiveTool; |
|
|
|
|
|
|
|
|
function getArrowColor() { |
|
|
const saved = localStorage.getItem('uvnote-arrow-color'); |
|
|
if (saved) return saved; |
|
|
return '#e53935'; |
|
|
} |
|
|
|
|
|
function setStoredArrowColor(color) { |
|
|
try { localStorage.setItem('uvnote-arrow-color', color); } catch (_) {} |
|
|
} |
|
|
|
|
|
function getLineThickness() { |
|
|
const saved = localStorage.getItem('uvnote-line-thickness'); |
|
|
if (saved) return parseInt(saved, 10); |
|
|
return 6; |
|
|
} |
|
|
|
|
|
function setStoredLineThickness(thickness) { |
|
|
try { localStorage.setItem('uvnote-line-thickness', thickness); } catch (_) {} |
|
|
} |
|
|
|
|
|
function getFadeoutTime() { |
|
|
const saved = localStorage.getItem('uvnote-fadeout-time'); |
|
|
if (saved) return parseInt(saved, 10); |
|
|
return 5; |
|
|
} |
|
|
|
|
|
function setStoredFadeoutTime(seconds) { |
|
|
try { localStorage.setItem('uvnote-fadeout-time', seconds); } catch (_) {} |
|
|
} |
|
|
|
|
|
function createToolsWidget() { |
|
|
const tools = document.createElement('div'); |
|
|
tools.className = 'tools-widget'; |
|
|
|
|
|
const title = document.createElement('div'); |
|
|
title.className = 'tools-title'; |
|
|
title.textContent = 'tools'; |
|
|
tools.appendChild(title); |
|
|
|
|
|
const row = document.createElement('div'); |
|
|
row.className = 'tools-row'; |
|
|
tools.appendChild(row); |
|
|
|
|
|
|
|
|
const arrowBtn = document.createElement('div'); |
|
|
arrowBtn.className = 'tool-button'; |
|
|
arrowBtn.textContent = 'arrow'; |
|
|
arrowBtn.onclick = function() { |
|
|
const isActive = arrowBtn.classList.contains('active'); |
|
|
if (isActive) { |
|
|
arrowBtn.classList.remove('active'); |
|
|
setActiveTool('none'); |
|
|
} else { |
|
|
tools.querySelectorAll('.tool-button').forEach(b => b.classList.remove('active')); |
|
|
arrowBtn.classList.add('active'); |
|
|
setActiveTool('arrow'); |
|
|
} |
|
|
}; |
|
|
row.appendChild(arrowBtn); |
|
|
|
|
|
|
|
|
const penBtn = document.createElement('div'); |
|
|
penBtn.className = 'tool-button'; |
|
|
penBtn.textContent = 'pen'; |
|
|
penBtn.onclick = function() { |
|
|
const isActive = penBtn.classList.contains('active'); |
|
|
if (isActive) { |
|
|
penBtn.classList.remove('active'); |
|
|
setActiveTool('none'); |
|
|
} else { |
|
|
tools.querySelectorAll('.tool-button').forEach(b => b.classList.remove('active')); |
|
|
penBtn.classList.add('active'); |
|
|
setActiveTool('pen'); |
|
|
} |
|
|
}; |
|
|
row.appendChild(penBtn); |
|
|
|
|
|
|
|
|
const eraseBtn = document.createElement('div'); |
|
|
eraseBtn.className = 'tool-button'; |
|
|
eraseBtn.textContent = 'eraser'; |
|
|
eraseBtn.onclick = function() { |
|
|
const isActive = eraseBtn.classList.contains('active'); |
|
|
if (isActive) { |
|
|
eraseBtn.classList.remove('active'); |
|
|
setActiveTool('none'); |
|
|
} else { |
|
|
tools.querySelectorAll('.tool-button').forEach(b => b.classList.remove('active')); |
|
|
eraseBtn.classList.add('active'); |
|
|
setActiveTool('eraser'); |
|
|
} |
|
|
}; |
|
|
row.appendChild(eraseBtn); |
|
|
|
|
|
|
|
|
const spotlightBtn = document.createElement('div'); |
|
|
spotlightBtn.className = 'tool-button'; |
|
|
spotlightBtn.textContent = 'spotlight'; |
|
|
spotlightBtn.onclick = function() { |
|
|
const isActive = spotlightBtn.classList.contains('active'); |
|
|
if (isActive) { |
|
|
spotlightBtn.classList.remove('active'); |
|
|
setActiveTool('none'); |
|
|
} else { |
|
|
tools.querySelectorAll('.tool-button').forEach(b => b.classList.remove('active')); |
|
|
spotlightBtn.classList.add('active'); |
|
|
setActiveTool('spotlight'); |
|
|
} |
|
|
}; |
|
|
row.appendChild(spotlightBtn); |
|
|
|
|
|
|
|
|
const clearBtn = document.createElement('div'); |
|
|
clearBtn.className = 'tool-button'; |
|
|
clearBtn.textContent = 'clear'; |
|
|
clearBtn.onclick = function() { |
|
|
_shapes = []; |
|
|
saveShapes(); |
|
|
renderOverlay(); |
|
|
}; |
|
|
row.appendChild(clearBtn); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const saved = localStorage.getItem('uvnote-active-tool') || 'none'; |
|
|
if (saved === 'arrow') { |
|
|
arrowBtn.classList.add('active'); |
|
|
setActiveTool('arrow'); |
|
|
} else if (saved === 'pen') { |
|
|
penBtn.classList.add('active'); |
|
|
setActiveTool('pen'); |
|
|
} else if (saved === 'eraser') { |
|
|
eraseBtn.classList.add('active'); |
|
|
setActiveTool('eraser'); |
|
|
} else if (saved === 'spotlight') { |
|
|
spotlightBtn.classList.add('active'); |
|
|
setActiveTool('spotlight'); |
|
|
} |
|
|
|
|
|
|
|
|
const colorTitle = document.createElement('div'); |
|
|
colorTitle.className = 'tools-section-title'; |
|
|
colorTitle.textContent = 'color'; |
|
|
tools.appendChild(colorTitle); |
|
|
|
|
|
const colorRow = document.createElement('div'); |
|
|
colorRow.className = 'tools-row color-row'; |
|
|
tools.appendChild(colorRow); |
|
|
|
|
|
const swatchColors = [ |
|
|
|
|
|
'#e53935', '#fb8c00', '#fdd835', '#43a047', '#1e88e5', '#8e24aa', |
|
|
|
|
|
'#ff5722', '#795548', '#607d8b', '#9c27b0', |
|
|
|
|
|
'#000000', '#424242', '#9e9e9e', '#ffffff' |
|
|
]; |
|
|
const swatches = []; |
|
|
swatchColors.forEach(c => { |
|
|
const s = document.createElement('div'); |
|
|
s.className = 'color-swatch'; |
|
|
s.style.backgroundColor = c; |
|
|
s.title = c; |
|
|
s.onclick = () => { |
|
|
setStoredArrowColor(c); |
|
|
refreshColorUI(c); |
|
|
if (_cursorVisible) renderOverlay(); |
|
|
encodeToolStateToUrl(); |
|
|
}; |
|
|
colorRow.appendChild(s); |
|
|
swatches.push(s); |
|
|
}); |
|
|
|
|
|
const colorInput = document.createElement('input'); |
|
|
colorInput.type = 'color'; |
|
|
colorInput.className = 'color-input'; |
|
|
colorInput.oninput = () => { |
|
|
setStoredArrowColor(colorInput.value); |
|
|
refreshColorUI(colorInput.value); |
|
|
if (_cursorVisible) renderOverlay(); |
|
|
encodeToolStateToUrl(); |
|
|
}; |
|
|
colorRow.appendChild(colorInput); |
|
|
|
|
|
function refreshColorUI(selected) { |
|
|
const selectedHex = selected.startsWith('#') ? selected.toLowerCase() : rgbToHex(selected); |
|
|
|
|
|
swatches.forEach((s, i) => { |
|
|
const swatchHex = swatchColors[i].toLowerCase(); |
|
|
if (swatchHex === selectedHex) { |
|
|
s.classList.add('selected'); |
|
|
} else { |
|
|
s.classList.remove('selected'); |
|
|
} |
|
|
}); |
|
|
|
|
|
try { |
|
|
colorInput.value = selectedHex; |
|
|
} catch (_) {} |
|
|
} |
|
|
|
|
|
function rgbToHex(rgb) { |
|
|
const m = rgb.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)\)/i); |
|
|
if (!m) return '#000000'; |
|
|
const r = parseInt(m[1]).toString(16).padStart(2, '0'); |
|
|
const g = parseInt(m[2]).toString(16).padStart(2, '0'); |
|
|
const b = parseInt(m[3]).toString(16).padStart(2, '0'); |
|
|
return `#${r}${g}${b}`; |
|
|
} |
|
|
|
|
|
|
|
|
refreshColorUI(getArrowColor()); |
|
|
|
|
|
|
|
|
const thicknessTitle = document.createElement('div'); |
|
|
thicknessTitle.className = 'tools-section-title'; |
|
|
thicknessTitle.textContent = 'thickness'; |
|
|
tools.appendChild(thicknessTitle); |
|
|
|
|
|
const thicknessRow = document.createElement('div'); |
|
|
thicknessRow.className = 'thickness-row'; |
|
|
tools.appendChild(thicknessRow); |
|
|
|
|
|
const thicknessSlider = document.createElement('input'); |
|
|
thicknessSlider.type = 'range'; |
|
|
thicknessSlider.className = 'thickness-slider'; |
|
|
thicknessSlider.min = '1'; |
|
|
thicknessSlider.max = '10'; |
|
|
thicknessSlider.value = getLineThickness(); |
|
|
|
|
|
const thicknessValue = document.createElement('span'); |
|
|
thicknessValue.className = 'thickness-value'; |
|
|
thicknessValue.textContent = thicknessSlider.value + 'px'; |
|
|
|
|
|
thicknessSlider.oninput = function() { |
|
|
const value = parseInt(thicknessSlider.value, 10); |
|
|
setStoredLineThickness(value); |
|
|
thicknessValue.textContent = value + 'px'; |
|
|
if (_cursorVisible) renderOverlay(); |
|
|
encodeToolStateToUrl(); |
|
|
}; |
|
|
|
|
|
thicknessRow.appendChild(thicknessSlider); |
|
|
thicknessRow.appendChild(thicknessValue); |
|
|
|
|
|
|
|
|
const fadeoutTitle = document.createElement('div'); |
|
|
fadeoutTitle.className = 'tools-section-title'; |
|
|
fadeoutTitle.textContent = 'fadeout time'; |
|
|
tools.appendChild(fadeoutTitle); |
|
|
|
|
|
const fadeoutRow = document.createElement('div'); |
|
|
fadeoutRow.className = 'thickness-row'; |
|
|
tools.appendChild(fadeoutRow); |
|
|
|
|
|
const fadeoutSlider = document.createElement('input'); |
|
|
fadeoutSlider.type = 'range'; |
|
|
fadeoutSlider.className = 'thickness-slider'; |
|
|
fadeoutSlider.min = '0'; |
|
|
fadeoutSlider.max = '30'; |
|
|
fadeoutSlider.value = getFadeoutTime(); |
|
|
|
|
|
const fadeoutValue = document.createElement('span'); |
|
|
fadeoutValue.className = 'thickness-value'; |
|
|
fadeoutValue.textContent = fadeoutSlider.value === '0' ? 'never' : fadeoutSlider.value + 's'; |
|
|
|
|
|
fadeoutSlider.oninput = function() { |
|
|
const value = parseInt(fadeoutSlider.value, 10); |
|
|
setStoredFadeoutTime(value); |
|
|
fadeoutValue.textContent = value === 0 ? 'never' : value + 's'; |
|
|
encodeToolStateToUrl(); |
|
|
}; |
|
|
|
|
|
fadeoutRow.appendChild(fadeoutSlider); |
|
|
fadeoutRow.appendChild(fadeoutValue); |
|
|
|
|
|
|
|
|
makeDraggable(tools, 'uvnote-tools-pos', title); |
|
|
|
|
|
return tools; |
|
|
} |
|
|
|
|
|
function initTools() { |
|
|
const widget = createToolsWidget(); |
|
|
document.body.appendChild(widget); |
|
|
} |
|
|
|
|
|
function teardownTools() { |
|
|
const w = document.querySelector('.tools-widget'); |
|
|
if (w && w.parentNode) w.parentNode.removeChild(w); |
|
|
} |
|
|
|
|
|
|
|
|
let _overlay = null; |
|
|
let _overlayCtx = null; |
|
|
let _overlayContainer = null; |
|
|
let _overlayMode = 'single'; |
|
|
let _overlayResizeHandler = null; |
|
|
let _overlayScrollHandler = null; |
|
|
let _drawing = null; |
|
|
let _shapes = []; |
|
|
let _fadeTimer = null; |
|
|
let _urlLoadedTool = false; |
|
|
let _isInitializing = true; |
|
|
let _initialCellStates = {}; |
|
|
|
|
|
function getOverlayStorageKey() { return 'uvnote-shapes'; } |
|
|
|
|
|
function loadShapes() { |
|
|
try { |
|
|
const raw = localStorage.getItem(getOverlayStorageKey()); |
|
|
_shapes = raw ? JSON.parse(raw) : []; |
|
|
} catch (_) { _shapes = []; } |
|
|
} |
|
|
|
|
|
function saveShapes() { |
|
|
try { |
|
|
localStorage.setItem(getOverlayStorageKey(), JSON.stringify(_shapes)); |
|
|
|
|
|
encodeToolStateToUrl(); |
|
|
} catch (_) {} |
|
|
} |
|
|
|
|
|
function updateShapesFade() { |
|
|
const now = Date.now(); |
|
|
let needsUpdate = false; |
|
|
|
|
|
for (let i = _shapes.length - 1; i >= 0; i--) { |
|
|
const shape = _shapes[i]; |
|
|
if (!shape.createdAt) continue; |
|
|
|
|
|
|
|
|
const shapesFadeoutSeconds = shape.fadeoutTime !== undefined ? shape.fadeoutTime : getFadeoutTime(); |
|
|
|
|
|
|
|
|
if (shapesFadeoutSeconds === 0) continue; |
|
|
|
|
|
const fadeStartTime = Math.max(0, (shapesFadeoutSeconds - 2) * 1000); |
|
|
const fadeEndTime = shapesFadeoutSeconds * 1000; |
|
|
const age = now - shape.createdAt; |
|
|
|
|
|
if (age >= fadeEndTime) { |
|
|
|
|
|
_shapes.splice(i, 1); |
|
|
needsUpdate = true; |
|
|
} else if (age >= fadeStartTime) { |
|
|
|
|
|
const fadeProgress = (age - fadeStartTime) / (fadeEndTime - fadeStartTime); |
|
|
const newOpacity = 1 - fadeProgress; |
|
|
if (Math.abs(shape.opacity - newOpacity) > 0.01) { |
|
|
shape.opacity = newOpacity; |
|
|
needsUpdate = true; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
if (needsUpdate) { |
|
|
saveShapes(); |
|
|
renderOverlay(); |
|
|
|
|
|
encodeToolStateToUrl(); |
|
|
} |
|
|
} |
|
|
|
|
|
function getContentContainer() { return window; } |
|
|
|
|
|
function updateOverlayModeAndContainer() { |
|
|
_overlayContainer = window; |
|
|
_overlayMode = 'single'; |
|
|
} |
|
|
|
|
|
function updateOverlayBounds() { |
|
|
if (!_overlay) return; |
|
|
if (_overlayContainer === window) { |
|
|
_overlay.style.position = 'fixed'; |
|
|
_overlay.style.left = '0px'; |
|
|
_overlay.style.top = '0px'; |
|
|
_overlay.width = window.innerWidth; |
|
|
_overlay.height = window.innerHeight; |
|
|
} else { |
|
|
const rect = _overlayContainer.getBoundingClientRect(); |
|
|
_overlay.style.position = 'fixed'; |
|
|
_overlay.style.left = rect.left + 'px'; |
|
|
_overlay.style.top = rect.top + 'px'; |
|
|
_overlay.width = Math.max(0, Math.floor(rect.width)); |
|
|
_overlay.height = Math.max(0, Math.floor(rect.height)); |
|
|
} |
|
|
renderOverlay(); |
|
|
} |
|
|
|
|
|
function containerScrollLeft() { |
|
|
return (_overlayContainer === window) ? (window.scrollX || 0) : (_overlayContainer.scrollLeft || 0); |
|
|
} |
|
|
function containerScrollTop() { |
|
|
return (_overlayContainer === window) ? (window.scrollY || 0) : (_overlayContainer.scrollTop || 0); |
|
|
} |
|
|
|
|
|
function toCanvasCoords(clientX, clientY) { |
|
|
const rect = _overlay.getBoundingClientRect(); |
|
|
return { x: clientX - rect.left, y: clientY - rect.top }; |
|
|
} |
|
|
|
|
|
function onPointerDown(e) { |
|
|
const tool = document.body.dataset.tool; |
|
|
if (tool === 'arrow') { |
|
|
startDrawArrow(e); |
|
|
} else if (tool === 'pen') { |
|
|
startDrawPen(e); |
|
|
} else if (tool === 'eraser') { |
|
|
eraseAt(e); |
|
|
} else if (tool === 'spotlight') { |
|
|
startDrawSpotlight(e); |
|
|
} |
|
|
} |
|
|
|
|
|
function onPointerMove(e) { |
|
|
|
|
|
const pt = toCanvasCoords(e.touches ? e.touches[0].clientX : e.clientX, e.touches ? e.touches[0].clientY : e.clientY); |
|
|
_cursorX = pt.x; |
|
|
_cursorY = pt.y; |
|
|
|
|
|
if (!_drawing) { |
|
|
|
|
|
if (_cursorVisible) { |
|
|
renderOverlay(); |
|
|
} |
|
|
return; |
|
|
} |
|
|
|
|
|
if (_drawing.type === 'pen') { |
|
|
moveDrawPen(e); |
|
|
} else if (_drawing.type === 'spotlight') { |
|
|
moveDrawSpotlight(e); |
|
|
} else { |
|
|
moveDrawArrow(e); |
|
|
} |
|
|
} |
|
|
|
|
|
function onPointerEnter(e) { |
|
|
_cursorVisible = document.body.dataset.tool !== 'none'; |
|
|
if (_cursorVisible) { |
|
|
renderOverlay(); |
|
|
} |
|
|
} |
|
|
|
|
|
function onPointerLeave(e) { |
|
|
_cursorVisible = false; |
|
|
renderOverlay(); |
|
|
} |
|
|
|
|
|
function onPointerUp(e) { |
|
|
if (!_drawing) return; |
|
|
if (_drawing.type === 'pen') { |
|
|
endDrawPen(); |
|
|
} else if (_drawing.type === 'spotlight') { |
|
|
endDrawSpotlight(); |
|
|
} else { |
|
|
endDrawArrow(); |
|
|
} |
|
|
} |
|
|
|
|
|
function startDrawArrow(e) { |
|
|
if (document.body.dataset.tool !== 'arrow') return; |
|
|
const pt = toCanvasCoords(e.touches ? e.touches[0].clientX : e.clientX, e.touches ? e.touches[0].clientY : e.clientY); |
|
|
_drawing = { |
|
|
x1: pt.x + containerScrollLeft(), |
|
|
y1: pt.y + containerScrollTop(), |
|
|
x2: pt.x + containerScrollLeft(), |
|
|
y2: pt.y + containerScrollTop(), |
|
|
color: getArrowColor(), |
|
|
width: getLineThickness() |
|
|
}; |
|
|
renderOverlay(); |
|
|
e.preventDefault(); |
|
|
} |
|
|
|
|
|
function moveDrawArrow(e) { |
|
|
if (!_drawing) return; |
|
|
const pt = toCanvasCoords(e.touches ? e.touches[0].clientX : e.clientX, e.touches ? e.touches[0].clientY : e.clientY); |
|
|
_drawing.x2 = pt.x + containerScrollLeft(); |
|
|
_drawing.y2 = pt.y + containerScrollTop(); |
|
|
renderOverlay(); |
|
|
e.preventDefault(); |
|
|
} |
|
|
|
|
|
function endDrawArrow() { |
|
|
if (!_drawing) return; |
|
|
_shapes.push({ |
|
|
type: 'arrow', |
|
|
..._drawing, |
|
|
createdAt: Date.now(), |
|
|
fadeoutTime: getFadeoutTime(), |
|
|
opacity: 1.0 |
|
|
}); |
|
|
_drawing = null; |
|
|
saveShapes(); |
|
|
renderOverlay(); |
|
|
} |
|
|
|
|
|
function startDrawPen(e) { |
|
|
if (document.body.dataset.tool !== 'pen') return; |
|
|
const pt = toCanvasCoords(e.touches ? e.touches[0].clientX : e.clientX, e.touches ? e.touches[0].clientY : e.clientY); |
|
|
_drawing = { |
|
|
type: 'pen', |
|
|
points: [{ |
|
|
x: pt.x + containerScrollLeft(), |
|
|
y: pt.y + containerScrollTop() |
|
|
}], |
|
|
color: getArrowColor(), |
|
|
width: getLineThickness() |
|
|
}; |
|
|
renderOverlay(); |
|
|
e.preventDefault(); |
|
|
} |
|
|
|
|
|
function moveDrawPen(e) { |
|
|
if (!_drawing || _drawing.type !== 'pen') return; |
|
|
const pt = toCanvasCoords(e.touches ? e.touches[0].clientX : e.clientX, e.touches ? e.touches[0].clientY : e.clientY); |
|
|
_drawing.points.push({ |
|
|
x: pt.x + containerScrollLeft(), |
|
|
y: pt.y + containerScrollTop() |
|
|
}); |
|
|
renderOverlay(); |
|
|
e.preventDefault(); |
|
|
} |
|
|
|
|
|
function endDrawPen() { |
|
|
if (!_drawing || _drawing.type !== 'pen') return; |
|
|
if (_drawing.points.length > 1) { |
|
|
_shapes.push({ |
|
|
..._drawing, |
|
|
createdAt: Date.now(), |
|
|
fadeoutTime: getFadeoutTime(), |
|
|
opacity: 1.0 |
|
|
}); |
|
|
} |
|
|
_drawing = null; |
|
|
saveShapes(); |
|
|
renderOverlay(); |
|
|
} |
|
|
|
|
|
function startDrawSpotlight(e) { |
|
|
if (document.body.dataset.tool !== 'spotlight') return; |
|
|
const pt = toCanvasCoords(e.touches ? e.touches[0].clientX : e.clientX, e.touches ? e.touches[0].clientY : e.clientY); |
|
|
_drawing = { |
|
|
type: 'spotlight', |
|
|
x: pt.x + containerScrollLeft(), |
|
|
y: pt.y + containerScrollTop(), |
|
|
radius: getLineThickness() * 20, |
|
|
color: getArrowColor() |
|
|
}; |
|
|
renderOverlay(); |
|
|
e.preventDefault(); |
|
|
} |
|
|
|
|
|
function moveDrawSpotlight(e) { |
|
|
if (!_drawing || _drawing.type !== 'spotlight') return; |
|
|
const pt = toCanvasCoords(e.touches ? e.touches[0].clientX : e.clientX, e.touches ? e.touches[0].clientY : e.clientY); |
|
|
const dx = pt.x + containerScrollLeft() - _drawing.x; |
|
|
const dy = pt.y + containerScrollTop() - _drawing.y; |
|
|
_drawing.radius = Math.max(20, Math.sqrt(dx * dx + dy * dy)); |
|
|
renderOverlay(); |
|
|
e.preventDefault(); |
|
|
} |
|
|
|
|
|
function endDrawSpotlight() { |
|
|
if (!_drawing || _drawing.type !== 'spotlight') return; |
|
|
_shapes.push({ |
|
|
..._drawing, |
|
|
createdAt: Date.now(), |
|
|
fadeoutTime: getFadeoutTime(), |
|
|
opacity: 1.0 |
|
|
}); |
|
|
_drawing = null; |
|
|
saveShapes(); |
|
|
renderOverlay(); |
|
|
} |
|
|
|
|
|
function distPointToSegment(px, py, x1, y1, x2, y2) { |
|
|
const dx = x2 - x1, dy = y2 - y1; |
|
|
if (dx === 0 && dy === 0) return Math.hypot(px - x1, py - y1); |
|
|
const t = Math.max(0, Math.min(1, ((px - x1) * dx + (py - y1) * dy) / (dx*dx + dy*dy))); |
|
|
const cx = x1 + t * dx, cy = y1 + t * dy; |
|
|
return Math.hypot(px - cx, py - cy); |
|
|
} |
|
|
|
|
|
function eraseAt(e) { |
|
|
const pt = toCanvasCoords(e.touches ? e.touches[0].clientX : e.clientX, e.touches ? e.touches[0].clientY : e.clientY); |
|
|
const x = pt.x + containerScrollLeft(); |
|
|
const y = pt.y + containerScrollTop(); |
|
|
const threshold = 10; |
|
|
for (let i = _shapes.length - 1; i >= 0; i--) { |
|
|
const s = _shapes[i]; |
|
|
if (s.type === 'arrow') { |
|
|
const d = distPointToSegment(x, y, s.x1, s.y1, s.x2, s.y2); |
|
|
if (d <= threshold) { |
|
|
_shapes.splice(i, 1); |
|
|
saveShapes(); |
|
|
renderOverlay(); |
|
|
break; |
|
|
} |
|
|
} else if (s.type === 'pen' && s.points) { |
|
|
|
|
|
let minDist = Infinity; |
|
|
for (let j = 1; j < s.points.length; j++) { |
|
|
const d = distPointToSegment(x, y, s.points[j-1].x, s.points[j-1].y, s.points[j].x, s.points[j].y); |
|
|
minDist = Math.min(minDist, d); |
|
|
} |
|
|
if (minDist <= threshold) { |
|
|
_shapes.splice(i, 1); |
|
|
saveShapes(); |
|
|
renderOverlay(); |
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
e.preventDefault(); |
|
|
} |
|
|
|
|
|
function drawArrow(ctx, x1, y1, x2, y2, color, width, opacity = 1.0) { |
|
|
|
|
|
const oldAlpha = ctx.globalAlpha; |
|
|
ctx.globalAlpha = opacity; |
|
|
|
|
|
ctx.strokeStyle = color; |
|
|
ctx.fillStyle = color; |
|
|
ctx.lineWidth = width; |
|
|
ctx.lineCap = 'round'; |
|
|
ctx.lineJoin = 'round'; |
|
|
|
|
|
|
|
|
const dx = x2 - x1; |
|
|
const dy = y2 - y1; |
|
|
const distance = Math.sqrt(dx * dx + dy * dy); |
|
|
|
|
|
if (distance < 5) { |
|
|
|
|
|
const defaultAngle = Math.PI / 4; |
|
|
const headLength = Math.min(15 + width * 1.5, 25); |
|
|
const headAngle = Math.PI / 6; |
|
|
|
|
|
|
|
|
const hx1 = x1 + headLength * Math.cos(defaultAngle - headAngle); |
|
|
const hy1 = y1 + headLength * Math.sin(defaultAngle - headAngle); |
|
|
const hx2 = x1 + headLength * Math.cos(defaultAngle + headAngle); |
|
|
const hy2 = y1 + headLength * Math.sin(defaultAngle + headAngle); |
|
|
|
|
|
|
|
|
ctx.beginPath(); |
|
|
ctx.moveTo(x1, y1); |
|
|
ctx.lineTo(hx1, hy1); |
|
|
ctx.lineTo(hx2, hy2); |
|
|
ctx.closePath(); |
|
|
ctx.fill(); |
|
|
} else { |
|
|
|
|
|
const angle = Math.atan2(y1 - y2, x1 - x2); |
|
|
const headLength = Math.min(15 + width * 1.5, 25); |
|
|
const headAngle = Math.PI / 6; |
|
|
|
|
|
|
|
|
const lineEndX = x1 - headLength * 0.8 * Math.cos(angle); |
|
|
const lineEndY = y1 - headLength * 0.8 * Math.sin(angle); |
|
|
|
|
|
|
|
|
ctx.beginPath(); |
|
|
ctx.moveTo(x2, y2); |
|
|
ctx.lineTo(lineEndX, lineEndY); |
|
|
ctx.stroke(); |
|
|
|
|
|
|
|
|
const hx1 = x1 - headLength * Math.cos(angle - headAngle); |
|
|
const hy1 = y1 - headLength * Math.sin(angle - headAngle); |
|
|
const hx2 = x1 - headLength * Math.cos(angle + headAngle); |
|
|
const hy2 = y1 - headLength * Math.sin(angle + headAngle); |
|
|
|
|
|
|
|
|
ctx.beginPath(); |
|
|
ctx.moveTo(x1, y1); |
|
|
ctx.lineTo(hx1, hy1); |
|
|
ctx.lineTo(hx2, hy2); |
|
|
ctx.closePath(); |
|
|
ctx.fill(); |
|
|
} |
|
|
|
|
|
|
|
|
ctx.globalAlpha = oldAlpha; |
|
|
} |
|
|
|
|
|
function drawPen(ctx, points, color, width, offX, offY, opacity = 1.0) { |
|
|
if (!points || points.length < 2) return; |
|
|
|
|
|
|
|
|
const oldAlpha = ctx.globalAlpha; |
|
|
ctx.globalAlpha = opacity; |
|
|
|
|
|
ctx.strokeStyle = color; |
|
|
ctx.lineWidth = width; |
|
|
ctx.lineCap = 'round'; |
|
|
ctx.lineJoin = 'round'; |
|
|
ctx.beginPath(); |
|
|
ctx.moveTo(points[0].x - offX, points[0].y - offY); |
|
|
for (let i = 1; i < points.length; i++) { |
|
|
ctx.lineTo(points[i].x - offX, points[i].y - offY); |
|
|
} |
|
|
ctx.stroke(); |
|
|
|
|
|
|
|
|
ctx.globalAlpha = oldAlpha; |
|
|
} |
|
|
|
|
|
function drawAllSpotlights(ctx, spotlights, offX, offY) { |
|
|
if (!spotlights || spotlights.length === 0) return; |
|
|
|
|
|
ctx.save(); |
|
|
|
|
|
|
|
|
const maxOpacity = Math.max(...spotlights.map(s => s.opacity || 1.0)); |
|
|
|
|
|
|
|
|
ctx.fillStyle = `rgba(0, 0, 0, ${0.7 * maxOpacity})`; |
|
|
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height); |
|
|
|
|
|
|
|
|
ctx.globalCompositeOperation = 'destination-out'; |
|
|
ctx.fillStyle = 'rgba(0, 0, 0, 1)'; |
|
|
for (const spotlight of spotlights) { |
|
|
ctx.beginPath(); |
|
|
ctx.arc(spotlight.x - offX, spotlight.y - offY, spotlight.radius, 0, 2 * Math.PI); |
|
|
ctx.fill(); |
|
|
} |
|
|
|
|
|
ctx.restore(); |
|
|
} |
|
|
|
|
|
function renderOverlay() { |
|
|
if (!_overlay || !_overlayCtx) return; |
|
|
_overlayCtx.clearRect(0, 0, _overlay.width, _overlay.height); |
|
|
const offX = containerScrollLeft(); |
|
|
const offY = containerScrollTop(); |
|
|
|
|
|
for (const s of _shapes) { |
|
|
const opacity = s.opacity !== undefined ? s.opacity : 1.0; |
|
|
if (s.type === 'arrow') { |
|
|
drawArrow(_overlayCtx, s.x1 - offX, s.y1 - offY, s.x2 - offX, s.y2 - offY, s.color || '#f00', s.width || 2, opacity); |
|
|
} else if (s.type === 'pen') { |
|
|
drawPen(_overlayCtx, s.points, s.color || '#f00', s.width || 2, offX, offY, opacity); |
|
|
} |
|
|
} |
|
|
|
|
|
if (_drawing) { |
|
|
if (_drawing.type === 'pen') { |
|
|
drawPen(_overlayCtx, _drawing.points, _drawing.color, _drawing.width, offX, offY); |
|
|
} else if (_drawing.type !== 'spotlight') { |
|
|
drawArrow(_overlayCtx, _drawing.x1 - offX, _drawing.y1 - offY, _drawing.x2 - offX, _drawing.y2 - offY, _drawing.color, _drawing.width); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const spotlights = []; |
|
|
|
|
|
|
|
|
for (const s of _shapes) { |
|
|
if (s.type === 'spotlight') { |
|
|
spotlights.push({ |
|
|
x: s.x, |
|
|
y: s.y, |
|
|
radius: s.radius, |
|
|
opacity: s.opacity !== undefined ? s.opacity : 1.0 |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (_drawing && _drawing.type === 'spotlight') { |
|
|
spotlights.push({ |
|
|
x: _drawing.x, |
|
|
y: _drawing.y, |
|
|
radius: _drawing.radius, |
|
|
opacity: 1.0 |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
if (_cursorVisible && !_drawing) { |
|
|
const tool = document.body.dataset.tool; |
|
|
if (tool === 'spotlight') { |
|
|
const thickness = getLineThickness(); |
|
|
const radius = thickness * 20; |
|
|
const cursorWorldX = _cursorX + containerScrollLeft(); |
|
|
const cursorWorldY = _cursorY + containerScrollTop(); |
|
|
spotlights.push({ |
|
|
x: cursorWorldX, |
|
|
y: cursorWorldY, |
|
|
radius: radius, |
|
|
opacity: 0.8 |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
drawAllSpotlights(_overlayCtx, spotlights, offX, offY); |
|
|
|
|
|
|
|
|
if (_cursorVisible && !_drawing) { |
|
|
const tool = document.body.dataset.tool; |
|
|
const color = getArrowColor(); |
|
|
const thickness = getLineThickness(); |
|
|
|
|
|
if (tool !== 'spotlight') { |
|
|
_overlayCtx.save(); |
|
|
_overlayCtx.fillStyle = color; |
|
|
_overlayCtx.globalAlpha = 0.7; |
|
|
|
|
|
if (tool === 'eraser') { |
|
|
|
|
|
_overlayCtx.strokeStyle = color; |
|
|
_overlayCtx.lineWidth = 2; |
|
|
_overlayCtx.beginPath(); |
|
|
_overlayCtx.arc(_cursorX, _cursorY, 10, 0, 2 * Math.PI); |
|
|
_overlayCtx.stroke(); |
|
|
} else { |
|
|
|
|
|
_overlayCtx.beginPath(); |
|
|
_overlayCtx.arc(_cursorX, _cursorY, thickness / 2, 0, 2 * Math.PI); |
|
|
_overlayCtx.fill(); |
|
|
} |
|
|
|
|
|
_overlayCtx.restore(); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
function setOverlayActive(active) { |
|
|
if (!_overlay) initOverlay(); |
|
|
_overlay.style.pointerEvents = active ? 'auto' : 'none'; |
|
|
_overlay.style.cursor = active ? 'none' : 'auto'; |
|
|
|
|
|
renderOverlay(); |
|
|
} |
|
|
|
|
|
function initOverlay() { |
|
|
if (_overlay) return; |
|
|
updateOverlayModeAndContainer(); |
|
|
_overlay = document.createElement('canvas'); |
|
|
_overlay.className = 'draw-overlay'; |
|
|
_overlayCtx = _overlay.getContext('2d'); |
|
|
document.body.appendChild(_overlay); |
|
|
updateOverlayBounds(); |
|
|
loadShapes(); |
|
|
renderOverlay(); |
|
|
|
|
|
|
|
|
_overlay.addEventListener('mousedown', onPointerDown); |
|
|
_overlay.addEventListener('mousemove', onPointerMove); |
|
|
_overlay.addEventListener('mouseenter', onPointerEnter); |
|
|
_overlay.addEventListener('mouseleave', onPointerLeave); |
|
|
document.addEventListener('mouseup', onPointerUp); |
|
|
_overlay.addEventListener('touchstart', onPointerDown, { passive: false }); |
|
|
_overlay.addEventListener('touchmove', onPointerMove, { passive: false }); |
|
|
document.addEventListener('touchend', onPointerUp); |
|
|
|
|
|
_overlayResizeHandler = () => updateOverlayBounds(); |
|
|
window.addEventListener('resize', _overlayResizeHandler); |
|
|
|
|
|
_overlayScrollHandler = () => renderOverlay(); |
|
|
window.addEventListener('scroll', _overlayScrollHandler); |
|
|
|
|
|
|
|
|
_fadeTimer = setInterval(updateShapesFade, 100); |
|
|
} |
|
|
|
|
|
function rebindOverlayContainer() { |
|
|
if (!_overlay) return; |
|
|
|
|
|
if (_overlayScrollHandler) { window.removeEventListener('scroll', _overlayScrollHandler); } |
|
|
updateOverlayModeAndContainer(); |
|
|
updateOverlayBounds(); |
|
|
loadShapes(); |
|
|
renderOverlay(); |
|
|
_overlayScrollHandler = () => renderOverlay(); |
|
|
window.addEventListener('scroll', _overlayScrollHandler); |
|
|
} |
|
|
|
|
|
function teardownOverlay() { |
|
|
if (!_overlay) return; |
|
|
_overlay.removeEventListener('mousedown', onPointerDown); |
|
|
_overlay.removeEventListener('mousemove', onPointerMove); |
|
|
_overlay.removeEventListener('mouseenter', onPointerEnter); |
|
|
_overlay.removeEventListener('mouseleave', onPointerLeave); |
|
|
document.removeEventListener('mouseup', onPointerUp); |
|
|
_overlay.removeEventListener('touchstart', onPointerDown); |
|
|
_overlay.removeEventListener('touchmove', onPointerMove); |
|
|
document.removeEventListener('touchend', onPointerUp); |
|
|
if (_overlayResizeHandler) window.removeEventListener('resize', _overlayResizeHandler); |
|
|
if (_overlayScrollHandler) { |
|
|
if (_overlayContainer === window) { |
|
|
window.removeEventListener('scroll', _overlayScrollHandler); |
|
|
} else if (_overlayContainer) { |
|
|
_overlayContainer.removeEventListener('scroll', _overlayScrollHandler); |
|
|
} |
|
|
} |
|
|
if (_fadeTimer) { |
|
|
clearInterval(_fadeTimer); |
|
|
_fadeTimer = null; |
|
|
} |
|
|
if (_overlay.parentNode) _overlay.parentNode.removeChild(_overlay); |
|
|
_overlay = null; _overlayCtx = null; _overlayContainer = null; _overlayResizeHandler = null; _overlayScrollHandler = null; _drawing = null; |
|
|
} |
|
|
|
|
|
function teardownFileExplorer() { |
|
|
const fe = document.querySelector('.file-explorer'); |
|
|
if (fe && fe.parentNode) fe.parentNode.removeChild(fe); |
|
|
} |
|
|
|
|
|
function escapeHtml(text) { |
|
|
const div = document.createElement('div'); |
|
|
div.textContent = text; |
|
|
return div.innerHTML; |
|
|
} |
|
|
|
|
|
function runCell(cellId){ |
|
|
const btn=document.querySelector('.run-btn[onclick*="'+cellId+'"]'); |
|
|
const output=document.getElementById('output-'+cellId); |
|
|
if(btn){btn.textContent='⏳ running...';btn.disabled=true;} |
|
|
if(output){output.classList.add('output-stale');} |
|
|
fetch('/run/'+cellId,{method:'POST'}).then(r=>r.json()).then(data=>{ |
|
|
if(output){ |
|
|
output.classList.remove('output-stale'); |
|
|
let html=''; |
|
|
if (data.stdout) { |
|
|
html += '<div class="cell-stdout"><pre class="stdout-text">' |
|
|
+ escapeHtml(data.stdout) |
|
|
+ '</pre></div>'; |
|
|
} |
|
|
|
|
|
console.log('UV Logs:', data); |
|
|
if(data.stderr) { |
|
|
|
|
|
const lines = data.stderr.split('\\n'); |
|
|
let uvLogs = []; |
|
|
let regularLogs = []; |
|
|
let inUvSection = true; |
|
|
|
|
|
for (const line of lines) { |
|
|
if (inUvSection) { |
|
|
uvLogs.push(line); |
|
|
if (line.startsWith('Installed ')) { |
|
|
inUvSection = false; |
|
|
} |
|
|
} else { |
|
|
regularLogs.push(line); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (inUvSection) { |
|
|
html+='<div class="cell-stderr">'+escapeHtml(data.stderr)+'</div>'; |
|
|
} else { |
|
|
const uvLogsStr = uvLogs.join('\\n'); |
|
|
const regularLogsStr = regularLogs.join('\\n').trim(); |
|
|
|
|
|
if (uvLogsStr) { |
|
|
html+='<div class="uv-install-logs">'; |
|
|
html+='<div class="uv-logs-header" onclick="toggleUvLogs(this)">▶ UV Install Logs</div>'; |
|
|
html+='<div class="uv-logs-content" style="display: none;">'+escapeHtml(uvLogsStr)+'</div>'; |
|
|
html+='</div>'; |
|
|
} |
|
|
if (regularLogsStr) { |
|
|
html+='<div class="cell-stderr">'+escapeHtml(regularLogsStr)+'</div>'; |
|
|
} |
|
|
} |
|
|
} |
|
|
output.innerHTML=html; |
|
|
} |
|
|
if(btn){btn.textContent='▶ run';btn.disabled=false;} |
|
|
}).catch(e=>{ |
|
|
console.error('Run failed:',e); |
|
|
if(output){output.classList.remove('output-stale');} |
|
|
if(btn){btn.textContent='▶ run';btn.disabled=false;} |
|
|
}); |
|
|
} |
|
|
|
|
|
function copyCell(cellId){ |
|
|
|
|
|
|
|
|
let codeElement = document.querySelector('#code-'+cellId+' .highlight pre'); |
|
|
if (!codeElement) { |
|
|
codeElement = document.querySelector('#code-'+cellId+' pre'); |
|
|
} |
|
|
if (!codeElement) { |
|
|
codeElement = document.querySelector('#code-'+cellId+' code'); |
|
|
} |
|
|
if (!codeElement) { |
|
|
|
|
|
codeElement = document.getElementById('code-'+cellId); |
|
|
} |
|
|
|
|
|
const btn = document.querySelector('.copy-btn[onclick*="'+cellId+'"]'); |
|
|
|
|
|
if (!codeElement) { |
|
|
console.error('Code element not found for cell:', cellId); |
|
|
return; |
|
|
} |
|
|
if (!btn) { |
|
|
console.error('Copy button not found for cell:', cellId); |
|
|
return; |
|
|
} |
|
|
|
|
|
const codeText = codeElement.textContent; |
|
|
|
|
|
if (navigator.clipboard && navigator.clipboard.writeText) { |
|
|
navigator.clipboard.writeText(codeText).then(function() { |
|
|
console.log('Clipboard copy successful'); |
|
|
btn.textContent = '✓ Copied!'; |
|
|
btn.classList.add('copied'); |
|
|
setTimeout(function() { |
|
|
btn.textContent = 'Copy'; |
|
|
btn.classList.remove('copied'); |
|
|
}, 2000); |
|
|
}).catch(function(err) { |
|
|
console.warn('Clipboard copy failed:', err); |
|
|
fallbackCopy(); |
|
|
}); |
|
|
} else { |
|
|
console.log('Using fallback copy method'); |
|
|
fallbackCopy(); |
|
|
} |
|
|
|
|
|
function fallbackCopy() { |
|
|
const textarea = document.createElement('textarea'); |
|
|
textarea.value = codeText; |
|
|
textarea.style.position = 'absolute'; |
|
|
textarea.style.left = '-9999px'; |
|
|
document.body.appendChild(textarea); |
|
|
textarea.select(); |
|
|
try { |
|
|
const success = document.execCommand('copy'); |
|
|
console.log('Fallback copy success:', success); |
|
|
btn.textContent = '✓ Copied!'; |
|
|
btn.classList.add('copied'); |
|
|
setTimeout(function() { |
|
|
btn.textContent = 'Copy'; |
|
|
btn.classList.remove('copied'); |
|
|
}, 2000); |
|
|
} catch (err) { |
|
|
console.error('Fallback copy failed:', err); |
|
|
btn.textContent = 'Copy failed'; |
|
|
setTimeout(function() { |
|
|
btn.textContent = 'Copy'; |
|
|
}, 2000); |
|
|
} |
|
|
document.body.removeChild(textarea); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function() { |
|
|
|
|
|
captureInitialCellStates(); |
|
|
|
|
|
updateThemeIcon(); |
|
|
updateUiMenu(); |
|
|
updateUiDebug(); |
|
|
const widgetsEnabled = (document.documentElement.getAttribute('data-widgets') || 'on') === 'on'; |
|
|
if (widgetsEnabled) { |
|
|
initMinimap(); |
|
|
initFileExplorer(); |
|
|
initTools(); |
|
|
initOverlay(); |
|
|
initStatusWidget(); |
|
|
initializeWidgetVisibility(); |
|
|
layoutWidgetsStackedBottomRight(); |
|
|
window.addEventListener('resize', layoutWidgetsStackedBottomRight); |
|
|
} |
|
|
|
|
|
|
|
|
applyLocationFromUrl(); |
|
|
updateStateIndicator(); |
|
|
|
|
|
|
|
|
const url = new URL(window.location.href); |
|
|
const encodedCellStates = url.searchParams.get('cells'); |
|
|
if (encodedCellStates) { |
|
|
console.log('Applying cell states from URL...'); |
|
|
const cellStates = decodeCellStatesFromUrl(encodedCellStates); |
|
|
|
|
|
|
|
|
requestAnimationFrame(() => { |
|
|
applyCellStatesFromUrl(cellStates); |
|
|
|
|
|
|
|
|
if (typeof _isInitializing !== 'undefined') { |
|
|
_isInitializing = false; |
|
|
} |
|
|
}); |
|
|
} else { |
|
|
|
|
|
if (typeof _isInitializing !== 'undefined') { |
|
|
requestAnimationFrame(() => { |
|
|
_isInitializing = false; |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
document.addEventListener('mousedown', onLineNumberMouseDown); |
|
|
document.addEventListener('mousemove', onDocMouseMove); |
|
|
document.addEventListener('mouseup', onDocMouseUp); |
|
|
|
|
|
|
|
|
document.addEventListener('keydown', function(e) { |
|
|
if (e.key === 'Escape' || e.keyCode === 27) { |
|
|
const currentTool = document.body.dataset.tool; |
|
|
if (currentTool && currentTool !== 'none') { |
|
|
|
|
|
window.setActiveTool('none'); |
|
|
} |
|
|
|
|
|
clearSelection(true); |
|
|
} |
|
|
}); |
|
|
}); |
|
|
|
|
|
function updateStateIndicator() { |
|
|
try { |
|
|
const el = document.getElementById('status-widget'); |
|
|
if (!el) return; |
|
|
const tool = document.body.dataset.tool || 'none'; |
|
|
if (tool && tool !== 'none') { |
|
|
el.textContent = `tool: ${tool} — Esc`; |
|
|
return; |
|
|
} |
|
|
if (_selection) { |
|
|
const t = _selection.a === _selection.b ? `L${_selection.a}` : `L${_selection.a}-${_selection.b}`; |
|
|
el.textContent = `selected: ${t} — Esc`; |
|
|
return; |
|
|
} |
|
|
el.textContent = 'ready — Esc'; |
|
|
} catch (_) {} |
|
|
} |
|
|
</script> |
|
|
</head> |
|
|
|
|
|
|
|
|
<body> |
|
|
<div class="controls"> |
|
|
<div class="controls-buttons"> |
|
|
|
|
|
<a href="index.html" class="back-button">← back</a> |
|
|
|
|
|
<div class="theme-toggle" onclick="toggleTheme()">light</div> |
|
|
<div class="reset-toggle" onclick="resetLayout()">reset</div> |
|
|
<div class="menu-button" onclick="toggleMenu()"> |
|
|
menu ▼ |
|
|
<div class="menu-dropdown"> |
|
|
<div class="menu-item" onclick="setUiTheme('default')"> |
|
|
<span class="menu-checkbox" id="checkbox-ui-default">☑</span> Theme: default |
|
|
</div> |
|
|
<div class="menu-item" onclick="setUiTheme('none')"> |
|
|
<span class="menu-checkbox" id="checkbox-ui-none">☐</span> Theme: none |
|
|
</div> |
|
|
<div class="menu-item" onclick="setUiTheme('monocolor')"> |
|
|
<span class="menu-checkbox" id="checkbox-ui-monocolor">☐</span> Theme: monocolor |
|
|
</div> |
|
|
<div class="menu-item" onclick="toggleWidget('tools')"> |
|
|
<span class="menu-checkbox" id="checkbox-tools">☐</span> Tools |
|
|
</div> |
|
|
<div class="menu-item" onclick="toggleWidget('file-explorer')"> |
|
|
<span class="menu-checkbox" id="checkbox-file-explorer">☐</span> File Explorer |
|
|
</div> |
|
|
<div class="menu-item" onclick="toggleWidget('minimap')"> |
|
|
<span class="menu-checkbox" id="checkbox-minimap">☐</span> Table of Contents |
|
|
</div> |
|
|
<div class="menu-item" onclick="toggleWidget('status')"> |
|
|
<span class="menu-checkbox" id="checkbox-status">☑</span> Status Indicator |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="system-info"> |
|
|
<div class="system-info-header">Generated on:</div> |
|
|
<div class="system-info-content"> |
|
|
Linux x86_64 | Linux-5.10.244-240.970.amzn2.x86_64-x86_64-with-glibc2.35 |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="main-content"> |
|
|
<h1>HF Kernels - Rotary Position Embeddings</h1> |
|
|
<h2>GPU Info</h2> |
|
|
<div class="cell" id="cell-nv"> |
|
|
<div class="cell-header"> |
|
|
<span class="collapse-indicators"> |
|
|
<span onclick="toggleCode('nv')" style="cursor: pointer;">▼ code</span> |
|
|
<span onclick="toggleOutput('nv')" style="cursor: pointer;">▼ output</span> |
|
|
<span id="uv-indicator-nv" style="cursor: default; opacity: 0.3;">▶ uv-logs</span> |
|
|
</span> | |
|
|
Cell: nv | 0.23s |
|
|
| <button class="run-btn" onclick="runCell('nv')">▶ run</button> |
|
|
<button class="copy-btn" onclick="copyCell('nv')">Copy</button> |
|
|
<a href="cells/nv.py" target="_blank" class="raw-btn">Raw</a> |
|
|
<a href="https://github.com/huggingface/kernels-benchmarks/blob/main/rotary/impls/hf_kernels_rotary.md" target="_blank" class="github-btn">GitHub</a> |
|
|
<a href="https://huggingface.co/kernels-community/rotary" target="_blank" class="hf-btn">🤗 HF</a> |
|
|
</div> |
|
|
<div id="code-nv" class="cell-code" data-lines="2"> |
|
|
<div class="code-wrap"> |
|
|
<div class="highlight"><pre><span></span><span class="kn">import</span><span class="w"> </span><span class="nn">subprocess</span> |
|
|
<span class="nb">print</span><span class="p">(</span><span class="n">subprocess</span><span class="o">.</span><span class="n">run</span><span class="p">([</span><span class="s2">"nvidia-smi"</span><span class="p">],</span> <span class="n">capture_output</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">text</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span><span class="o">.</span><span class="n">stdout</span><span class="p">)</span> |
|
|
</pre></div> |
|
|
|
|
|
<div class="code-line-highlight" id="line-highlight-nv"></div> |
|
|
</div> |
|
|
</div> |
|
|
<div id="output-nv" class="cell-output"> |
|
|
<div class="cell-stdout"><pre class="stdout-text">Fri Oct 31 20:00:00 2025 |
|
|
+-----------------------------------------------------------------------------------------+ |
|
|
| NVIDIA-SMI 570.195.03 Driver Version: 570.195.03 CUDA Version: 12.8 | |
|
|
|-----------------------------------------+------------------------+----------------------+ |
|
|
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC | |
|
|
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. | |
|
|
| | | MIG M. | |
|
|
|=========================================+========================+======================| |
|
|
| 0 NVIDIA L40S On | 00000000:4D:00.0 Off | 0 | |
|
|
| N/A 32C P0 101W / 350W | 0MiB / 46068MiB | 100% Default | |
|
|
| | | N/A | |
|
|
+-----------------------------------------+------------------------+----------------------+ |
|
|
|
|
|
+-----------------------------------------------------------------------------------------+ |
|
|
| Processes: | |
|
|
| GPU GI CI PID Type Process name GPU Memory | |
|
|
| ID ID Usage | |
|
|
|=========================================================================================| |
|
|
| No running processes found | |
|
|
+-----------------------------------------------------------------------------------------+ |
|
|
|
|
|
</pre></div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<h2>Rotary Embeddings Benchmark</h2> |
|
|
<div class="cell" id="cell-benchmark"> |
|
|
<div class="cell-header"> |
|
|
<span class="collapse-indicators"> |
|
|
<span onclick="toggleCode('benchmark')" style="cursor: pointer;">▼ code</span> |
|
|
<span onclick="toggleOutput('benchmark')" style="cursor: pointer;">▼ output</span> |
|
|
<span id="uv-indicator-benchmark" onclick="toggleUvLogsFromHeader('benchmark')" style="cursor: pointer;">▶ uv-logs</span> |
|
|
</span> | |
|
|
Cell: benchmark | 4.67s |
|
|
| <button class="run-btn" onclick="runCell('benchmark')">▶ run</button> |
|
|
<button class="copy-btn" onclick="copyCell('benchmark')">Copy</button> |
|
|
<a href="cells/benchmark.py" target="_blank" class="raw-btn">Raw</a> |
|
|
<a href="https://github.com/huggingface/kernels-benchmarks/blob/main/rotary/impls/hf_kernels_rotary.md" target="_blank" class="github-btn">GitHub</a> |
|
|
<a href="https://huggingface.co/kernels-community/rotary" target="_blank" class="hf-btn">🤗 HF</a> |
|
|
</div> |
|
|
<div id="code-benchmark" class="cell-code" data-lines="48"> |
|
|
<div class="code-wrap"> |
|
|
<div class="highlight"><pre><span></span><span class="c1"># /// script</span> |
|
|
<span class="c1"># requires-python = ">=3.10"</span> |
|
|
<span class="c1"># dependencies = [</span> |
|
|
<span class="c1"># "numpy",</span> |
|
|
<span class="c1"># "torch==2.8.0",</span> |
|
|
<span class="c1"># "kernels-benchmark-tools",</span> |
|
|
<span class="c1"># "kernels",</span> |
|
|
<span class="c1"># ]</span> |
|
|
<span class="c1">#</span> |
|
|
<span class="c1"># [tool.uv.sources]</span> |
|
|
<span class="c1"># kernels-benchmark-tools = { path = "../../../../../tools", editable = true }</span> |
|
|
<span class="c1"># ///</span> |
|
|
<span class="kn">import</span><span class="w"> </span><span class="nn">torch</span> |
|
|
<span class="kn">import</span><span class="w"> </span><span class="nn">sys</span> |
|
|
<span class="kn">from</span><span class="w"> </span><span class="nn">kernels_benchmark_tools</span><span class="w"> </span><span class="kn">import</span> <span class="n">KernelTypeEnum</span><span class="p">,</span> <span class="n">run_benchmark</span> |
|
|
<span class="kn">from</span><span class="w"> </span><span class="nn">kernels</span><span class="w"> </span><span class="kn">import</span> <span class="n">get_kernel</span> |
|
|
|
|
|
<span class="c1"># Load the rotary kernel</span> |
|
|
<span class="n">rotary</span> <span class="o">=</span> <span class="n">get_kernel</span><span class="p">(</span><span class="s2">"kernels-community/rotary"</span><span class="p">)</span> |
|
|
|
|
|
|
|
|
<span class="k">def</span><span class="w"> </span><span class="nf">hf_kernels_rotary</span><span class="p">(</span><span class="n">query</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">cos</span><span class="p">,</span> <span class="n">sin</span><span class="p">,</span> <span class="n">conj</span><span class="o">=</span><span class="kc">False</span><span class="p">):</span> |
|
|
<span class="n">rotary_dim</span> <span class="o">=</span> <span class="n">cos</span><span class="o">.</span><span class="n">shape</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> |
|
|
|
|
|
<span class="c1"># Clone to avoid modifying inputs</span> |
|
|
<span class="n">q_out</span> <span class="o">=</span> <span class="n">query</span><span class="o">.</span><span class="n">clone</span><span class="p">()</span> |
|
|
<span class="n">k_out</span> <span class="o">=</span> <span class="n">key</span><span class="o">.</span><span class="n">clone</span><span class="p">()</span> |
|
|
|
|
|
<span class="c1"># Apply rotation to query</span> |
|
|
<span class="n">q1</span> <span class="o">=</span> <span class="n">q_out</span><span class="p">[</span><span class="o">...</span><span class="p">,</span> <span class="p">:</span><span class="n">rotary_dim</span><span class="p">]</span> |
|
|
<span class="n">q2</span> <span class="o">=</span> <span class="n">q_out</span><span class="p">[</span><span class="o">...</span><span class="p">,</span> <span class="n">rotary_dim</span> <span class="p">:</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">rotary_dim</span><span class="p">]</span> |
|
|
<span class="n">rotary</span><span class="o">.</span><span class="n">apply_rotary</span><span class="p">(</span><span class="n">q1</span><span class="p">,</span> <span class="n">q2</span><span class="p">,</span> <span class="n">cos</span><span class="p">,</span> <span class="n">sin</span><span class="p">,</span> <span class="n">q1</span><span class="p">,</span> <span class="n">q2</span><span class="p">,</span> <span class="n">conj</span><span class="p">)</span> |
|
|
|
|
|
<span class="c1"># Apply rotation to key</span> |
|
|
<span class="n">k1</span> <span class="o">=</span> <span class="n">k_out</span><span class="p">[</span><span class="o">...</span><span class="p">,</span> <span class="p">:</span><span class="n">rotary_dim</span><span class="p">]</span> |
|
|
<span class="n">k2</span> <span class="o">=</span> <span class="n">k_out</span><span class="p">[</span><span class="o">...</span><span class="p">,</span> <span class="n">rotary_dim</span> <span class="p">:</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">rotary_dim</span><span class="p">]</span> |
|
|
<span class="n">rotary</span><span class="o">.</span><span class="n">apply_rotary</span><span class="p">(</span><span class="n">k1</span><span class="p">,</span> <span class="n">k2</span><span class="p">,</span> <span class="n">cos</span><span class="p">,</span> <span class="n">sin</span><span class="p">,</span> <span class="n">k1</span><span class="p">,</span> <span class="n">k2</span><span class="p">,</span> <span class="n">conj</span><span class="p">)</span> |
|
|
|
|
|
<span class="k">return</span> <span class="n">q_out</span><span class="p">,</span> <span class="n">k_out</span> |
|
|
|
|
|
|
|
|
<span class="n">run_benchmark</span><span class="p">(</span> |
|
|
<span class="n">kernel_type</span><span class="o">=</span><span class="n">KernelTypeEnum</span><span class="o">.</span><span class="n">ROTARY</span><span class="p">,</span> |
|
|
<span class="n">impl_name</span><span class="o">=</span><span class="s2">"hf_kernels_rotary"</span><span class="p">,</span> |
|
|
<span class="n">impl_tags</span><span class="o">=</span><span class="p">{</span><span class="s2">"family"</span><span class="p">:</span> <span class="s2">"hf-kernels"</span><span class="p">,</span> <span class="s2">"backend"</span><span class="p">:</span> <span class="s2">"cuda"</span><span class="p">},</span> |
|
|
<span class="n">impl_func</span><span class="o">=</span><span class="n">hf_kernels_rotary</span><span class="p">,</span> |
|
|
<span class="n">dtype</span><span class="o">=</span><span class="s2">"float32"</span><span class="p">,</span> |
|
|
<span class="p">)</span> |
|
|
</pre></div> |
|
|
|
|
|
<div class="code-line-highlight" id="line-highlight-benchmark"></div> |
|
|
</div> |
|
|
</div> |
|
|
<div id="output-benchmark" class="cell-output"> |
|
|
<div class="cell-stdout"><pre class="stdout-text">Running rotary benchmark on cuda with 24 workloads. |
|
|
|
|
|
====================================================================== |
|
|
PROFILE TRACE: hf_kernels_rotary | cuda_B1_S128_H8_D64_R32 |
|
|
====================================================================== |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Name Self CPU % Self CPU CPU total % CPU total CPU time avg Self CUDA Self CUDA % CUDA total CUDA time avg # of Calls |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
hf_kernels_rotary 0.00% 0.000us 0.00% 0.000us 0.000us 426.303us 1837.51% 426.303us 426.303us 1 |
|
|
hf_kernels_rotary 12.40% 260.056us 99.66% 2.090ms 2.090ms 0.000us 0.00% 24.480us 24.480us 1 |
|
|
_rotary_dba7d1e::apply_rotary 2.75% 57.674us 5.07% 106.315us 17.719us 16.128us 69.52% 16.128us 2.688us 6 |
|
|
void at::native::(anonymous namespace)::unrolled_ele... 0.00% 0.000us 0.00% 0.000us 0.000us 16.128us 69.52% 16.128us 2.688us 6 |
|
|
aten::clone 2.13% 44.582us 79.34% 1.664ms 277.309us 0.000us 0.00% 8.352us 1.392us 6 |
|
|
aten::copy_ 1.84% 38.562us 74.44% 1.561ms 260.165us 7.072us 30.48% 8.352us 1.392us 6 |
|
|
Memcpy DtoD (Device -> Device) 0.00% 0.000us 0.00% 0.000us 0.000us 7.072us 30.48% 7.072us 1.179us 6 |
|
|
Activity Buffer Request 69.01% 1.447ms 69.01% 1.447ms 1.447ms 1.280us 5.52% 1.280us 1.280us 1 |
|
|
aten::empty_strided 2.78% 58.281us 2.78% 58.281us 9.713us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaMemcpyAsync 3.58% 75.121us 3.58% 75.121us 12.520us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
aten::slice 2.14% 44.780us 2.85% 59.790us 4.983us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
aten::as_strided 0.72% 15.010us 0.72% 15.010us 1.251us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
cudaLaunchKernel 2.32% 48.641us 2.32% 48.641us 8.107us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaDeviceSynchronize 0.34% 7.100us 0.34% 7.100us 7.100us 0.000us 0.00% 0.000us 0.000us 1 |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Self CPU time total: 2.097ms |
|
|
Self CUDA time total: 23.200us |
|
|
|
|
|
|
|
|
|
|
|
====================================================================== |
|
|
PROFILE TRACE: hf_kernels_rotary | cuda_B1_S128_H8_D128_R64 |
|
|
====================================================================== |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Name Self CPU % Self CPU CPU total % CPU total CPU time avg Self CUDA Self CUDA % CUDA total CUDA time avg # of Calls |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
hf_kernels_rotary 0.00% 0.000us 0.00% 0.000us 0.000us 340.796us 1422.00% 340.796us 340.796us 1 |
|
|
hf_kernels_rotary 9.48% 182.026us 99.73% 1.916ms 1.916ms 0.000us 0.00% 25.278us 25.278us 1 |
|
|
_rotary_dba7d1e::apply_rotary 2.22% 42.701us 4.40% 84.531us 14.088us 16.159us 67.42% 16.159us 2.693us 6 |
|
|
void at::native::(anonymous namespace)::unrolled_ele... 0.00% 0.000us 0.00% 0.000us 0.000us 16.159us 67.42% 16.159us 2.693us 6 |
|
|
aten::clone 1.41% 27.120us 83.58% 1.605ms 267.570us 0.000us 0.00% 9.119us 1.520us 6 |
|
|
aten::copy_ 2.02% 38.773us 80.45% 1.545ms 257.555us 7.807us 32.58% 9.119us 1.520us 6 |
|
|
Memcpy DtoD (Device -> Device) 0.00% 0.000us 0.00% 0.000us 0.000us 7.807us 32.58% 7.807us 1.301us 6 |
|
|
Activity Buffer Request 75.56% 1.451ms 75.56% 1.451ms 1.451ms 1.312us 5.47% 1.312us 1.312us 1 |
|
|
aten::empty_strided 1.72% 32.970us 1.72% 32.970us 5.495us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaMemcpyAsync 2.88% 55.291us 2.88% 55.291us 9.215us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
aten::slice 1.76% 33.749us 2.27% 43.642us 3.637us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
aten::as_strided 0.52% 9.893us 0.52% 9.893us 0.824us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
cudaLaunchKernel 2.18% 41.830us 2.18% 41.830us 6.972us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaDeviceSynchronize 0.27% 5.161us 0.27% 5.161us 5.161us 0.000us 0.00% 0.000us 0.000us 1 |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Self CPU time total: 1.921ms |
|
|
Self CUDA time total: 23.966us |
|
|
|
|
|
|
|
|
|
|
|
====================================================================== |
|
|
PROFILE TRACE: hf_kernels_rotary | cuda_B1_S128_H32_D64_R32 |
|
|
====================================================================== |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Name Self CPU % Self CPU CPU total % CPU total CPU time avg Self CUDA Self CUDA % CUDA total CUDA time avg # of Calls |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
hf_kernels_rotary 0.00% 0.000us 0.00% 0.000us 0.000us 339.421us 1391.81% 339.421us 339.421us 1 |
|
|
hf_kernels_rotary 9.18% 172.926us 99.76% 1.879ms 1.879ms 0.000us 0.00% 25.699us 25.699us 1 |
|
|
_rotary_dba7d1e::apply_rotary 2.20% 41.409us 4.51% 85.000us 14.167us 16.481us 67.58% 16.481us 2.747us 6 |
|
|
void at::native::(anonymous namespace)::unrolled_ele... 0.00% 0.000us 0.00% 0.000us 0.000us 16.481us 67.58% 16.481us 2.747us 6 |
|
|
aten::clone 1.46% 27.581us 83.73% 1.577ms 262.862us 0.000us 0.00% 9.218us 1.536us 6 |
|
|
aten::copy_ 1.97% 37.091us 80.45% 1.515ms 252.563us 7.906us 32.42% 9.218us 1.536us 6 |
|
|
Memcpy DtoD (Device -> Device) 0.00% 0.000us 0.00% 0.000us 0.000us 7.906us 32.42% 7.906us 1.318us 6 |
|
|
Activity Buffer Request 75.71% 1.426ms 75.71% 1.426ms 1.426ms 1.312us 5.38% 1.312us 1.312us 1 |
|
|
aten::empty_strided 1.82% 34.210us 1.82% 34.210us 5.702us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaMemcpyAsync 2.77% 52.231us 2.77% 52.231us 8.705us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
aten::slice 1.80% 33.892us 2.33% 43.952us 3.663us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
aten::as_strided 0.53% 10.060us 0.53% 10.060us 0.838us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
cudaLaunchKernel 2.31% 43.591us 2.31% 43.591us 7.265us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaDeviceSynchronize 0.24% 4.550us 0.24% 4.550us 4.550us 0.000us 0.00% 0.000us 0.000us 1 |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Self CPU time total: 1.884ms |
|
|
Self CUDA time total: 24.387us |
|
|
|
|
|
|
|
|
|
|
|
====================================================================== |
|
|
PROFILE TRACE: hf_kernels_rotary | cuda_B1_S128_H32_D128_R64 |
|
|
====================================================================== |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Name Self CPU % Self CPU CPU total % CPU total CPU time avg Self CUDA Self CUDA % CUDA total CUDA time avg # of Calls |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
hf_kernels_rotary 0.00% 0.000us 0.00% 0.000us 0.000us 353.466us 1252.36% 353.466us 353.466us 1 |
|
|
hf_kernels_rotary 8.35% 176.747us 99.76% 2.111ms 2.111ms 0.000us 0.00% 30.048us 30.048us 1 |
|
|
_rotary_dba7d1e::apply_rotary 2.17% 45.850us 4.21% 89.000us 14.833us 17.664us 62.59% 17.664us 2.944us 6 |
|
|
void at::native::(anonymous namespace)::unrolled_ele... 0.00% 0.000us 0.00% 0.000us 0.000us 17.664us 62.59% 17.664us 2.944us 6 |
|
|
aten::clone 1.36% 28.714us 85.13% 1.802ms 300.274us 0.000us 0.00% 12.384us 2.064us 6 |
|
|
aten::copy_ 1.83% 38.751us 82.20% 1.740ms 289.944us 10.560us 37.41% 12.384us 2.064us 6 |
|
|
Memcpy DtoD (Device -> Device) 0.00% 0.000us 0.00% 0.000us 0.000us 10.560us 37.41% 10.560us 1.760us 6 |
|
|
Activity Buffer Request 67.60% 1.431ms 67.60% 1.431ms 1.431ms 1.824us 6.46% 1.824us 1.824us 1 |
|
|
aten::empty_strided 1.57% 33.269us 1.57% 33.269us 5.545us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaMemcpyAsync 12.77% 270.306us 12.77% 270.306us 45.051us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
aten::slice 1.59% 33.568us 2.07% 43.911us 3.659us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
aten::as_strided 0.49% 10.343us 0.49% 10.343us 0.862us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
cudaLaunchKernel 2.04% 43.150us 2.04% 43.150us 7.192us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaDeviceSynchronize 0.24% 5.130us 0.24% 5.130us 5.130us 0.000us 0.00% 0.000us 0.000us 1 |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Self CPU time total: 2.116ms |
|
|
Self CUDA time total: 28.224us |
|
|
|
|
|
|
|
|
|
|
|
====================================================================== |
|
|
PROFILE TRACE: hf_kernels_rotary | cuda_B1_S512_H8_D64_R32 |
|
|
====================================================================== |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Name Self CPU % Self CPU CPU total % CPU total CPU time avg Self CUDA Self CUDA % CUDA total CUDA time avg # of Calls |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
hf_kernels_rotary 0.00% 0.000us 0.00% 0.000us 0.000us 351.740us 1444.46% 351.740us 351.740us 1 |
|
|
hf_kernels_rotary 8.68% 176.155us 99.77% 2.024ms 2.024ms 0.000us 0.00% 25.663us 25.663us 1 |
|
|
_rotary_dba7d1e::apply_rotary 2.27% 46.099us 4.32% 87.680us 14.613us 16.479us 67.67% 16.479us 2.747us 6 |
|
|
void at::native::(anonymous namespace)::unrolled_ele... 0.00% 0.000us 0.00% 0.000us 0.000us 16.479us 67.67% 16.479us 2.747us 6 |
|
|
aten::clone 1.42% 28.832us 84.62% 1.717ms 286.091us 0.000us 0.00% 9.184us 1.531us 6 |
|
|
aten::copy_ 1.86% 37.831us 81.49% 1.653ms 275.519us 7.872us 32.33% 9.184us 1.531us 6 |
|
|
Memcpy DtoD (Device -> Device) 0.00% 0.000us 0.00% 0.000us 0.000us 7.872us 32.33% 7.872us 1.312us 6 |
|
|
Activity Buffer Request 70.03% 1.420ms 70.03% 1.420ms 1.420ms 1.312us 5.39% 1.312us 1.312us 1 |
|
|
aten::empty_strided 1.71% 34.601us 1.71% 34.601us 5.767us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaMemcpyAsync 9.60% 194.784us 9.60% 194.784us 32.464us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
aten::slice 1.63% 33.102us 2.14% 43.512us 3.626us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
aten::as_strided 0.51% 10.410us 0.51% 10.410us 0.867us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
cudaLaunchKernel 2.05% 41.581us 2.05% 41.581us 6.930us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaDeviceSynchronize 0.23% 4.660us 0.23% 4.660us 4.660us 0.000us 0.00% 0.000us 0.000us 1 |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Self CPU time total: 2.029ms |
|
|
Self CUDA time total: 24.351us |
|
|
|
|
|
|
|
|
|
|
|
====================================================================== |
|
|
PROFILE TRACE: hf_kernels_rotary | cuda_B1_S512_H8_D128_R64 |
|
|
====================================================================== |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Name Self CPU % Self CPU CPU total % CPU total CPU time avg Self CUDA Self CUDA % CUDA total CUDA time avg # of Calls |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
hf_kernels_rotary 0.00% 0.000us 0.00% 0.000us 0.000us 349.111us 1238.38% 349.111us 349.111us 1 |
|
|
hf_kernels_rotary 23.24% 192.013us 99.32% 820.571us 820.571us 0.000us 0.00% 30.015us 30.015us 1 |
|
|
_rotary_dba7d1e::apply_rotary 5.42% 44.795us 10.63% 87.866us 14.644us 17.632us 62.54% 17.632us 2.939us 6 |
|
|
void at::native::(anonymous namespace)::unrolled_ele... 0.00% 0.000us 0.00% 0.000us 0.000us 17.632us 62.54% 17.632us 2.939us 6 |
|
|
aten::clone 2.69% 22.223us 60.09% 496.442us 82.740us 0.000us 0.00% 12.383us 2.064us 6 |
|
|
aten::copy_ 4.60% 38.000us 53.48% 441.890us 73.648us 10.559us 37.46% 12.383us 2.064us 6 |
|
|
Memcpy DtoD (Device -> Device) 0.00% 0.000us 0.00% 0.000us 0.000us 10.559us 37.46% 10.559us 1.760us 6 |
|
|
Activity Buffer Request 26.48% 218.816us 26.48% 218.816us 218.816us 1.824us 6.47% 1.824us 1.824us 1 |
|
|
aten::empty_strided 3.91% 32.329us 3.91% 32.329us 5.388us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaMemcpyAsync 22.40% 185.074us 22.40% 185.074us 30.846us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
aten::slice 4.04% 33.410us 5.36% 44.250us 3.688us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
aten::as_strided 1.31% 10.840us 1.31% 10.840us 0.903us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
cudaLaunchKernel 5.21% 43.071us 5.21% 43.071us 7.178us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaDeviceSynchronize 0.68% 5.641us 0.68% 5.641us 5.641us 0.000us 0.00% 0.000us 0.000us 1 |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Self CPU time total: 826.212us |
|
|
Self CUDA time total: 28.191us |
|
|
|
|
|
|
|
|
|
|
|
====================================================================== |
|
|
PROFILE TRACE: hf_kernels_rotary | cuda_B1_S512_H32_D64_R32 |
|
|
====================================================================== |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Name Self CPU % Self CPU CPU total % CPU total CPU time avg Self CUDA Self CUDA % CUDA total CUDA time avg # of Calls |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
hf_kernels_rotary 0.00% 0.000us 0.00% 0.000us 0.000us 344.984us 852.93% 344.984us 344.984us 1 |
|
|
hf_kernels_rotary 22.02% 168.975us 99.39% 762.759us 762.759us 0.000us 0.00% 43.263us 43.263us 1 |
|
|
_rotary_dba7d1e::apply_rotary 5.75% 44.162us 11.18% 85.802us 14.300us 23.456us 57.99% 23.456us 3.909us 6 |
|
|
void at::native::(anonymous namespace)::unrolled_ele... 0.00% 0.000us 0.00% 0.000us 0.000us 23.456us 57.99% 23.456us 3.909us 6 |
|
|
aten::clone 2.91% 22.350us 60.45% 463.932us 77.322us 0.000us 0.00% 19.807us 3.301us 6 |
|
|
aten::copy_ 4.98% 38.249us 53.45% 410.170us 68.362us 16.991us 42.01% 19.807us 3.301us 6 |
|
|
Memcpy DtoD (Device -> Device) 0.00% 0.000us 0.00% 0.000us 0.000us 16.991us 42.01% 16.991us 2.832us 6 |
|
|
Activity Buffer Request 24.55% 188.395us 24.55% 188.395us 188.395us 2.816us 6.96% 2.816us 2.816us 1 |
|
|
aten::empty_strided 4.09% 31.412us 4.09% 31.412us 5.235us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaMemcpyAsync 23.91% 183.526us 23.91% 183.526us 30.588us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
aten::slice 4.40% 33.790us 5.74% 44.050us 3.671us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
aten::as_strided 1.34% 10.260us 1.34% 10.260us 0.855us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
cudaLaunchKernel 5.43% 41.640us 5.43% 41.640us 6.940us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaDeviceSynchronize 0.61% 4.661us 0.61% 4.661us 4.661us 0.000us 0.00% 0.000us 0.000us 1 |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Self CPU time total: 767.420us |
|
|
Self CUDA time total: 40.447us |
|
|
|
|
|
|
|
|
|
|
|
====================================================================== |
|
|
PROFILE TRACE: hf_kernels_rotary | cuda_B1_S512_H32_D128_R64 |
|
|
====================================================================== |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Name Self CPU % Self CPU CPU total % CPU total CPU time avg Self CUDA Self CUDA % CUDA total CUDA time avg # of Calls |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
hf_kernels_rotary 0.00% 0.000us 0.00% 0.000us 0.000us 347.453us 442.64% 347.453us 347.453us 1 |
|
|
hf_kernels_rotary 20.37% 160.826us 99.39% 784.751us 784.751us 0.000us 0.00% 91.040us 91.040us 1 |
|
|
aten::clone 2.83% 22.340us 62.44% 492.983us 82.164us 0.000us 0.00% 52.865us 8.811us 6 |
|
|
aten::copy_ 4.65% 36.740us 55.30% 436.663us 72.777us 40.321us 51.37% 52.865us 8.811us 6 |
|
|
Memcpy DtoD (Device -> Device) 0.00% 0.000us 0.00% 0.000us 0.000us 40.321us 51.37% 40.321us 6.720us 6 |
|
|
_rotary_dba7d1e::apply_rotary 5.74% 45.350us 11.00% 86.891us 14.482us 38.175us 48.63% 38.175us 6.362us 6 |
|
|
void at::native::(anonymous namespace)::unrolled_ele... 0.00% 0.000us 0.00% 0.000us 0.000us 38.175us 48.63% 38.175us 6.362us 6 |
|
|
Activity Buffer Request 27.86% 219.946us 27.86% 219.946us 219.946us 12.544us 15.98% 12.544us 12.544us 1 |
|
|
aten::empty_strided 4.30% 33.980us 4.30% 33.980us 5.663us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaMemcpyAsync 22.79% 179.977us 22.79% 179.977us 29.996us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
aten::slice 4.35% 34.361us 5.58% 44.051us 3.671us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
aten::as_strided 1.23% 9.690us 1.23% 9.690us 0.808us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
cudaLaunchKernel 5.26% 41.541us 5.26% 41.541us 6.924us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaDeviceSynchronize 0.61% 4.830us 0.61% 4.830us 4.830us 0.000us 0.00% 0.000us 0.000us 1 |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Self CPU time total: 789.581us |
|
|
Self CUDA time total: 78.496us |
|
|
|
|
|
|
|
|
|
|
|
====================================================================== |
|
|
PROFILE TRACE: hf_kernels_rotary | cuda_B1_S2048_H8_D64_R32 |
|
|
====================================================================== |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Name Self CPU % Self CPU CPU total % CPU total CPU time avg Self CUDA Self CUDA % CUDA total CUDA time avg # of Calls |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
hf_kernels_rotary 0.00% 0.000us 0.00% 0.000us 0.000us 347.324us 858.06% 347.324us 347.324us 1 |
|
|
hf_kernels_rotary 8.65% 173.958us 99.77% 2.007ms 2.007ms 0.000us 0.00% 43.325us 43.325us 1 |
|
|
_rotary_dba7d1e::apply_rotary 2.18% 43.910us 4.21% 84.770us 14.128us 23.423us 57.87% 23.423us 3.904us 6 |
|
|
void at::native::(anonymous namespace)::unrolled_ele... 0.00% 0.000us 0.00% 0.000us 0.000us 23.423us 57.87% 23.423us 3.904us 6 |
|
|
aten::clone 1.35% 27.211us 84.83% 1.706ms 284.405us 0.000us 0.00% 19.902us 3.317us 6 |
|
|
aten::copy_ 1.92% 38.681us 81.76% 1.645ms 274.138us 17.055us 42.13% 19.902us 3.317us 6 |
|
|
Memcpy DtoD (Device -> Device) 0.00% 0.000us 0.00% 0.000us 0.000us 17.055us 42.13% 17.055us 2.842us 6 |
|
|
Activity Buffer Request 70.68% 1.422ms 70.68% 1.422ms 1.422ms 2.847us 7.03% 2.847us 2.847us 1 |
|
|
aten::empty_strided 1.71% 34.392us 1.71% 34.392us 5.732us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaMemcpyAsync 9.16% 184.363us 9.16% 184.363us 30.727us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
aten::slice 1.62% 32.593us 2.08% 41.861us 3.488us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
aten::as_strided 0.46% 9.268us 0.46% 9.268us 0.772us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
cudaLaunchKernel 2.03% 40.860us 2.03% 40.860us 6.810us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaDeviceSynchronize 0.23% 4.670us 0.23% 4.670us 4.670us 0.000us 0.00% 0.000us 0.000us 1 |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Self CPU time total: 2.012ms |
|
|
Self CUDA time total: 40.478us |
|
|
|
|
|
|
|
|
|
|
|
====================================================================== |
|
|
PROFILE TRACE: hf_kernels_rotary | cuda_B1_S2048_H8_D128_R64 |
|
|
====================================================================== |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Name Self CPU % Self CPU CPU total % CPU total CPU time avg Self CUDA Self CUDA % CUDA total CUDA time avg # of Calls |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
hf_kernels_rotary 0.00% 0.000us 0.00% 0.000us 0.000us 361.785us 476.45% 361.785us 361.785us 1 |
|
|
hf_kernels_rotary 8.64% 176.662us 99.77% 2.040ms 2.040ms 0.000us 0.00% 86.685us 86.685us 1 |
|
|
aten::clone 1.40% 28.682us 84.64% 1.731ms 288.486us 0.000us 0.00% 47.871us 7.979us 6 |
|
|
aten::copy_ 1.80% 36.737us 81.55% 1.668ms 277.962us 37.119us 48.88% 47.871us 7.979us 6 |
|
|
_rotary_dba7d1e::apply_rotary 2.24% 45.910us 4.34% 88.820us 14.803us 38.814us 51.12% 38.814us 6.469us 6 |
|
|
void at::native::(anonymous namespace)::unrolled_ele... 0.00% 0.000us 0.00% 0.000us 0.000us 38.814us 51.12% 38.814us 6.469us 6 |
|
|
Memcpy DtoD (Device -> Device) 0.00% 0.000us 0.00% 0.000us 0.000us 37.119us 48.88% 37.119us 6.187us 6 |
|
|
Activity Buffer Request 70.82% 1.448ms 70.82% 1.448ms 1.448ms 10.752us 14.16% 10.752us 10.752us 1 |
|
|
aten::empty_strided 1.69% 34.462us 1.69% 34.462us 5.744us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaMemcpyAsync 8.93% 182.677us 8.93% 182.677us 30.446us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
aten::slice 1.66% 33.994us 2.15% 43.925us 3.660us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
aten::as_strided 0.49% 9.931us 0.49% 9.931us 0.828us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
cudaLaunchKernel 2.10% 42.910us 2.10% 42.910us 7.152us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaDeviceSynchronize 0.23% 4.670us 0.23% 4.670us 4.670us 0.000us 0.00% 0.000us 0.000us 1 |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Self CPU time total: 2.045ms |
|
|
Self CUDA time total: 75.933us |
|
|
|
|
|
|
|
|
|
|
|
====================================================================== |
|
|
PROFILE TRACE: hf_kernels_rotary | cuda_B1_S2048_H32_D64_R32 |
|
|
====================================================================== |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Name Self CPU % Self CPU CPU total % CPU total CPU time avg Self CUDA Self CUDA % CUDA total CUDA time avg # of Calls |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
hf_kernels_rotary 0.00% 0.000us 0.00% 0.000us 0.000us 373.629us 268.97% 373.629us 373.629us 1 |
|
|
hf_kernels_rotary 8.95% 179.578us 99.78% 2.002ms 2.002ms 0.000us 0.00% 162.750us 162.750us 1 |
|
|
aten::clone 1.48% 29.597us 83.94% 1.684ms 280.680us 0.000us 0.00% 102.944us 17.157us 6 |
|
|
aten::copy_ 1.82% 36.553us 80.73% 1.620ms 269.962us 79.104us 56.95% 102.944us 17.157us 6 |
|
|
Memcpy DtoD (Device -> Device) 0.00% 0.000us 0.00% 0.000us 0.000us 79.104us 56.95% 79.104us 13.184us 6 |
|
|
_rotary_dba7d1e::apply_rotary 2.30% 46.131us 4.57% 91.713us 15.285us 59.806us 43.05% 59.806us 9.968us 6 |
|
|
void at::native::(anonymous namespace)::unrolled_ele... 0.00% 0.000us 0.00% 0.000us 0.000us 59.806us 43.05% 59.806us 9.968us 6 |
|
|
Activity Buffer Request 69.91% 1.403ms 69.91% 1.403ms 1.403ms 23.840us 17.16% 23.840us 23.840us 1 |
|
|
aten::empty_strided 1.73% 34.712us 1.73% 34.712us 5.785us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaMemcpyAsync 9.00% 180.563us 9.00% 180.563us 30.094us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
aten::slice 1.75% 35.198us 2.31% 46.409us 3.867us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
aten::as_strided 0.56% 11.211us 0.56% 11.211us 0.934us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
cudaLaunchKernel 2.27% 45.582us 2.27% 45.582us 7.597us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaDeviceSynchronize 0.22% 4.510us 0.22% 4.510us 4.510us 0.000us 0.00% 0.000us 0.000us 1 |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Self CPU time total: 2.006ms |
|
|
Self CUDA time total: 138.910us |
|
|
|
|
|
|
|
|
|
|
|
====================================================================== |
|
|
PROFILE TRACE: hf_kernels_rotary | cuda_B1_S2048_H32_D128_R64 |
|
|
====================================================================== |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Name Self CPU % Self CPU CPU total % CPU total CPU time avg Self CUDA Self CUDA % CUDA total CUDA time avg # of Calls |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
hf_kernels_rotary 7.56% 177.196us 86.68% 2.032ms 2.032ms 0.000us 0.00% 778.402us 778.402us 1 |
|
|
hf_kernels_rotary 0.00% 0.000us 0.00% 0.000us 0.000us 717.248us 101.07% 717.248us 717.248us 1 |
|
|
aten::clone 1.23% 28.772us 72.98% 1.711ms 285.141us 0.000us 0.00% 578.626us 96.438us 6 |
|
|
aten::copy_ 1.64% 38.341us 70.23% 1.646ms 274.415us 509.889us 71.85% 578.626us 96.438us 6 |
|
|
Memcpy DtoD (Device -> Device) 0.00% 0.000us 0.00% 0.000us 0.000us 509.889us 71.85% 509.889us 84.982us 6 |
|
|
_rotary_dba7d1e::apply_rotary 2.34% 54.801us 4.25% 99.591us 16.598us 199.776us 28.15% 199.776us 33.296us 6 |
|
|
void at::native::(anonymous namespace)::unrolled_ele... 0.00% 0.000us 0.00% 0.000us 0.000us 199.776us 28.15% 199.776us 33.296us 6 |
|
|
Activity Buffer Request 60.86% 1.427ms 60.86% 1.427ms 1.427ms 68.737us 9.69% 68.737us 68.737us 1 |
|
|
aten::empty_strided 1.52% 35.581us 1.52% 35.581us 5.930us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaMemcpyAsync 7.74% 181.435us 7.74% 181.435us 30.239us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
aten::slice 1.41% 33.151us 1.89% 44.330us 3.694us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
aten::as_strided 0.48% 11.179us 0.48% 11.179us 0.932us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
cudaLaunchKernel 1.91% 44.790us 1.91% 44.790us 7.465us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaDeviceSynchronize 13.32% 312.348us 13.32% 312.348us 312.348us 0.000us 0.00% 0.000us 0.000us 1 |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Self CPU time total: 2.344ms |
|
|
Self CUDA time total: 709.665us |
|
|
|
|
|
|
|
|
|
|
|
====================================================================== |
|
|
PROFILE TRACE: hf_kernels_rotary | cuda_B2_S128_H8_D64_R32 |
|
|
====================================================================== |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Name Self CPU % Self CPU CPU total % CPU total CPU time avg Self CUDA Self CUDA % CUDA total CUDA time avg # of Calls |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
hf_kernels_rotary 0.00% 0.000us 0.00% 0.000us 0.000us 349.629us 1313.11% 349.629us 349.629us 1 |
|
|
hf_kernels_rotary 8.75% 174.875us 99.76% 1.994ms 1.994ms 0.000us 0.00% 27.938us 27.938us 1 |
|
|
_rotary_dba7d1e::apply_rotary 2.16% 43.200us 4.40% 87.900us 14.650us 18.754us 70.43% 18.754us 3.126us 6 |
|
|
void at::native::(anonymous namespace)::unrolled_ele... 0.00% 0.000us 0.00% 0.000us 0.000us 18.754us 70.43% 18.754us 3.126us 6 |
|
|
aten::clone 1.44% 28.720us 84.48% 1.688ms 281.365us 0.000us 0.00% 9.184us 1.531us 6 |
|
|
aten::copy_ 1.82% 36.432us 81.36% 1.626ms 271.003us 7.872us 29.57% 9.184us 1.531us 6 |
|
|
Memcpy DtoD (Device -> Device) 0.00% 0.000us 0.00% 0.000us 0.000us 7.872us 29.57% 7.872us 1.312us 6 |
|
|
Activity Buffer Request 70.53% 1.410ms 70.53% 1.410ms 1.410ms 1.312us 4.93% 1.312us 1.312us 1 |
|
|
aten::empty_strided 1.67% 33.452us 1.67% 33.452us 5.575us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaMemcpyAsync 9.01% 180.083us 9.01% 180.083us 30.014us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
aten::slice 1.63% 32.560us 2.14% 42.684us 3.557us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
aten::as_strided 0.51% 10.124us 0.51% 10.124us 0.844us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
cudaLaunchKernel 2.24% 44.700us 2.24% 44.700us 7.450us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaDeviceSynchronize 0.24% 4.780us 0.24% 4.780us 4.780us 0.000us 0.00% 0.000us 0.000us 1 |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Self CPU time total: 1.998ms |
|
|
Self CUDA time total: 26.626us |
|
|
|
|
|
|
|
|
|
|
|
====================================================================== |
|
|
PROFILE TRACE: hf_kernels_rotary | cuda_B2_S128_H8_D128_R64 |
|
|
====================================================================== |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Name Self CPU % Self CPU CPU total % CPU total CPU time avg Self CUDA Self CUDA % CUDA total CUDA time avg # of Calls |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
hf_kernels_rotary 0.00% 0.000us 0.00% 0.000us 0.000us 344.698us 1282.22% 344.698us 344.698us 1 |
|
|
hf_kernels_rotary 22.61% 152.757us 99.23% 670.538us 670.538us 0.000us 0.00% 28.195us 28.195us 1 |
|
|
_rotary_dba7d1e::apply_rotary 6.64% 44.870us 12.97% 87.630us 14.605us 19.009us 70.71% 19.009us 3.168us 6 |
|
|
void at::native::(anonymous namespace)::unrolled_ele... 0.00% 0.000us 0.00% 0.000us 0.000us 19.009us 70.71% 19.009us 3.168us 6 |
|
|
aten::clone 3.38% 22.839us 57.25% 386.869us 64.478us 0.000us 0.00% 9.186us 1.531us 6 |
|
|
aten::copy_ 5.63% 38.041us 49.11% 331.829us 55.305us 7.874us 29.29% 9.186us 1.531us 6 |
|
|
Memcpy DtoD (Device -> Device) 0.00% 0.000us 0.00% 0.000us 0.000us 7.874us 29.29% 7.874us 1.312us 6 |
|
|
Activity Buffer Request 16.48% 111.363us 16.48% 111.363us 111.363us 1.312us 4.88% 1.312us 1.312us 1 |
|
|
aten::empty_strided 4.77% 32.201us 4.77% 32.201us 5.367us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaMemcpyAsync 27.00% 182.425us 27.00% 182.425us 30.404us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
aten::slice 4.90% 33.085us 6.41% 43.282us 3.607us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
aten::as_strided 1.51% 10.197us 1.51% 10.197us 0.850us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
cudaLaunchKernel 6.33% 42.760us 6.33% 42.760us 7.127us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaDeviceSynchronize 0.77% 5.200us 0.77% 5.200us 5.200us 0.000us 0.00% 0.000us 0.000us 1 |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Self CPU time total: 675.738us |
|
|
Self CUDA time total: 26.883us |
|
|
|
|
|
|
|
|
|
|
|
====================================================================== |
|
|
PROFILE TRACE: hf_kernels_rotary | cuda_B2_S128_H32_D64_R32 |
|
|
====================================================================== |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Name Self CPU % Self CPU CPU total % CPU total CPU time avg Self CUDA Self CUDA % CUDA total CUDA time avg # of Calls |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
hf_kernels_rotary 0.00% 0.000us 0.00% 0.000us 0.000us 350.004us 1141.75% 350.004us 350.004us 1 |
|
|
hf_kernels_rotary 19.05% 154.214us 99.36% 804.261us 804.261us 0.000us 0.00% 32.414us 32.414us 1 |
|
|
_rotary_dba7d1e::apply_rotary 5.47% 44.240us 10.98% 88.910us 14.818us 20.064us 65.45% 20.064us 3.344us 6 |
|
|
void at::native::(anonymous namespace)::unrolled_ele... 0.00% 0.000us 0.00% 0.000us 0.000us 20.064us 65.45% 20.064us 3.344us 6 |
|
|
aten::clone 3.02% 24.421us 63.80% 516.433us 86.072us 0.000us 0.00% 12.350us 2.058us 6 |
|
|
aten::copy_ 4.66% 37.732us 56.69% 458.901us 76.483us 10.591us 34.55% 12.350us 2.058us 6 |
|
|
Memcpy DtoD (Device -> Device) 0.00% 0.000us 0.00% 0.000us 0.000us 10.591us 34.55% 10.591us 1.765us 6 |
|
|
Activity Buffer Request 29.69% 240.306us 29.69% 240.306us 240.306us 1.759us 5.74% 1.759us 1.759us 1 |
|
|
aten::empty_strided 4.09% 33.111us 4.09% 33.111us 5.518us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaMemcpyAsync 22.34% 180.863us 22.34% 180.863us 30.144us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
aten::slice 4.15% 33.594us 5.52% 44.704us 3.725us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
aten::as_strided 1.37% 11.110us 1.37% 11.110us 0.926us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
cudaLaunchKernel 5.52% 44.670us 5.52% 44.670us 7.445us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaDeviceSynchronize 0.64% 5.201us 0.64% 5.201us 5.201us 0.000us 0.00% 0.000us 0.000us 1 |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Self CPU time total: 809.462us |
|
|
Self CUDA time total: 30.655us |
|
|
|
|
|
|
|
|
|
|
|
====================================================================== |
|
|
PROFILE TRACE: hf_kernels_rotary | cuda_B2_S128_H32_D128_R64 |
|
|
====================================================================== |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Name Self CPU % Self CPU CPU total % CPU total CPU time avg Self CUDA Self CUDA % CUDA total CUDA time avg # of Calls |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
hf_kernels_rotary 0.00% 0.000us 0.00% 0.000us 0.000us 350.355us 822.64% 350.355us 350.355us 1 |
|
|
hf_kernels_rotary 19.55% 155.605us 99.35% 790.981us 790.981us 0.000us 0.00% 45.469us 45.469us 1 |
|
|
_rotary_dba7d1e::apply_rotary 5.55% 44.191us 11.02% 87.731us 14.622us 25.565us 60.03% 25.565us 4.261us 6 |
|
|
void at::native::(anonymous namespace)::unrolled_ele... 0.00% 0.000us 0.00% 0.000us 0.000us 25.565us 60.03% 25.565us 4.261us 6 |
|
|
aten::clone 2.81% 22.389us 63.13% 502.593us 83.766us 0.000us 0.00% 19.904us 3.317us 6 |
|
|
aten::copy_ 4.90% 39.043us 56.13% 446.833us 74.472us 17.024us 39.97% 19.904us 3.317us 6 |
|
|
Memcpy DtoD (Device -> Device) 0.00% 0.000us 0.00% 0.000us 0.000us 17.024us 39.97% 17.024us 2.837us 6 |
|
|
Activity Buffer Request 28.37% 225.886us 28.37% 225.886us 225.886us 2.880us 6.76% 2.880us 2.880us 1 |
|
|
aten::empty_strided 4.19% 33.371us 4.19% 33.371us 5.562us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaMemcpyAsync 22.85% 181.904us 22.85% 181.904us 30.317us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
aten::slice 4.29% 34.142us 5.66% 45.052us 3.754us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
aten::as_strided 1.37% 10.910us 1.37% 10.910us 0.909us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
cudaLaunchKernel 5.47% 43.540us 5.47% 43.540us 7.257us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaDeviceSynchronize 0.65% 5.140us 0.65% 5.140us 5.140us 0.000us 0.00% 0.000us 0.000us 1 |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Self CPU time total: 796.121us |
|
|
Self CUDA time total: 42.589us |
|
|
|
|
|
|
|
|
|
|
|
====================================================================== |
|
|
PROFILE TRACE: hf_kernels_rotary | cuda_B2_S512_H8_D64_R32 |
|
|
====================================================================== |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Name Self CPU % Self CPU CPU total % CPU total CPU time avg Self CUDA Self CUDA % CUDA total CUDA time avg # of Calls |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
hf_kernels_rotary 0.00% 0.000us 0.00% 0.000us 0.000us 344.951us 1133.59% 344.951us 344.951us 1 |
|
|
hf_kernels_rotary 19.05% 153.418us 99.42% 800.680us 800.680us 0.000us 0.00% 32.125us 32.125us 1 |
|
|
_rotary_dba7d1e::apply_rotary 5.43% 43.718us 10.83% 87.180us 14.530us 20.095us 66.04% 20.095us 3.349us 6 |
|
|
void at::native::(anonymous namespace)::unrolled_ele... 0.00% 0.000us 0.00% 0.000us 0.000us 20.095us 66.04% 20.095us 3.349us 6 |
|
|
aten::clone 2.75% 22.180us 64.20% 517.012us 86.169us 0.000us 0.00% 12.030us 2.005us 6 |
|
|
aten::copy_ 4.82% 38.813us 57.22% 460.802us 76.800us 10.335us 33.96% 12.030us 2.005us 6 |
|
|
Memcpy DtoD (Device -> Device) 0.00% 0.000us 0.00% 0.000us 0.000us 10.335us 33.96% 10.335us 1.722us 6 |
|
|
Activity Buffer Request 30.13% 242.666us 30.13% 242.666us 242.666us 1.695us 5.57% 1.695us 1.695us 1 |
|
|
aten::empty_strided 4.23% 34.030us 4.23% 34.030us 5.672us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaMemcpyAsync 22.27% 179.323us 22.27% 179.323us 29.887us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
aten::slice 4.11% 33.131us 5.35% 43.070us 3.589us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
aten::as_strided 1.23% 9.939us 1.23% 9.939us 0.828us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
cudaLaunchKernel 5.40% 43.462us 5.40% 43.462us 7.244us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaDeviceSynchronize 0.58% 4.660us 0.58% 4.660us 4.660us 0.000us 0.00% 0.000us 0.000us 1 |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Self CPU time total: 805.340us |
|
|
Self CUDA time total: 30.430us |
|
|
|
|
|
|
|
|
|
|
|
====================================================================== |
|
|
PROFILE TRACE: hf_kernels_rotary | cuda_B2_S512_H8_D128_R64 |
|
|
====================================================================== |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Name Self CPU % Self CPU CPU total % CPU total CPU time avg Self CUDA Self CUDA % CUDA total CUDA time avg # of Calls |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
hf_kernels_rotary 0.00% 0.000us 0.00% 0.000us 0.000us 358.905us 840.15% 358.905us 358.905us 1 |
|
|
hf_kernels_rotary 15.26% 159.123us 99.55% 1.038ms 1.038ms 0.000us 0.00% 45.598us 45.598us 1 |
|
|
_rotary_dba7d1e::apply_rotary 4.27% 44.490us 8.42% 87.790us 14.632us 25.600us 59.93% 25.600us 4.267us 6 |
|
|
void at::native::(anonymous namespace)::unrolled_ele... 0.00% 0.000us 0.00% 0.000us 0.000us 25.600us 59.93% 25.600us 4.267us 6 |
|
|
aten::clone 2.23% 23.211us 71.54% 746.059us 124.343us 0.000us 0.00% 19.998us 3.333us 6 |
|
|
aten::copy_ 3.70% 38.572us 65.96% 687.817us 114.636us 17.119us 40.07% 19.998us 3.333us 6 |
|
|
Memcpy DtoD (Device -> Device) 0.00% 0.000us 0.00% 0.000us 0.000us 17.119us 40.07% 17.119us 2.853us 6 |
|
|
Activity Buffer Request 44.90% 468.242us 44.90% 468.242us 468.242us 2.879us 6.74% 2.879us 2.879us 1 |
|
|
aten::empty_strided 3.36% 35.031us 3.36% 35.031us 5.838us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaMemcpyAsync 17.36% 181.003us 17.36% 181.003us 30.167us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
aten::slice 3.32% 34.604us 4.33% 45.135us 3.761us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
aten::as_strided 1.01% 10.531us 1.01% 10.531us 0.878us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
cudaLaunchKernel 4.15% 43.300us 4.15% 43.300us 7.217us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaDeviceSynchronize 0.45% 4.700us 0.45% 4.700us 4.700us 0.000us 0.00% 0.000us 0.000us 1 |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Self CPU time total: 1.043ms |
|
|
Self CUDA time total: 42.719us |
|
|
|
|
|
|
|
|
|
|
|
====================================================================== |
|
|
PROFILE TRACE: hf_kernels_rotary | cuda_B2_S512_H32_D64_R32 |
|
|
====================================================================== |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Name Self CPU % Self CPU CPU total % CPU total CPU time avg Self CUDA Self CUDA % CUDA total CUDA time avg # of Calls |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
hf_kernels_rotary 0.00% 0.000us 0.00% 0.000us 0.000us 383.638us 432.19% 383.638us 383.638us 1 |
|
|
hf_kernels_rotary 19.20% 158.364us 99.38% 819.611us 819.611us 0.000us 0.00% 103.870us 103.870us 1 |
|
|
aten::clone 2.74% 22.581us 61.51% 507.313us 84.552us 0.000us 0.00% 63.135us 10.522us 6 |
|
|
aten::copy_ 4.83% 39.811us 54.76% 451.622us 75.270us 48.031us 54.11% 63.135us 10.522us 6 |
|
|
Memcpy DtoD (Device -> Device) 0.00% 0.000us 0.00% 0.000us 0.000us 48.031us 54.11% 48.031us 8.005us 6 |
|
|
_rotary_dba7d1e::apply_rotary 5.49% 45.243us 13.16% 108.504us 18.084us 40.735us 45.89% 40.735us 6.789us 6 |
|
|
void at::native::(anonymous namespace)::unrolled_ele... 0.00% 0.000us 0.00% 0.000us 0.000us 40.735us 45.89% 40.735us 6.789us 6 |
|
|
Activity Buffer Request 27.50% 226.825us 27.50% 226.825us 226.825us 15.104us 17.02% 15.104us 15.104us 1 |
|
|
aten::empty_strided 4.01% 33.110us 4.01% 33.110us 5.518us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaMemcpyAsync 22.43% 184.986us 22.43% 184.986us 30.831us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
aten::slice 4.25% 35.021us 5.51% 45.430us 3.786us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
aten::as_strided 1.26% 10.409us 1.26% 10.409us 0.867us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
cudaLaunchKernel 7.67% 63.261us 7.67% 63.261us 10.543us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaDeviceSynchronize 0.62% 5.141us 0.62% 5.141us 5.141us 0.000us 0.00% 0.000us 0.000us 1 |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Self CPU time total: 824.752us |
|
|
Self CUDA time total: 88.766us |
|
|
|
|
|
|
|
|
|
|
|
====================================================================== |
|
|
PROFILE TRACE: hf_kernels_rotary | cuda_B2_S512_H32_D128_R64 |
|
|
====================================================================== |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Name Self CPU % Self CPU CPU total % CPU total CPU time avg Self CUDA Self CUDA % CUDA total CUDA time avg # of Calls |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
hf_kernels_rotary 0.00% 0.000us 0.00% 0.000us 0.000us 359.259us 247.18% 359.259us 359.259us 1 |
|
|
hf_kernels_rotary 19.06% 158.337us 99.39% 825.781us 825.781us 0.000us 0.00% 168.829us 168.829us 1 |
|
|
aten::clone 2.83% 23.549us 64.09% 532.493us 88.749us 0.000us 0.00% 105.470us 17.578us 6 |
|
|
aten::copy_ 4.58% 38.013us 57.29% 475.972us 79.329us 81.982us 56.41% 105.470us 17.578us 6 |
|
|
Memcpy DtoD (Device -> Device) 0.00% 0.000us 0.00% 0.000us 0.000us 81.982us 56.41% 81.982us 13.664us 6 |
|
|
_rotary_dba7d1e::apply_rotary 5.47% 45.451us 10.86% 90.251us 15.042us 63.359us 43.59% 63.359us 10.560us 6 |
|
|
void at::native::(anonymous namespace)::unrolled_ele... 0.00% 0.000us 0.00% 0.000us 0.000us 63.359us 43.59% 63.359us 10.560us 6 |
|
|
Activity Buffer Request 31.29% 259.966us 31.29% 259.966us 259.966us 23.488us 16.16% 23.488us 23.488us 1 |
|
|
aten::empty_strided 3.97% 32.972us 3.97% 32.972us 5.495us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaMemcpyAsync 21.42% 177.993us 21.42% 177.993us 29.665us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
aten::slice 4.19% 34.839us 5.38% 44.700us 3.725us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
aten::as_strided 1.19% 9.861us 1.19% 9.861us 0.822us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
cudaLaunchKernel 5.39% 44.800us 5.39% 44.800us 7.467us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaDeviceSynchronize 0.61% 5.100us 0.61% 5.100us 5.100us 0.000us 0.00% 0.000us 0.000us 1 |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Self CPU time total: 830.881us |
|
|
Self CUDA time total: 145.341us |
|
|
|
|
|
|
|
|
|
|
|
====================================================================== |
|
|
PROFILE TRACE: hf_kernels_rotary | cuda_B2_S2048_H8_D64_R32 |
|
|
====================================================================== |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Name Self CPU % Self CPU CPU total % CPU total CPU time avg Self CUDA Self CUDA % CUDA total CUDA time avg # of Calls |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
hf_kernels_rotary 0.00% 0.000us 0.00% 0.000us 0.000us 385.725us 509.05% 385.725us 385.725us 1 |
|
|
hf_kernels_rotary 8.62% 176.456us 99.78% 2.043ms 2.043ms 0.000us 0.00% 82.558us 82.558us 1 |
|
|
_rotary_dba7d1e::apply_rotary 2.32% 47.603us 4.41% 90.273us 15.045us 41.694us 55.02% 41.694us 6.949us 6 |
|
|
void at::native::(anonymous namespace)::unrolled_ele... 0.00% 0.000us 0.00% 0.000us 0.000us 41.694us 55.02% 41.694us 6.949us 6 |
|
|
aten::clone 1.42% 29.000us 84.54% 1.731ms 288.534us 0.000us 0.00% 40.864us 6.811us 6 |
|
|
aten::copy_ 1.93% 39.552us 80.14% 1.641ms 273.497us 34.080us 44.98% 40.864us 6.811us 6 |
|
|
Memcpy DtoD (Device -> Device) 0.00% 0.000us 0.00% 0.000us 0.000us 34.080us 44.98% 34.080us 5.680us 6 |
|
|
Activity Buffer Request 69.16% 1.416ms 69.16% 1.416ms 1.416ms 6.784us 8.95% 6.784us 6.784us 1 |
|
|
aten::empty_strided 2.99% 61.221us 2.99% 61.221us 10.204us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaMemcpyAsync 9.05% 185.224us 9.05% 185.224us 30.871us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
aten::slice 1.69% 34.591us 2.21% 45.260us 3.772us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
aten::as_strided 0.52% 10.669us 0.52% 10.669us 0.889us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
cudaLaunchKernel 2.08% 42.670us 2.08% 42.670us 7.112us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaDeviceSynchronize 0.22% 4.530us 0.22% 4.530us 4.530us 0.000us 0.00% 0.000us 0.000us 1 |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Self CPU time total: 2.048ms |
|
|
Self CUDA time total: 75.774us |
|
|
|
|
|
|
|
|
|
|
|
====================================================================== |
|
|
PROFILE TRACE: hf_kernels_rotary | cuda_B2_S2048_H8_D128_R64 |
|
|
====================================================================== |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Name Self CPU % Self CPU CPU total % CPU total CPU time avg Self CUDA Self CUDA % CUDA total CUDA time avg # of Calls |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
hf_kernels_rotary 0.00% 0.000us 0.00% 0.000us 0.000us 368.925us 253.94% 368.925us 368.925us 1 |
|
|
hf_kernels_rotary 8.62% 177.641us 99.74% 2.055ms 2.055ms 0.000us 0.00% 169.118us 169.118us 1 |
|
|
aten::clone 1.42% 29.322us 84.62% 1.743ms 290.539us 0.000us 0.00% 105.470us 17.578us 6 |
|
|
aten::copy_ 1.92% 39.462us 81.52% 1.679ms 279.897us 81.631us 56.19% 105.470us 17.578us 6 |
|
|
Memcpy DtoD (Device -> Device) 0.00% 0.000us 0.00% 0.000us 0.000us 81.631us 56.19% 81.631us 13.605us 6 |
|
|
_rotary_dba7d1e::apply_rotary 2.27% 46.683us 4.40% 90.665us 15.111us 63.648us 43.81% 63.648us 10.608us 6 |
|
|
void at::native::(anonymous namespace)::unrolled_ele... 0.00% 0.000us 0.00% 0.000us 0.000us 63.648us 43.81% 63.648us 10.608us 6 |
|
|
Activity Buffer Request 70.79% 1.458ms 70.79% 1.458ms 1.458ms 23.839us 16.41% 23.839us 23.839us 1 |
|
|
aten::empty_strided 1.68% 34.530us 1.68% 34.530us 5.755us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaMemcpyAsync 8.81% 181.504us 8.81% 181.504us 30.251us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
aten::slice 1.62% 33.289us 2.09% 43.080us 3.590us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
aten::as_strided 0.48% 9.791us 0.48% 9.791us 0.816us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
cudaLaunchKernel 2.13% 43.982us 2.13% 43.982us 7.330us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaDeviceSynchronize 0.26% 5.450us 0.26% 5.450us 5.450us 0.000us 0.00% 0.000us 0.000us 1 |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Self CPU time total: 2.060ms |
|
|
Self CUDA time total: 145.279us |
|
|
|
|
|
|
|
|
|
|
|
====================================================================== |
|
|
PROFILE TRACE: hf_kernels_rotary | cuda_B2_S2048_H32_D64_R32 |
|
|
====================================================================== |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Name Self CPU % Self CPU CPU total % CPU total CPU time avg Self CUDA Self CUDA % CUDA total CUDA time avg # of Calls |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
hf_kernels_rotary 20.72% 223.838us 78.32% 845.992us 845.992us 0.000us 0.00% 747.476us 747.476us 1 |
|
|
hf_kernels_rotary 0.00% 0.000us 0.00% 0.000us 0.000us 688.117us 101.15% 688.117us 688.117us 1 |
|
|
aten::clone 2.05% 22.091us 45.23% 488.522us 81.420us 0.000us 0.00% 558.423us 93.070us 6 |
|
|
aten::copy_ 3.67% 39.650us 40.20% 434.190us 72.365us 491.256us 72.21% 558.423us 93.070us 6 |
|
|
Memcpy DtoD (Device -> Device) 0.00% 0.000us 0.00% 0.000us 0.000us 491.256us 72.21% 491.256us 81.876us 6 |
|
|
_rotary_dba7d1e::apply_rotary 4.18% 45.161us 8.45% 91.252us 15.209us 189.053us 27.79% 189.053us 31.509us 6 |
|
|
void at::native::(anonymous namespace)::unrolled_ele... 0.00% 0.000us 0.00% 0.000us 0.000us 189.053us 27.79% 189.053us 31.509us 6 |
|
|
Activity Buffer Request 19.62% 211.896us 19.62% 211.896us 211.896us 67.167us 9.87% 67.167us 67.167us 1 |
|
|
aten::empty_strided 2.98% 32.241us 2.98% 32.241us 5.374us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaMemcpyAsync 16.91% 182.644us 16.91% 182.644us 30.441us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
aten::slice 3.05% 32.939us 3.92% 42.380us 3.532us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
aten::as_strided 0.87% 9.441us 0.87% 9.441us 0.787us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
cudaLaunchKernel 4.27% 46.091us 4.27% 46.091us 7.682us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaDeviceSynchronize 21.68% 234.186us 21.68% 234.186us 234.186us 0.000us 0.00% 0.000us 0.000us 1 |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Self CPU time total: 1.080ms |
|
|
Self CUDA time total: 680.309us |
|
|
|
|
|
|
|
|
|
|
|
====================================================================== |
|
|
PROFILE TRACE: hf_kernels_rotary | cuda_B2_S2048_H32_D128_R64 |
|
|
====================================================================== |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Name Self CPU % Self CPU CPU total % CPU total CPU time avg Self CUDA Self CUDA % CUDA total CUDA time avg # of Calls |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
hf_kernels_rotary 5.41% 154.946us 27.83% 797.061us 797.061us 0.000us 0.00% 2.625ms 2.625ms 1 |
|
|
hf_kernels_rotary 0.00% 0.000us 0.00% 0.000us 0.000us 2.453ms 100.31% 2.453ms 2.453ms 1 |
|
|
aten::clone 0.79% 22.601us 17.83% 510.683us 85.114us 0.000us 0.00% 1.396ms 232.586us 6 |
|
|
aten::copy_ 1.43% 40.940us 15.89% 455.120us 75.853us 1.216ms 49.74% 1.396ms 232.586us 6 |
|
|
_rotary_dba7d1e::apply_rotary 1.59% 45.590us 3.06% 87.640us 14.607us 1.229ms 50.26% 1.229ms 204.885us 6 |
|
|
void at::native::(anonymous namespace)::unrolled_ele... 0.00% 0.000us 0.00% 0.000us 0.000us 1.229ms 50.26% 1.229ms 204.885us 6 |
|
|
Memcpy DtoD (Device -> Device) 0.00% 0.000us 0.00% 0.000us 0.000us 1.216ms 49.74% 1.216ms 202.730us 6 |
|
|
Activity Buffer Request 7.23% 207.076us 7.23% 207.076us 207.076us 179.136us 7.32% 179.136us 179.136us 1 |
|
|
aten::empty_strided 1.15% 32.962us 1.15% 32.962us 5.494us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaMemcpyAsync 7.23% 207.104us 7.23% 207.104us 34.517us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
aten::slice 1.15% 33.011us 1.53% 43.792us 3.649us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
aten::as_strided 0.38% 10.781us 0.38% 10.781us 0.898us 0.000us 0.00% 0.000us 0.000us 12 |
|
|
cudaLaunchKernel 1.47% 42.050us 1.47% 42.050us 7.008us 0.000us 0.00% 0.000us 0.000us 6 |
|
|
cudaDeviceSynchronize 72.17% 2.067ms 72.17% 2.067ms 2.067ms 0.000us 0.00% 0.000us 0.000us 1 |
|
|
------------------------------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ |
|
|
Self CPU time total: 2.864ms |
|
|
Self CUDA time total: 2.446ms |
|
|
|
|
|
|
|
|
impl wl p50(ms) ok |
|
|
hf_kernels_rotary cuda_B1_S128_H32_D128_R64 0.09 True |
|
|
hf_kernels_rotary cuda_B1_S128_H32_D64_R32 0.09 True |
|
|
hf_kernels_rotary cuda_B1_S128_H8_D128_R64 0.09 True |
|
|
hf_kernels_rotary cuda_B1_S128_H8_D64_R32 0.08 True |
|
|
hf_kernels_rotary cuda_B1_S2048_H32_D128_R64 0.26 True |
|
|
hf_kernels_rotary cuda_B1_S2048_H32_D64_R32 0.10 True |
|
|
hf_kernels_rotary cuda_B1_S2048_H8_D128_R64 0.10 True |
|
|
hf_kernels_rotary cuda_B1_S2048_H8_D64_R32 0.09 True |
|
|
hf_kernels_rotary cuda_B1_S512_H32_D128_R64 0.09 True |
|
|
hf_kernels_rotary cuda_B1_S512_H32_D64_R32 0.09 True |
|
|
hf_kernels_rotary cuda_B1_S512_H8_D128_R64 0.09 True |
|
|
hf_kernels_rotary cuda_B1_S512_H8_D64_R32 0.09 True |
|
|
hf_kernels_rotary cuda_B2_S128_H32_D128_R64 0.09 True |
|
|
hf_kernels_rotary cuda_B2_S128_H32_D64_R32 0.09 True |
|
|
hf_kernels_rotary cuda_B2_S128_H8_D128_R64 0.09 True |
|
|
hf_kernels_rotary cuda_B2_S128_H8_D64_R32 0.09 True |
|
|
hf_kernels_rotary cuda_B2_S2048_H32_D128_R64 0.85 True |
|
|
hf_kernels_rotary cuda_B2_S2048_H32_D64_R32 0.27 True |
|
|
hf_kernels_rotary cuda_B2_S2048_H8_D128_R64 0.09 True |
|
|
hf_kernels_rotary cuda_B2_S2048_H8_D64_R32 0.09 True |
|
|
hf_kernels_rotary cuda_B2_S512_H32_D128_R64 0.09 True |
|
|
hf_kernels_rotary cuda_B2_S512_H32_D64_R32 0.09 True |
|
|
hf_kernels_rotary cuda_B2_S512_H8_D128_R64 0.09 True |
|
|
hf_kernels_rotary cuda_B2_S512_H8_D64_R32 0.09 True |
|
|
</pre></div> |
|
|
<div class="uv-install-logs" id="uv-logs-benchmark"> |
|
|
<div class="uv-logs-header" onclick="toggleUvLogs(this)">▶ UV Install Logs</div> |
|
|
<div class="uv-logs-content" style="display: none;"> |
|
|
Installed 15 packages in 14ms |
|
|
</div> |
|
|
</div> |
|
|
<div class="cell-stderr">Fetching 5 files: 0%| | 0/5 [00:00<?, ?it/s] |
|
|
Fetching 5 files: 60%|██████ | 3/5 [00:00<00:00, 28.46it/s] |
|
|
Fetching 5 files: 100%|██████████| 5/5 [00:00<00:00, 9.80it/s]</div> |
|
|
<div class="cell-artifacts"> |
|
|
<h4>Artifacts:</h4> |
|
|
<a href="artifacts/benchmark/rotary.jsonl" class="artifact" target="_blank">rotary.jsonl</a> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
</body> |
|
|
</html> |