diff options
author | bptato <nincsnevem662@gmail.com> | 2024-09-22 13:14:36 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2024-09-22 13:37:13 +0200 |
commit | e23fa780cf2fff7146efcd64b2806ce428858b80 (patch) | |
tree | 1c27dc0a50c03bc1d4348d9b96328009e0adce8f | |
parent | 9386feca5e39b89676dd410f6331553c37434239 (diff) | |
download | chawan-e23fa780cf2fff7146efcd64b2806ce428858b80.tar.gz |
pager: improve hover text handling
* align status truncating behavior with w3m (not exactly, clipping is still different, but this should be fine for now) * add "su" for "show last alert" - w3m's solution here is to scroll one char at a time with "u", but that's extremely annoying to use. We already have a line editor that can navigate lines, so reuse that instead. * fix peekCursor showing empty text * update todo
-rw-r--r-- | doc/api.md | 5 | ||||
-rw-r--r-- | doc/config.md | 33 | ||||
-rw-r--r-- | res/chawan.html | 13 | ||||
-rw-r--r-- | res/config.toml | 4 | ||||
-rw-r--r-- | src/local/container.nim | 3 | ||||
-rw-r--r-- | src/local/lineedit.nim | 3 | ||||
-rw-r--r-- | src/local/pager.nim | 115 | ||||
-rw-r--r-- | todo | 27 |
8 files changed, 136 insertions, 67 deletions
diff --git a/doc/api.md b/doc/api.md index 004b52af..ec41f390 100644 --- a/doc/api.md +++ b/doc/api.md @@ -223,6 +223,11 @@ press again -> URL)</td> </tr> <tr> +<td>`showFullAlert()`</td> +<td>Show the last alert inside the line editor.</td> +</tr> + +<tr> <td>`ask(prompt)`</td> <td>Ask the user for confirmation. Returns a promise which resolves to a boolean value indicating whether the user responded with yes.<br> diff --git a/doc/config.md b/doc/config.md index d4253b4e..13afec04 100644 --- a/doc/config.md +++ b/doc/config.md @@ -484,7 +484,7 @@ images. <tr> <td>sixel-colors</td> -<td>"auto" / 3..65535</td> +<td>"auto" / 2..65535</td> <td>Only applies when `display.image-mode="sixel"`. Setting a number overrides the number of sixel color registers reported by the terminal, while "auto" leaves color detection to Chawan.<br> @@ -888,16 +888,19 @@ Examples: <table> <tr> +<th>Default key</th> <th>Name</th> <th>Function</th> </tr> <tr> +<td><kbd>q</kbd></td> <td>`cmd.pager.quit`</td> <td>Exit the browser.</td> </tr> <tr> +<td><kbd>C-z</kbd></td> <td>`cmd.pager.suspend`</td> <td>Temporarily suspend the browser<br> Note: this also suspends e.g. buffer processes or CGI scripts. So if you are @@ -905,56 +908,67 @@ downloading something, that will be delayed until you restart the process.</td> </tr> <tr> +<td><kbd>C-l</kbd></td> <td>`cmd.pager.load`</td> <td>Open the current address in the URL bar.</td> </tr> <tr> +<td><kbd>C-k</kbd></td> <td>`cmd.pager.webSearch`</td> <td>Open the URL bar with an arbitrary search engine. At the moment, this is DuckDuckGo Lite, but this may change in the future.</td> </tr> <tr> +<td><kbd>M-u</kbd></td> <td>`cmd.pager.dupeBuffer`</td> <td>Duplicate the current buffer by loading its source to a new buffer.</td> </tr> <tr> +<td><kbd>U</kbd></td> <td>`cmd.pager.reloadBuffer`</td> <td>Open a new buffer with the current buffer's URL, replacing the current buffer.</td> </tr> <tr> +<td><kbd>C-g</kbd></td> <td>`cmd.pager.lineInfo`</td> <td>Display information about the current line on the status line.</td> </tr> <tr> +<td><kbd>\</kbd></td> <td>`cmd.pager.toggleSource`</td> <td>If viewing an HTML buffer, open a new buffer with its source. Otherwise, open the current buffer's contents as HTML.</td> </tr> <tr> +<td><kbd>D</kbd></td> <td>`cmd.pager.discardBuffer`</td> <td>Discard the current buffer, and move back to the previous/next buffer depending on what the previously viewed buffer was.</td> </tr> <tr> +<td><kbd>d,</kbd>, <kbd>d.</kbd></td> <td>`cmd.pager.discardBufferPrev`, `cmd.pager.discardBufferNext`</td> <td>Discard the current buffer, and move back to the previous/next buffer, or open the link under the cursor.</td> </tr> <tr> +<td><kbd>M-d</kbd></td> <td>`cmd.pager.discardTree`</td> <td>Discard all child buffers of the current buffer.</td> </tr> <tr> +<td><kbd>.</kbd>, <kbd>,</kbd>, <kbd>M-,</kbd>, <kbd>M-.</kbd>, +<kbd>M-/</kbd></td> <td>`cmd.pager.nextBuffer`, `cmd.pager.prevBuffer`, `cmd.pager.prevSiblingBuffer`, `cmd.pager.nextSiblingBufer`, `cmd.pager.parentBuffer`</td> @@ -969,33 +983,39 @@ opened from, even if e.g. the user returns and opens another page "in between". </tr> <tr> +<td><kbd>M-c</kbd></td> <td>`cmd.pager.enterCommand`</td> <td>Directly enter a JavaScript command. Note that this interacts with the pager, not the website being displayed.</td> </tr> <tr> +<td>None</td> <td>`cmd.pager.searchForward`, `cmd.pager.searchBackward`</td> <td>Search for a string in the current buffer, forwards or backwards.</td> </tr> <tr> +<td><kbd>/</kbd>, <kbd>?</kbd></td> <td>`cmd.pager.isearchForward`, `cmd.pager.searchBackward`</td> <td>Incremental-search for a string, highlighting the first result, forwards or backwards.</td> </tr> <tr> +<td><kbd>n</kbd>, <kbd>N</kbd></td> <td>`cmd.pager.searchNext`, `cmd.pager.searchPrev`</td> <td>Jump to the nth (or if unspecified, first) next/previous search result.</td> </tr> <tr> +<td><kbd>c</kbd></td> <td>`cmd.pager.peek`</td> <td>Display a message of the current buffer's URL on the status line.</td> </tr> <tr> +<td><kbd>u</kbd></td> <td>`cmd.pager.peekCursor`</td> <td>Display a message of the URL or title under the cursor on the status line. Multiple calls allow cycling through the two. (i.e. by default, press u once -> @@ -1003,21 +1023,32 @@ title, press again -> URL)</td> </tr> <tr> +<td><kbd>su</kbd></td> +<td>`cmd.pager.showFullAlert`</td> +<td>Show the last alert inside the line editor. You can also view previous +ones using C-p or C-n.</td> +</tr> + +<tr> +<td><kbd>M-y</kbd></td> <td>`cmd.pager.copyURL`</td> <td>Copy the current buffer's URL to the system clipboard.</td> </tr> <tr> +<td><kbd>yu</kbd></td> <td>`cmd.pager.copyCursorLink`</td> <td>Copy the link under the cursor to the system clipboard.</td> </tr> <tr> +<td><kbd>yI</kbd></td> <td>`cmd.pager.copyCursorImage`</td> <td>Copy the URL of the image under the cursor to the system clipboard.</td> </tr> <tr> +<td><kbd>M-p</kbd></td> <td>`cmd.pager.gotoClipboardURL`</td> <td>Go to the URL currently on the clipboard.</td> </tr> diff --git a/res/chawan.html b/res/chawan.html index 06d0597a..72bd041f 100644 --- a/res/chawan.html +++ b/res/chawan.html @@ -24,15 +24,12 @@ kbd { <center><h1>Welcome to Chawan!</h1></center> <pre class=logo> _......................._ -fr.,~.~,,~~.~~~,~,~~~~,,.bl -I!www~www~www~www~www~www!ds +fr.,~.~,,~~.~~~,~,~~~~,~.l\ +I!www~www~www~www~www~www!dp lCHCHCHCHCHCHCHCHCHCHCHCHCp - kCHCHCHCHCHCHCHCHCHCHCHCp sAAAAAAAAAAAAAAAAAAAAAp - l<u>S</u>WWWWWWWWWWWWWWWWW<u>Z</u>i - lSAAAAAAAAAAAAAZ/ - \<u>ZM</u>NNNNNNN<u>MZ</u>/ - \<u>HHHHH</u>/ + SWWWWWWWWWWWWWWWWWZ + \Z<u>MNMNMNMNMNM</u>Z/ </pre> <p> This is the default visual home page. You can change it in your configuration @@ -62,6 +59,8 @@ came from <li><kbd>d,</kbd>, <kbd>d.</kbd>: discard (delete) current buffer, then move to previous/next buffer <li><kbd>M-y</kbd>: copy current buffer's URL to clipboard (needs xsel) +<li><kbd>u</kbd>: view link/title text currently under the cursor +<li><kbd>su</kbd>: show last alert message <li><kbd>yu</kbd>: copy the link currently under the cursor to clipboard (needs xsel) <li><kbd>yI</kbd>: copy the image link currently under the cursor to clipboard diff --git a/res/config.toml b/res/config.toml index 1a7318a0..6cd83253 100644 --- a/res/config.toml +++ b/res/config.toml @@ -47,7 +47,7 @@ gotoClipboardURL = ''' } ''' peek = '() => pager.alert(pager.url)' -peekCursor = '() => pager.peekCursor()' +peekCursor = 'n => pager.peekCursor(n)' toggleWrap = ''' () => { config.search.wrap = !config.search.wrap; @@ -85,6 +85,7 @@ toggleCommandMode = ''' console.hide(); } ''' +showFullAlert = '() => pager.showFullAlert()' [cmd.buffer] cursorLeft = 'n => pager.cursorLeft(n)' @@ -420,6 +421,7 @@ n = 'cmd.pager.searchNext' N = 'cmd.pager.searchPrev' c = 'cmd.pager.peek' u = 'cmd.pager.peekCursor' +su = 'cmd.pager.showFullAlert' C-w = 'cmd.pager.toggleWrap' M-y = 'cmd.pager.copyURL' yc = 'pager.alert("Please use `yu` to copy URLs")' diff --git a/src/local/container.nim b/src/local/container.nim index 6310c8d6..8f295ad9 100644 --- a/src/local/container.nim +++ b/src/local/container.nim @@ -1930,7 +1930,8 @@ proc peekCursor(container: Container) {.jsfunc.} = p = low(HoverType) if container.hoverText[p] != "" or p == container.lastPeek: break - container.alert($p & ": " & container.hoverText[p]) + if container.hoverText[p] != "": + container.alert($p & ": " & container.hoverText[p]) container.lastPeek = p func hoverLink(container: Container): string {.jsfget.} = diff --git a/src/local/lineedit.nim b/src/local/lineedit.nim index edb6ee09..2f998b84 100644 --- a/src/local/lineedit.nim +++ b/src/local/lineedit.nim @@ -18,7 +18,7 @@ type lesEdit, lesFinish, lesCancel LineHistory* = ref object - lines: seq[string] + lines*: seq[string] LineEdit* = ref object news*: string @@ -299,6 +299,7 @@ proc nextHist(edit: LineEdit) {.jsfunc.} = edit.begin() edit.end() edit.histtmp = "" + edit.redraw = true proc windowChange*(edit: LineEdit; attrs: WindowAttributes) = edit.maxwidth = attrs.width - edit.promptw - 1 diff --git a/src/local/pager.nim b/src/local/pager.nim index 0bd60abb..fa205596 100644 --- a/src/local/pager.nim +++ b/src/local/pager.nim @@ -68,6 +68,7 @@ type lmGotoLine = "Goto line: " lmDownload = "(Download)Save file to: " lmBufferFile = "(Upload)Filename: " + lmAlert = "Alert: " # fdin is the original fd; fdout may be the same, or different if mailcap # is used. @@ -129,15 +130,15 @@ type devRandom: PosixStream display: Surface forkserver*: ForkServer - formRequestMap*: Table[string, FormRequestType] hasload*: bool # has a page been successfully loaded since startup? inputBuffer*: string # currently uninterpreted characters iregex: Result[Regex, string] isearchpromise: EmptyPromise jsctx: JSContext + lastAlert: string # last alert seen by the user lineData: LineData lineedit*: LineEdit - linehist: array[LineMode, LineHistory] + lineHist: array[LineMode, LineHistory] linemode: LineMode loader*: FileLoader luctx: LUContext @@ -160,6 +161,7 @@ jsDestructor(Pager) # Forward declarations proc alert*(pager: Pager; msg: string) +proc getLineHist(pager: Pager; mode: LineMode): LineHistory template attrs(pager: Pager): WindowAttributes = pager.term.attrs @@ -266,9 +268,9 @@ proc searchPrev(pager: Pager; n = 1) {.jsfunc.} = pager.alert("No previous regular expression") proc getLineHist(pager: Pager; mode: LineMode): LineHistory = - if pager.linehist[mode] == nil: - pager.linehist[mode] = newLineHistory() - return pager.linehist[mode] + if pager.lineHist[mode] == nil: + pager.lineHist[mode] = newLineHistory() + return pager.lineHist[mode] proc setLineEdit(pager: Pager; mode: LineMode; current = ""; hide = false; extraPrompt = "") = @@ -279,6 +281,11 @@ proc setLineEdit(pager: Pager; mode: LineMode; current = ""; hide = false; {}, hide, hist, pager.luctx) pager.linemode = mode +# Reuse the line editor as an alert message viewer. +proc showFullAlert(pager: Pager) {.jsfunc.} = + if pager.lastAlert != "": + pager.setLineEdit(lmAlert, pager.lastAlert) + proc clearLineEdit(pager: Pager) = pager.lineedit = nil if pager.term.isatty() and pager.config.input.use_mouse: @@ -368,38 +375,41 @@ proc launchPager*(pager: Pager; istream: PosixStream; selector: Selector[int]) = proc buffer(pager: Pager): Container {.jsfget, inline.} = return pager.container -# Note: this function does not work correctly if start < i of last written char +# Note: this function does not work correctly if start < x of last written char proc writeStatusMessage(pager: Pager; str: string; format = Format(); - start = 0; maxwidth = -1; clip = '$'): int {.discardable.} = + start = 0; maxwidth = -1; clip = '$'): int = var maxwidth = maxwidth if maxwidth == -1: maxwidth = pager.status.grid.len - var i = start + var x = start let e = min(start + maxwidth, pager.status.grid.width) - if i >= e: - return i + if x >= e: + return x pager.status.redraw = true + var lx = 0 for u in str.points: let w = u.width() - if i + w >= e: - pager.status.grid[i].format = format - pager.status.grid[i].str = $clip - inc i # Note: we assume `clip' is 1 cell wide + if x + w > e: # clip if we overflow (but not on exact fit) + if lx < e: + pager.status.grid[lx].format = format + pager.status.grid[lx].str = $clip + x = lx + 1 # clip must be 1 cell wide break if u.isControlChar(): - pager.status.grid[i].str = "^" - pager.status.grid[i + 1].str = $getControlLetter(char(u)) - pager.status.grid[i + 1].format = format + pager.status.grid[x].str = "^" + pager.status.grid[x + 1].str = $getControlLetter(char(u)) + pager.status.grid[x + 1].format = format else: - pager.status.grid[i].str = u.toUTF8() - pager.status.grid[i].format = format - i += w - result = i + pager.status.grid[x].str = u.toUTF8() + pager.status.grid[x].format = format + lx = x + x += w + result = x var def = Format() - while i < e: - pager.status.grid[i].str = "" - pager.status.grid[i].format = def - inc i + while x < e: + pager.status.grid[x].str = "" + pager.status.grid[x].format = def + inc x # Note: should only be called directly after user interaction. proc refreshStatusMsg*(pager: Pager) = @@ -407,29 +417,49 @@ proc refreshStatusMsg*(pager: Pager) = if container == nil: return if pager.askpromise != nil: return if pager.precnum != 0: - pager.writeStatusMessage($pager.precnum & pager.inputBuffer) + discard pager.writeStatusMessage($pager.precnum & pager.inputBuffer) elif pager.inputBuffer != "": - pager.writeStatusMessage(pager.inputBuffer) + discard pager.writeStatusMessage(pager.inputBuffer) elif pager.alerts.len > 0: pager.alertState = pasAlertOn - pager.writeStatusMessage(pager.alerts[0]) + discard pager.writeStatusMessage(pager.alerts[0]) + # save to alert history + if pager.lastAlert != "": + let hist = pager.getLineHist(lmAlert) + if hist.lines.len == 0 or hist.lines[^1] != pager.lastAlert: + if hist.lines.len > 19: + hist.lines.delete(0) + hist.lines.add(move(pager.lastAlert)) + pager.lastAlert = move(pager.alerts[0]) pager.alerts.delete(0) else: var format = Format(flags: {ffReverse}) pager.alertState = pasNormal container.clearHover() - var msg = $(container.cursory + 1) & "/" & $container.numLines & " (" & - $container.atPercentOf() & "%)" - let mw = pager.writeStatusMessage(msg, format) - let title = " <" & container.getTitle() & ">" + var msg = $(container.cursory + 1) & "/" & $container.numLines & + " (" & $container.atPercentOf() & "%)" & + " <" & container.getTitle() let hover = container.getHoverText() - if hover.len == 0: - pager.writeStatusMessage(title, format, mw) - else: - let hover2 = " " & hover - let maxwidth = pager.status.grid.width - hover2.width() - mw - let tw = pager.writeStatusMessage(title, format, mw, maxwidth, '>') - pager.writeStatusMessage(hover2, format, tw) + let sl = hover.notwidth() + var l = 0 + var i = 0 + var maxw = pager.status.grid.width - 1 # -1 for '>' + if sl > 0: + maxw -= 1 # plus one blank + while i < msg.len: + let pi = i + let u = msg.nextUTF8(i) + l += u.width() + if l + sl >= maxw: + i = pi + break + msg.setLen(i) + if i > 0: + msg &= ">" + if sl > 0: + msg &= ' ' + msg &= hover + discard pager.writeStatusMessage(msg, format) # Call refreshStatusMsg if no alert is being displayed on the screen. # Alerts take precedence over load info, but load info is preserved when no @@ -671,7 +701,7 @@ proc onSetLoadInfo(pager: Pager; container: Container) = if container.loadinfo == "": pager.alertState = pasNormal else: - pager.writeStatusMessage(container.loadinfo) + discard pager.writeStatusMessage(container.loadinfo) pager.alertState = pasLoadInfo proc newContainer(pager: Pager; bufferConfig: BufferConfig; @@ -897,7 +927,8 @@ proc nextSiblingBuffer(pager: Pager): bool {.jsfunc.} = return true proc alert*(pager: Pager; msg: string) {.jsfunc.} = - pager.alerts.add(msg) + if msg != "": + pager.alerts.add(msg) # replace target with container in the tree proc replace*(pager: Pager; target, container: Container) = @@ -1451,7 +1482,7 @@ proc updateReadLine*(pager: Pager) = ) else: pager.saveTo(data, lineedit.news) - of lmISearchF, lmISearchB: discard + of lmISearchF, lmISearchB, lmAlert: discard of lesCancel: case pager.linemode of lmUsername, lmPassword: pager.discardBuffer() diff --git a/todo b/todo index c08b289a..e6b14ece 100644 --- a/todo +++ b/todo @@ -1,7 +1,6 @@ compilation: - reduce binary size * fbf for unifont - * maybe use system wcwidth? charsets: - set up some fuzzer - use appropriate charsets in forms, urls, etc. @@ -14,14 +13,13 @@ display: config: - important: fix crash on missing /tmp dir with default config - important: config editor -- completely replace siteconf; the new solution should: - * not be based on table arrays - * allow overriding pretty much every global value per URL - * allow better URL matching (regexes aren't great for this task) - * be called url-config - * allow matching $TERM string, buffer groups (but maybe this should - be a separate setting?) -- add per-scheme configuration (e.g. proto.gemini.known-hosts = '/some/path') +- switch from table arrays to tables +- better siteconf URL matching +- $TERM-based display config +* better path handling (e.g. inline files, so we could get rid of css + "include/inline" etc.) +- add per-scheme env var configuration (e.g. + proto.gemini.known-hosts = '/some/path'; maybe also with inline JS?) - add RPC for CGI scripts e.g. toggle settings/issue downloads/etc * also some way to set permissions for RPC calls buffer: @@ -51,7 +49,7 @@ javascript: - add support for JS mixins - distinguish double from unrestricted double - better dom support: more events, CSSOM, ... -- implement ReadableStream, XHR +- ReadableStream - separate console for each buffer - buffer selection layout engine: @@ -72,12 +70,13 @@ layout engine: - writing-mode, grid, ruby, ... (i.e. cool new stuff) images: - z order, proper image blending -- incremental decoding, interlaced images, animation +- animation man: - add a DOM -> man page converter so that we do not depend on pandoc for man page conversion +- move default keybinding definitions to man page gmifetch: - rewrite in Nim -etc: -- orc support -- maybe windows support? (blocker: needs a windows machine) +tests: +- network/XHR (make net test async?) +- pager? (how?) |