2026-05-26 11:26:37 -03:00
<!DOCTYPE html>
< html lang = "en" >
< head >
< meta charset = "UTF-8" />
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" />
< title > URL Launcher</ title >
< style >
@ import url ( 'https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600&family=Syne:wght@400;700;800&display=swap' ) ;
*, * :: before , * :: after {
box-sizing : border-box ;
margin : 0 ;
padding : 0 ;
}
: root {
--bg : #0d0d0f ;
--surface : #16161a ;
--border : #2a2a32 ;
--accent : #7fff6e ;
--accent-dim : #3a7a33 ;
--text : #e8e8ec ;
--muted : #6b6b7a ;
--danger : #ff6b6b ;
--radius : 6 px ;
}
body {
2026-05-26 11:55:20 -03:00
width : 600 px ;
/*min-height: 400px;*/
2026-05-26 11:26:37 -03:00
background : var ( -- bg );
color : var ( -- text );
font-family : 'Syne' , sans-serif ;
padding : 0 ;
overflow : hidden ;
}
/* ── Header ── */
. header {
display : flex ;
align-items : center ;
gap : 10 px ;
padding : 16 px 18 px 14 px ;
border-bottom : 1 px solid var ( -- border );
background : var ( -- surface );
}
. logo {
width : 28 px ;
height : 28 px ;
background : var ( -- accent );
border-radius : 50 % ;
display : flex ;
align-items : center ;
justify-content : center ;
flex-shrink : 0 ;
}
. logo svg {
width : 16 px ;
height : 16 px ;
fill : var ( -- bg );
}
. title {
font-size : 15 px ;
font-weight : 800 ;
letter-spacing : 0.04 em ;
text-transform : uppercase ;
color : var ( -- text );
}
. subtitle {
font-family : 'JetBrains Mono' , monospace ;
font-size : 10 px ;
color : var ( -- muted );
margin-left : auto ;
}
/* ── Body ── */
. body {
padding : 16 px 18 px ;
display : flex ;
flex-direction : column ;
gap : 14 px ;
}
/* ── Textarea ── */
. textarea-wrap {
position : relative ;
}
textarea {
width : 100 % ;
2026-05-26 11:55:20 -03:00
height : 190 px ;
2026-05-26 11:26:37 -03:00
background : var ( -- surface );
border : 1 px solid var ( -- border );
border-radius : var ( -- radius );
color : var ( -- text );
font-family : 'JetBrains Mono' , monospace ;
font-size : 12 px ;
line-height : 1.7 ;
padding : 10 px 12 px ;
resize : vertical ;
outline : none ;
transition : border-color 0.15 s ;
caret-color : var ( -- accent );
}
textarea :: placeholder {
color : var ( -- muted );
}
textarea : focus {
border-color : var ( -- accent - dim );
}
. url-count {
position : absolute ;
bottom : 8 px ;
right : 10 px ;
font-family : 'JetBrains Mono' , monospace ;
font-size : 10 px ;
color : var ( -- muted );
pointer-events : none ;
transition : color 0.15 s ;
}
. url-count . has-urls {
color : var ( -- accent );
}
/* ── Options row ── */
. options {
display : flex ;
align-items : center ;
gap : 10 px ;
}
. checkbox-label {
display : flex ;
align-items : center ;
gap : 8 px ;
cursor : pointer ;
user-select : none ;
flex : 1 ;
}
. checkbox-label input [ type = "checkbox" ] {
display : none ;
}
. custom-checkbox {
width : 16 px ;
height : 16 px ;
border : 1.5 px solid var ( -- border );
border-radius : 3 px ;
background : var ( -- surface );
display : flex ;
align-items : center ;
justify-content : center ;
flex-shrink : 0 ;
transition : border-color 0.15 s , background 0.15 s ;
}
. checkbox-label input [ type = "checkbox" ] : checked + . custom-checkbox {
background : var ( -- accent );
border-color : var ( -- accent );
}
. custom-checkbox svg {
display : none ;
width : 10 px ;
height : 10 px ;
}
. checkbox-label input [ type = "checkbox" ] : checked + . custom-checkbox svg {
display : block ;
}
. checkbox-text {
font-size : 12 px ;
color : var ( -- muted );
line-height : 1 ;
}
. checkbox-label : hover . checkbox-text {
color : var ( -- text );
}
/* ── Button ── */
. launch-btn {
background : var ( -- accent );
color : var ( -- bg );
border : none ;
border-radius : var ( -- radius );
font-family : 'Syne' , sans-serif ;
font-size : 12 px ;
font-weight : 700 ;
letter-spacing : 0.06 em ;
text-transform : uppercase ;
padding : 9 px 18 px ;
cursor : pointer ;
white-space : nowrap ;
transition : opacity 0.15 s , transform 0.1 s ;
display : flex ;
align-items : center ;
gap : 6 px ;
}
. launch-btn : hover : not ( : disabled ) {
opacity : 0.88 ;
}
. launch-btn : active : not ( : disabled ) {
transform : scale ( 0.97 );
}
. launch-btn : disabled {
opacity : 0.3 ;
cursor : not-allowed ;
}
. launch-btn svg {
width : 13 px ;
height : 13 px ;
fill : var ( -- bg );
}
/* ── Status bar ── */
. status {
font-family : 'JetBrains Mono' , monospace ;
font-size : 11 px ;
color : var ( -- muted );
min-height : 16 px ;
display : flex ;
align-items : center ;
gap : 6 px ;
}
. status . success { color : var ( -- accent ); }
. status . error { color : var ( -- danger ); }
. status-dot {
width : 6 px ;
height : 6 px ;
border-radius : 50 % ;
background : currentColor ;
flex-shrink : 0 ;
}
/* ── Toolbar above textarea ── */
. textarea-toolbar {
display : flex ;
align-items : center ;
justify-content : space-between ;
margin-bottom : 6 px ;
}
. textarea-label {
font-family : 'JetBrains Mono' , monospace ;
font-size : 10 px ;
letter-spacing : 0.08 em ;
text-transform : uppercase ;
color : var ( -- muted );
}
. extract-btn {
display : flex ;
align-items : center ;
gap : 5 px ;
background : transparent ;
border : 1 px solid var ( -- border );
border-radius : var ( -- radius );
color : var ( -- muted );
font-family : 'JetBrains Mono' , monospace ;
font-size : 10 px ;
letter-spacing : 0.05 em ;
padding : 4 px 9 px ;
cursor : pointer ;
transition : border-color 0.15 s , color 0.15 s , background 0.15 s ;
white-space : nowrap ;
}
. extract-btn svg {
width : 11 px ;
height : 11 px ;
flex-shrink : 0 ;
transition : stroke 0.15 s ;
}
. extract-btn : hover {
border-color : var ( -- accent - dim );
color : var ( -- accent );
background : rgba ( 127 , 255 , 110 , 0.05 );
}
. extract-btn : active {
transform : scale ( 0.96 );
}
. extract-btn . flash {
border-color : var ( -- accent );
color : var ( -- accent );
background : rgba ( 127 , 255 , 110 , 0.1 );
}
2026-05-29 10:42:52 -03:00
/* ── Checkboxes stack ── */
. checkboxes {
display : flex ;
flex-direction : column ;
gap : 8 px ;
flex : 1 ;
}
. delay-hint {
color : var ( -- muted );
font-size : 10 px ;
font-family : 'JetBrains Mono' , monospace ;
}
. checkbox-label . disabled {
opacity : 0.35 ;
pointer-events : none ;
}
2026-05-26 11:26:37 -03:00
/* ── Focus mode indicator ── */
. mode-badge {
display : inline-flex ;
align-items : center ;
gap : 4 px ;
padding : 3 px 8 px ;
border-radius : 100 px ;
font-family : 'JetBrains Mono' , monospace ;
font-size : 10 px ;
font-weight : 600 ;
letter-spacing : 0.03 em ;
background : rgba ( 127 , 255 , 110 , 0.08 );
border : 1 px solid var ( -- accent - dim );
color : var ( -- accent );
transition : opacity 0.2 s ;
}
. mode-badge . off {
background : rgba ( 107 , 107 , 122 , 0.12 );
border-color : var ( -- border );
color : var ( -- muted );
}
. mode-badge :: before {
content : '' ;
width : 5 px ;
height : 5 px ;
border-radius : 50 % ;
background : currentColor ;
}
</ style >
</ head >
< body >
< div class = "header" >
< div class = "logo" >
<!-- Tab icon -->
< svg viewBox = "0 0 16 16" xmlns = "http://www.w3.org/2000/svg" >
< rect x = "1" y = "4" width = "14" height = "10" rx = "1.5" />
< rect x = "1" y = "1" width = "6" height = "4" rx = "1" />
</ svg >
</ div >
< span class = "title" > URL Launcher</ span >
< span class = "subtitle" id = "modeBadge" >
< span class = "mode-badge" id = "modeBadgeInner" > lazy</ span >
</ span >
</ div >
< div class = "body" >
< div class = "textarea-toolbar" >
< span class = "textarea-label" > URLs / Text</ span >
< button class = "extract-btn" id = "extractBtn" title = "Extract all URLs from pasted text" >
< svg viewBox = "0 0 11 11" fill = "none" xmlns = "http://www.w3.org/2000/svg" stroke = "currentColor" stroke-width = "1.4" stroke-linecap = "round" stroke-linejoin = "round" >
< path d = "M4.5 2.5H3a2 2 0 0 0 0 4h1.5" />
< path d = "M6.5 2.5H8a2 2 0 0 1 0 4H6.5" />
< line x1 = "3.5" y1 = "4.5" x2 = "7.5" y2 = "4.5" />
</ svg >
Extract URLs
</ button >
</ div >
< div class = "textarea-wrap" >
< textarea
id = "urlInput"
placeholder = "Paste any text here — emails, docs, HTML, markdown… Hit 'Extract URLs' to pull out links automatically. Or just type one URL per line: https://example.com https://github.com"
spellcheck = "false"
autocomplete = "off"
></ textarea >
< span class = "url-count" id = "urlCount" > 0 URLs</ span >
</ div >
< div class = "options" >
2026-05-29 10:42:52 -03:00
< div class = "checkboxes" >
< label class = "checkbox-label" title = "Each URL opens via an intermediate page and only navigates when you focus that tab" >
< input type = "checkbox" id = "lazyLoad" checked />
< span class = "custom-checkbox" >
< svg viewBox = "0 0 10 10" fill = "none" xmlns = "http://www.w3.org/2000/svg" >
< path d = "M1.5 5L4 7.5L8.5 2.5" stroke = "#0d0d0f" stroke-width = "1.8" stroke-linecap = "round" stroke-linejoin = "round" />
</ svg >
</ span >
< span class = "checkbox-text" > Wait for focus to load</ span >
</ label >
< label class = "checkbox-label" id = "delayedLoadLabel" title = "Each tab auto-loads after a staggered timer: 1s, 2s, 3s… Requires 'Wait for focus'" >
< input type = "checkbox" id = "delayedLoad" checked />
< span class = "custom-checkbox" >
< svg viewBox = "0 0 10 10" fill = "none" xmlns = "http://www.w3.org/2000/svg" >
< path d = "M1.5 5L4 7.5L8.5 2.5" stroke = "#0d0d0f" stroke-width = "1.8" stroke-linecap = "round" stroke-linejoin = "round" />
</ svg >
</ span >
< span class = "checkbox-text" > Delayed load < span class = "delay-hint" id = "delayHint" > (1s, 2s, 3s…)</ span ></ span >
</ label >
</ div >
2026-05-26 11:26:37 -03:00
< button class = "launch-btn" id = "openBtn" disabled >
< svg viewBox = "0 0 13 13" xmlns = "http://www.w3.org/2000/svg" >
< path d = "M2 6.5h9M7.5 3l4 3.5-4 3.5" stroke = "#0d0d0f" stroke-width = "1.6" stroke-linecap = "round" stroke-linejoin = "round" fill = "none" />
</ svg >
Open URLs
</ button >
</ div >
< div class = "status" id = "status" ></ div >
</ div >
< script src = "popup.js" ></ script >
</ body >
</ html >