@@ -378,10 +378,12 @@ function renderPortCard(port, index) {
378378 } ;
379379 li . appendChild ( urlEl ) ;
380380 if ( s . data !== false ) {
381+ const tokenParam = s . token
382+ ? '?token=' + encodeURIComponent ( s . token ) : '' ;
381383 const linksDiv = el ( 'div' , null , 'ws-links' ) ;
382- linksDiv . innerHTML = '<a href="/xterm/' + s . endpoint
384+ linksDiv . innerHTML = '<a href="/xterm/' + s . endpoint + tokenParam
383385 + '" class="detect-link" target="_blank" rel="noopener">Terminal</a>'
384- + '<a href="/raw/' + s . endpoint
386+ + '<a href="/raw/' + s . endpoint + tokenParam
385387 + '" class="detect-link" target="_blank" rel="noopener">Raw</a>' ;
386388 li . appendChild ( linksDiv ) ;
387389 }
@@ -877,6 +879,23 @@ function renderServerBox(srv, index, total) {
877879 wsToken . placeholder = '(use global auth)' ;
878880 wsToken . value = srv . token || '' ;
879881 wsRow2 . appendChild ( wsToken ) ;
882+ const wsGenBtn = el ( 'button' , null , 'btn-icon' ) ;
883+ wsGenBtn . type = 'button' ;
884+ wsGenBtn . title = 'Generate token' ;
885+ wsGenBtn . innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4"/></svg>' ;
886+ wsGenBtn . onclick = ( ) => { wsToken . value = crypto . randomUUID ( ) ; } ;
887+ wsRow2 . appendChild ( wsGenBtn ) ;
888+ const wsCopyBtn = el ( 'button' , null , 'btn-icon' ) ;
889+ wsCopyBtn . type = 'button' ;
890+ wsCopyBtn . title = 'Copy token' ;
891+ wsCopyBtn . innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>' ;
892+ wsCopyBtn . onclick = ( ) => {
893+ if ( wsToken . value ) navigator . clipboard . writeText ( wsToken . value ) . then ( ( ) => {
894+ wsCopyBtn . style . color = 'var(--success)' ;
895+ setTimeout ( ( ) => { wsCopyBtn . style . color = '' ; } , 1000 ) ;
896+ } ) ;
897+ } ;
898+ wsRow2 . appendChild ( wsCopyBtn ) ;
880899 wsDiv . appendChild ( wsRow2 ) ;
881900 box . appendChild ( wsDiv ) ;
882901
@@ -1401,7 +1420,7 @@ function showUserEditor(login, user) {
14011420 <div class="edit-buttons">
14021421 <button type="button" class="btn-primary user-save-btn">Save</button>
14031422 ${ ! isNew ? '<button type="button" class="btn-danger user-delete-btn">Delete</button>' : '' }
1404- <button type="button" class="user-cancel-btn">Cancel</button>
1423+ <button type="button" class="btn btn-secondary user-cancel-btn">Cancel</button>
14051424 </div>
14061425 ` ;
14071426 card . querySelector ( '.user-save-btn' ) . addEventListener ( 'click' , ( ) => saveUser ( isNew ? null : login , card ) ) ;
@@ -1425,7 +1444,7 @@ function showTokenEditor(tokenId, tok) {
14251444 card . className = 'section user-edit' ;
14261445 card . dataset . tokenId = isNew ? 'new' : tokenId ;
14271446 const title = isNew ? 'New API Token' : 'Edit API Token' ;
1428- const tokenValue = tok . token || generateToken ( ) ;
1447+ const tokenValue = tok . token || crypto . randomUUID ( ) ;
14291448 card . innerHTML = `
14301449 <h3>${ title } </h3>
14311450 <div class="field-row">
@@ -1445,11 +1464,11 @@ function showTokenEditor(tokenId, tok) {
14451464 <div class="edit-buttons">
14461465 <button type="button" class="btn-primary token-save-btn">Save</button>
14471466 ${ ! isNew ? '<button type="button" class="btn-danger token-delete-btn">Delete</button>' : '' }
1448- <button type="button" class="token-cancel-btn">Cancel</button>
1467+ <button type="button" class="btn btn-secondary token-cancel-btn">Cancel</button>
14491468 </div>
14501469 ` ;
14511470 card . querySelector ( '.token-generate-btn' ) . addEventListener ( 'click' , ( ) => {
1452- card . querySelector ( '.token-value-input' ) . value = generateToken ( ) ;
1471+ card . querySelector ( '.token-value-input' ) . value = crypto . randomUUID ( ) ;
14531472 } ) ;
14541473 card . querySelector ( '.token-copy-btn' ) . addEventListener ( 'click' , ( ) => {
14551474 const input = card . querySelector ( '.token-value-input' ) ;
@@ -1468,11 +1487,6 @@ function showTokenEditor(tokenId, tok) {
14681487 card . querySelector ( '.token-name' ) . focus ( ) ;
14691488}
14701489
1471- function generateToken ( ) {
1472- const arr = new Uint8Array ( 32 ) ;
1473- crypto . getRandomValues ( arr ) ;
1474- return Array . from ( arr , b => b . toString ( 16 ) . padStart ( 2 , '0' ) ) . join ( '' ) ;
1475- }
14761490
14771491async function saveUser ( login , card ) {
14781492 const isNew = login === null ;
@@ -1641,7 +1655,7 @@ function showSessionEditor() {
16411655 </div>
16421656 <div class="edit-buttons">
16431657 <button type="button" class="btn-primary" id="save-session-btn">Save</button>
1644- <button type="button" id="cancel-session-btn">Cancel</button>
1658+ <button type="button" class="btn btn-secondary" id="cancel-session-btn">Cancel</button>
16451659 </div>
16461660 ` ;
16471661 existing . replaceWith ( card ) ;
@@ -1701,7 +1715,7 @@ function showHttpEditor(index, srv) {
17011715 <div class="edit-buttons">
17021716 <button type="button" class="btn-primary http-save-btn">Save</button>
17031717 ${ ! isNew ? '<button type="button" class="btn-danger http-delete-btn">Delete</button>' : '' }
1704- <button type="button" class="http-cancel-btn">Cancel</button>
1718+ <button type="button" class="btn btn-secondary http-cancel-btn">Cancel</button>
17051719 </div>
17061720 ` ;
17071721 card . querySelector ( '.http-ssl' ) . addEventListener ( 'change' , e => {
0 commit comments