mirror of
https://github.com/vale981/tridactyl
synced 2025-03-05 09:31:41 -05:00
Merge branch 'master' of github.com:cmcaine/tridactyl into orangenschalen-variable_hintname_length
This commit is contained in:
commit
9dd2555984
10 changed files with 294 additions and 39 deletions
26
doc/changelog.md
Normal file
26
doc/changelog.md
Normal file
|
@ -0,0 +1,26 @@
|
|||
# Tridactyl changelogs
|
||||
|
||||
## Release 1.7.0
|
||||
|
||||
- History completion is massively improved: much faster, more relevant results, and less janky as you type.
|
||||
- User configuration
|
||||
- set [setting] without a value will inform you of the current value
|
||||
- Add configuration options for hinting: `hintchars` and `hintorder`
|
||||
- Add unset for resetting a bind to default
|
||||
- You can now change default search engine with e.g, `set searchengine bing` (#60)
|
||||
- The default new tab page can be replaced with any URL via `set newtab [url]` (#59)
|
||||
- Add `gh` and `gH` and "homepages" setting (#96)
|
||||
- Shift-tab and tab now will cycle around completions correctly
|
||||
- `ys` now works on some older pages
|
||||
- Add bmarks command for searching through bookmarks (#167)
|
||||
- Add `hint -c [selector]`: add hints that match CSS selector
|
||||
- Add text-to-speech hint mode on `;r`
|
||||
- Allow `;p` to yank any element which contains text
|
||||
- Add `;#` hint yank anchor mode
|
||||
- Improve hint CSS by adding a border and making background semi-transparent
|
||||
- Add `tabonly` command
|
||||
- Fix hinting mysteriously not working on some pages (#168)
|
||||
- Fix issue where command line would invisibly cover up part of the screen (#170)
|
||||
- Bookmarks can now have spaces in their titles
|
||||
- Fix some hints on sites such as pcgamer.co.uk
|
||||
- Long page titles will no longer appear after URLs in completions
|
|
@ -6,7 +6,7 @@ Replace Firefox's default control mechanism with one modelled on the one true ed
|
|||
|
||||
## Installing
|
||||
|
||||
[Get our "beta" builds!][amo-betas] These are updated on AMO with each commit to master on this repo; your browser will automatically update from there once a day. If you want more frequent updates, you can change `extensions.update.interval` in `about:config` to whatever time you want, say, 15 minutes (900 seconds).
|
||||
[Get our "beta" builds!][amo-betas] These are updated on AMO with each commit to master on this repo; your browser will automatically update from there once a day. If you want more frequent updates, you can change `extensions.update.interval` in `about:config` to whatever time you want, say, 15 minutes (900 seconds). Changelogs for the stable versions on the AMO can be found [here](https://github.com/cmcaine/tridactyl/blob/master/doc/changelog.md).
|
||||
|
||||
Type `:help` for online help once you're in :)
|
||||
|
||||
|
@ -76,7 +76,7 @@ A pre-commit hook is added by `npm install` that simply runs `npm test`. If you
|
|||
|
||||
Ask in `#tridactyl` on [matrix.org][matrix-link], freenode, or [gitter][gitter-link]. We're friendly!
|
||||
|
||||
Default keybindings are currently best discovered by reading the source for the [normal mode parser](./src/parsers/normalmode.ts).
|
||||
Default keybindings are currently best discovered by reading the [default config](./src/config.ts).
|
||||
|
||||
Development notes are in the doc directory, but they're mostly out of date now. Code is quite short and not *too* badly commented, though.
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import * as excmds from './excmds_background'
|
|||
import * as commandline_background from './commandline_background'
|
||||
import * as controller from './controller'
|
||||
import * as convert from './convert'
|
||||
import * as config from './config'
|
||||
import * as dom from './dom'
|
||||
import * as hinting_background from './hinting_background'
|
||||
import * as gobble_mode from './parsers/gobblemode'
|
||||
|
@ -34,6 +35,7 @@ import * as webext from './lib/webext'
|
|||
commandline_background,
|
||||
controller,
|
||||
convert,
|
||||
config,
|
||||
dom,
|
||||
hinting_background,
|
||||
gobble_mode,
|
||||
|
|
|
@ -40,6 +40,7 @@ function enableCompletions() {
|
|||
activeCompletions = [
|
||||
new Completions.BufferCompletionSource(completionsDiv),
|
||||
new Completions.HistoryCompletionSource(completionsDiv),
|
||||
new Completions.BmarkCompletionSource(completionsDiv),
|
||||
]
|
||||
|
||||
const fragment = document.createDocumentFragment()
|
||||
|
@ -197,21 +198,22 @@ function history(n){
|
|||
/* Send the commandline to the background script and await response. */
|
||||
function process() {
|
||||
console.log(clInput.value)
|
||||
clInput.value = getCompletion() || clInput.value
|
||||
console.log(clInput.value)
|
||||
sendExstr(clInput.value)
|
||||
const command = getCompletion() || clInput.value
|
||||
console.log(command)
|
||||
|
||||
hide_and_clear()
|
||||
|
||||
// Save non-secret commandlines to the history.
|
||||
const [func,...args] = clInput.value.trim().split(/\s+/)
|
||||
const [func,...args] = command.trim().split(/\s+/)
|
||||
if (! browser.extension.inIncognitoContext &&
|
||||
! (func === 'winopen' && args[0] === '-private')
|
||||
) {
|
||||
state.cmdHistory = state.cmdHistory.concat([clInput.value])
|
||||
state.cmdHistory = state.cmdHistory.concat([command])
|
||||
}
|
||||
console.log(state.cmdHistory)
|
||||
cmdline_history_position = 0
|
||||
|
||||
hide_and_clear()
|
||||
sendExstr(command)
|
||||
}
|
||||
|
||||
export function fillcmdline(newcommand?: string, trailspace = true){
|
||||
|
|
|
@ -301,8 +301,10 @@ abstract class CompletionSourceFuse extends CompletionSource {
|
|||
if (this.state != "hidden"){
|
||||
let visopts = this.options.filter((o) => o.state != "hidden")
|
||||
let currind = visopts.findIndex((o) => o.state == "focused")
|
||||
if ((inc < 0) && (currind == -1)) inc += 1
|
||||
this.deselect()
|
||||
this.select(visopts[currind + inc])
|
||||
this.select(visopts[(currind + inc + visopts.length) % visopts.length
|
||||
])
|
||||
return true
|
||||
} else return false
|
||||
}
|
||||
|
@ -338,11 +340,109 @@ class HistoryCompletionOption extends CompletionOptionHTML implements Completion
|
|||
}
|
||||
}
|
||||
|
||||
class BmarkCompletionOption extends CompletionOptionHTML implements CompletionOptionFuse {
|
||||
public fuseKeys = []
|
||||
|
||||
constructor(public value: string, bmark: browser.bookmarks.BookmarkTreeNode) {
|
||||
super()
|
||||
if (! bmark.title) {
|
||||
bmark.title = new URL(bmark.url).host
|
||||
}
|
||||
|
||||
// Push properties we want to fuzmatch on
|
||||
this.fuseKeys.push(bmark.title, bmark.url)
|
||||
|
||||
// Create HTMLElement
|
||||
// need to download favicon
|
||||
const favIconUrl = DEFAULT_FAVICON
|
||||
// const favIconUrl = tab.favIconUrl ? tab.favIconUrl : DEFAULT_FAVICON
|
||||
this.html = html`<tr class="HistoryCompletionOption option">
|
||||
<td class="prefix">${"".padEnd(2)}</td>
|
||||
<td></td>
|
||||
<td>${bmark.title}</td>
|
||||
<td><a class="url" target="_blank" href=${bmark.url}>${bmark.url}</a></td>
|
||||
</tr>`
|
||||
}
|
||||
}
|
||||
|
||||
function sleep(ms: number) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
|
||||
export class BmarkCompletionSource extends CompletionSourceFuse {
|
||||
public options: BmarkCompletionOption[]
|
||||
|
||||
constructor(private _parent) {
|
||||
super(
|
||||
[
|
||||
"bmarks ",
|
||||
],
|
||||
"BmarkCompletionSource", "Bookmarks"
|
||||
)
|
||||
|
||||
this._parent.appendChild(this.node)
|
||||
}
|
||||
|
||||
public async filter(exstr: string) {
|
||||
this.lastExstr = exstr
|
||||
const [prefix, query] = this.splitOnPrefix(exstr)
|
||||
|
||||
// Hide self and stop if prefixes don't match
|
||||
if (prefix) {
|
||||
// Show self if prefix and currently hidden
|
||||
if (this.state === 'hidden') {
|
||||
this.state = 'normal'
|
||||
}
|
||||
} else {
|
||||
this.state = 'hidden'
|
||||
return
|
||||
}
|
||||
|
||||
this.options = (await this.scoreOptions(query, 10)).map(
|
||||
page => new BmarkCompletionOption(page.url, page)
|
||||
)
|
||||
|
||||
this.updateChain()
|
||||
}
|
||||
|
||||
updateChain() {
|
||||
// Options are pre-trimmed to the right length.
|
||||
this.options.forEach(option => option.state = 'normal')
|
||||
|
||||
// Call concrete class
|
||||
this.updateDisplay()
|
||||
}
|
||||
|
||||
onInput() {}
|
||||
|
||||
private async scoreOptions(query: string, n: number) {
|
||||
// Search bookmarks, dedupe and sort by frecency
|
||||
let bookmarks = await browserBg.bookmarks.search({query})
|
||||
bookmarks = bookmarks.filter(b=>{
|
||||
try {
|
||||
return new URL(b.url)
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
})
|
||||
|
||||
bookmarks.sort((a, b) => b.dateAdded - a.dateAdded)
|
||||
|
||||
return bookmarks.slice(0, n)
|
||||
}
|
||||
|
||||
select(option: CompletionOption) {
|
||||
if (this.lastExstr !== undefined && option !== undefined) {
|
||||
this.completion = "open " + option.value
|
||||
option.state = 'focused'
|
||||
this.lastFocused = option
|
||||
} else {
|
||||
throw new Error("lastExstr and option must be defined!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class HistoryCompletionSource extends CompletionSourceFuse {
|
||||
public options: HistoryCompletionOption[]
|
||||
|
||||
|
|
|
@ -80,6 +80,28 @@ const DEFAULTS = o({
|
|||
"A": "bmark",
|
||||
}),
|
||||
"searchengine": "google",
|
||||
"searchurls": o({
|
||||
"google":"https://www.google.com/search?q=",
|
||||
"scholar":"https://scholar.google.com/scholar?q=",
|
||||
"googleuk":"https://www.google.co.uk/search?q=",
|
||||
"bing":"https://www.bing.com/search?q=",
|
||||
"duckduckgo":"https://duckduckgo.com/?q=",
|
||||
"yahoo":"https://search.yahoo.com/search?p=",
|
||||
"twitter":"https://twitter.com/search?q=",
|
||||
"wikipedia":"https://en.wikipedia.org/wiki/Special:Search/",
|
||||
"youtube":"https://www.youtube.com/results?search_query=",
|
||||
"amazon":"https://www.amazon.com/s/ref=nb_sb_noss?url=search-alias%3Daps&field-keywords=",
|
||||
"amazonuk":"https://www.amazon.co.uk/s/ref=nb_sb_noss?url=search-alias%3Daps&field-keywords=",
|
||||
"startpage":"https://startpage.com/do/search?language=english&cat=web&query=",
|
||||
"github":"https://github.com/search?utf8=✓&q=",
|
||||
"searx":"https://searx.me/?category_general=on&q=",
|
||||
"cnrtl":"http://www.cnrtl.fr/lexicographie/",
|
||||
"osm":"https://www.openstreetmap.org/search?query=",
|
||||
"mdn":"https://developer.mozilla.org/en-US/search?q=",
|
||||
"gentoo_wiki":"https://wiki.gentoo.org/index.php?title=Special%3ASearch&profile=default&fulltext=Search&search=",
|
||||
"qwant":"https://www.qwant.com/?q=",
|
||||
|
||||
}),
|
||||
"newtab": undefined,
|
||||
"storageloc": "sync",
|
||||
"homepages": [],
|
||||
|
|
|
@ -14,6 +14,7 @@ console.log("Tridactyl content script loaded, boss!")
|
|||
// Add various useful modules to the window for debugging
|
||||
import * as commandline_content from './commandline_content'
|
||||
import * as convert from './convert'
|
||||
import * as config from './config'
|
||||
import * as dom from './dom'
|
||||
import * as excmds from './excmds_content'
|
||||
import * as hinting_content from './hinting'
|
||||
|
@ -28,6 +29,7 @@ import * as webext from './lib/webext'
|
|||
browserBg: webext.browserBg,
|
||||
commandline_content,
|
||||
convert,
|
||||
config,
|
||||
dom,
|
||||
excmds,
|
||||
hinting_content,
|
||||
|
|
143
src/excmds.ts
143
src/excmds.ts
|
@ -75,26 +75,6 @@ import * as config from './config'
|
|||
//#background_helper
|
||||
export const cmd_params = new Map<string, Map<string, string>>()
|
||||
|
||||
const SEARCH_URLS = new Map<string, string>([
|
||||
["google","https://www.google.com/search?q="],
|
||||
["googleuk","https://www.google.co.uk/search?q="],
|
||||
["bing","https://www.bing.com/search?q="],
|
||||
["duckduckgo","https://duckduckgo.com/?q="],
|
||||
["yahoo","https://search.yahoo.com/search?p="],
|
||||
["twitter","https://twitter.com/search?q="],
|
||||
["wikipedia","https://en.wikipedia.org/wiki/Special:Search/"],
|
||||
["youtube","https://www.youtube.com/results?search_query="],
|
||||
["amazon","https://www.amazon.com/s/ref=nb_sb_noss?url=search-alias%3Daps&field-keywords="],
|
||||
["amazonuk","https://www.amazon.co.uk/s/ref=nb_sb_noss?url=search-alias%3Daps&field-keywords="],
|
||||
["startpage","https://www.startpage.com/do/search?query="],
|
||||
["github","https://github.com/search?utf8=✓&q="],
|
||||
["searx","https://searx.me/?category_general=on&q="],
|
||||
["cnrtl","http://www.cnrtl.fr/lexicographie/"],
|
||||
["osm","https://www.openstreetmap.org/search?query="],
|
||||
["mdn","https://developer.mozilla.org/en-US/search?q="],
|
||||
["gentoo_wiki","https://wiki.gentoo.org/index.php?title=Special%3ASearch&profile=default&fulltext=Search&search="],
|
||||
])
|
||||
|
||||
// map a page-relation (next or previous) to a fallback pattern to match link texts against
|
||||
const REL_PATTERN = {
|
||||
next: /^(?:next|newer)\b|»|>>/i,
|
||||
|
@ -109,8 +89,9 @@ function hasScheme(uri: string) {
|
|||
/** @hidden */
|
||||
function searchURL(provider: string, query: string) {
|
||||
if (provider == "search") provider = config.get("searchengine")
|
||||
if (SEARCH_URLS.has(provider)) {
|
||||
const url = new URL(SEARCH_URLS.get(provider) + encodeURIComponent(query))
|
||||
let searchurlprovider = config.get("searchurls", provider)
|
||||
if (searchurlprovider !== undefined){
|
||||
const url = new URL(searchurlprovider + encodeURIComponent(query))
|
||||
// URL constructor doesn't convert +s because they're valid literals in
|
||||
// the standard it adheres to. But they are special characters in
|
||||
// x-www-form-urlencoded and e.g. google excepts query parameters in
|
||||
|
@ -258,6 +239,7 @@ export async function reloadhard(n = 1) {
|
|||
//#content
|
||||
export function open(...urlarr: string[]) {
|
||||
let url = urlarr.join(" ")
|
||||
console.log("open url:" + url)
|
||||
window.location.href = forceURI(url)
|
||||
}
|
||||
|
||||
|
@ -274,7 +256,7 @@ export function home(all: "false" | "true" = "false"){
|
|||
let homepages = config.get("homepages")
|
||||
console.log(homepages)
|
||||
if (homepages.length > 0){
|
||||
if (all === "false") open(homepages[-1])
|
||||
if (all === "false") open(homepages[homepages.length - 1])
|
||||
else {
|
||||
homepages.map(t=>tabopen(t))
|
||||
}
|
||||
|
@ -394,9 +376,8 @@ export function urlparent (){
|
|||
@hidden
|
||||
*/
|
||||
//#content
|
||||
export function geturlsforlinks(rel: string){
|
||||
let elems = document.querySelectorAll("link[rel='" + rel + "']") as NodeListOf<HTMLLinkElement>
|
||||
console.log(rel, elems)
|
||||
export function geturlsforlinks(reltype = "rel", rel: string){
|
||||
let elems = document.querySelectorAll("link[" + reltype + "='" + rel + "']") as NodeListOf<HTMLLinkElement>
|
||||
if (elems)
|
||||
return Array.prototype.map.call(elems, x => x.href)
|
||||
return []
|
||||
|
@ -896,13 +877,16 @@ export async function clipboard(excmd: "open"|"yank"|"yankshort"|"yankcanon"|"ta
|
|||
let urls = []
|
||||
switch (excmd) {
|
||||
case 'yankshort':
|
||||
urls = await geturlsforlinks("shortlink")
|
||||
urls = await geturlsforlinks("rel", "shortlink")
|
||||
if (urls.length == 0) {
|
||||
urls = await geturlsforlinks("rev", "canonical")
|
||||
}
|
||||
if (urls.length > 0) {
|
||||
messageActiveTab("commandline_frame", "setClipboard", [urls[0]])
|
||||
break
|
||||
}
|
||||
case 'yankcanon':
|
||||
urls = await geturlsforlinks("canonical")
|
||||
urls = await geturlsforlinks("rel", "canonical")
|
||||
if (urls.length > 0) {
|
||||
messageActiveTab("commandline_frame", "setClipboard", [urls[0]])
|
||||
break
|
||||
|
@ -1002,6 +986,12 @@ export function bind(key: string, ...bindarr: string[]){
|
|||
config.set("nmaps",exstring,key)
|
||||
}
|
||||
|
||||
/** Set a search engine keyword for use with *open or `set searchengine` */
|
||||
//#background
|
||||
export function searchsetkeyword(keyword: string, url: string){
|
||||
config.set("searchurls",forceURI(url),keyword)
|
||||
}
|
||||
|
||||
/** Unbind a sequence of keys so that they do nothing at all.
|
||||
|
||||
See also:
|
||||
|
@ -1032,6 +1022,103 @@ export async function reset(key: string){
|
|||
browser.storage.sync.set({nmaps})
|
||||
}
|
||||
|
||||
/** Deletes various privacy-related items.
|
||||
|
||||
The list of possible arguments can be found here:
|
||||
https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/browsingData/DataTypeSet
|
||||
|
||||
Additional, tridactyl-specific arguments are:
|
||||
- commandline: Removes the in-memory commandline history.
|
||||
- tridactyllocal: Removes all tridactyl storage local to this machine. Use it with
|
||||
commandline if you want to delete your commandline history.
|
||||
- tridactylsync: Removes all tridactyl storage associated with your Firefox Account (i.e, all user configuration, by default).
|
||||
These arguments aren't affected by the timespan parameter.
|
||||
|
||||
Timespan parameter:
|
||||
-t [0-9]+(m|h|d|w)
|
||||
|
||||
Examples:
|
||||
`sanitize all` -> Deletes everything
|
||||
`sanitize history` -> Deletes all history
|
||||
`sanitize commandline tridactyllocal tridactylsync` -> Deletes every bit of data Tridactyl holds
|
||||
`sanitize cookies -t 3d` -> Deletes cookies that were set during the last three days.
|
||||
|
||||
*/
|
||||
//#background
|
||||
export async function sanitize(...args: string[]) {
|
||||
let flagpos = args.indexOf("-t")
|
||||
let since = {}
|
||||
// If the -t flag has been given and there is an arg after it
|
||||
if (flagpos > -1) {
|
||||
if (flagpos < args.length - 1) {
|
||||
let match = args[flagpos + 1].match('^([0-9])+(m|h|d|w)$')
|
||||
// If the arg of the flag matches Pentadactyl's sanitizetimespan format
|
||||
if (match !== null && match.length == 3) {
|
||||
// Compute the timespan in milliseconds and get a Date object
|
||||
let millis = parseInt(match[1]) * 1000
|
||||
switch (match[2]) {
|
||||
case 'w': millis *= 7
|
||||
case 'd': millis *= 24
|
||||
case 'h': millis *= 60
|
||||
case 'm': millis *= 60
|
||||
}
|
||||
since = { "since": (new Date()).getTime() - millis }
|
||||
} else {
|
||||
console.log(":sanitize error: expected time format: ^([0-9])+(m|h|d|w)$, given format:" + args[flagpos+1])
|
||||
return
|
||||
}
|
||||
} else {
|
||||
console.log(":sanitize error: -t given but no following arguments")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
let dts = {
|
||||
"cache": false,
|
||||
"cookies": false,
|
||||
"downloads": false,
|
||||
"formData": false,
|
||||
"history": false,
|
||||
"localStorage": false,
|
||||
"passwords": false,
|
||||
"serviceWorkers": false,
|
||||
// These are Tridactyl-specific
|
||||
"commandline": false,
|
||||
"tridactyllocal": false,
|
||||
"tridactylsync": false,
|
||||
/* When this one is activated, a lot of errors seem to pop up in
|
||||
the console. Keeping it disabled is probably a good idea.
|
||||
"pluginData": false,
|
||||
*/
|
||||
/* These 3 are supported by Chrome and Opera but not by Firefox yet.
|
||||
"fileSystems": false,
|
||||
"indexedDB": false,
|
||||
"serverBoundCertificates": false,
|
||||
*/
|
||||
}
|
||||
if (args.find(x => x == "all") !== undefined) {
|
||||
for (let attr in dts)
|
||||
dts[attr] = true
|
||||
} else {
|
||||
// We bother checking if dts[x] is false because
|
||||
// browser.browsingData.remove() is very strict on the format of the
|
||||
// object it expects
|
||||
args.map(x => { if (dts[x] === false) dts[x] = true })
|
||||
}
|
||||
// Tridactyl-specific items
|
||||
if (dts.commandline === true)
|
||||
state.cmdHistory = []
|
||||
delete dts.commandline
|
||||
if (dts.tridactyllocal === true)
|
||||
browser.storage.local.clear()
|
||||
delete dts.tridactyllocal
|
||||
if (dts.tridactylsync === true)
|
||||
browser.storage.sync.clear()
|
||||
delete dts.tridactylsync
|
||||
// Global items
|
||||
browser.browsingData.remove(since, dts)
|
||||
}
|
||||
|
||||
/** Bind a quickmark for the current URL to a key.
|
||||
|
||||
Afterwards use go[key], gn[key], or gw[key] to [[open]], [[tabopen]], or
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"manifest_version": 2,
|
||||
"name": "Tridactyl",
|
||||
"version": "1.6.1",
|
||||
"version": "1.7.0",
|
||||
"icons": {
|
||||
"64": "static/logo/Tridactyl_64px.png",
|
||||
"100": "static/logo/Tridactyl_100px.png",
|
||||
|
@ -38,6 +38,7 @@
|
|||
"permissions": [
|
||||
"activeTab",
|
||||
"bookmarks",
|
||||
"browsingData",
|
||||
"contextMenus",
|
||||
"clipboardWrite",
|
||||
"clipboardRead",
|
||||
|
|
|
@ -42,6 +42,19 @@ input {
|
|||
border-spacing: 0;
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
/* redundancy 2: redundancy 2: more redundancy */
|
||||
#completions .BmarkCompletionSource {
|
||||
max-height: calc(20 * var(--option-height));
|
||||
min-height: calc(10 * var(--option-height));
|
||||
}
|
||||
|
||||
#completions .BmarkCompletionSource table {
|
||||
width: 100%;
|
||||
font-size: 9pt;
|
||||
border-spacing: 0;
|
||||
table-layout: fixed;
|
||||
}
|
||||
/* redundancy ends */
|
||||
|
||||
#completions .BufferCompletionSource {
|
||||
|
|
Loading…
Add table
Reference in a new issue