mirror of
https://github.com/vale981/tridactyl
synced 2025-03-05 17:41:40 -05:00
Merge branch 'master' of github.com:cmcaine/tridactyl into glacambre-fix_pcgamer_hints
This commit is contained in:
commit
d9fc24dc62
16 changed files with 650 additions and 254 deletions
|
@ -1,4 +1,6 @@
|
|||
#!/usr/bin/env bash
|
||||
shopt -s globstar
|
||||
sed -i '/<\/body>/s@^@<script src="/content.js"></script><link rel="stylesheet" href="/static/content.css"><link rel="stylesheet" href="/static/hint.css">@' $1
|
||||
|
||||
sed -i.bak '/<\/body>/s@^@<script src="/content.js"></script><link rel="stylesheet" href="/static/content.css"><link rel="stylesheet" href="/static/hint.css">@' "$1"
|
||||
rm "$1.bak"
|
||||
|
||||
#static/docs/modules/_excmds_.html
|
||||
|
|
|
@ -93,7 +93,7 @@ def content(lines, context):
|
|||
sig = Signature(block.split('\n')[0])
|
||||
return "cmd_params.set('{sig.name}', ".format(**locals()) + dict_to_js(sig.params) + """)
|
||||
{sig.raw}
|
||||
messageActiveTab(
|
||||
return messageActiveTab(
|
||||
"excmd_content",
|
||||
"{sig.name}",
|
||||
""".format(**locals()) + str(list(sig.params.keys())).replace("'","") + """,
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
version=$(git describe --tags)
|
||||
gitversion=$(git describe --tags | cut -d"-" -f2-)
|
||||
manversion=$(grep '"version":' ./src/manifest.json | cut -d":" -f2 | tr -d \" | tr -d , | cut -d" " -f2)
|
||||
version=$manversion-$gitversion
|
||||
|
||||
sed -i 's/REPLACE_ME_WITH_THE_VERSION_USING_SED/'$version'/' ./build/background.js
|
||||
sed -i.bak 's/REPLACE_ME_WITH_THE_VERSION_USING_SED/'$version'/' ./build/background.js
|
||||
rm ./build/background.js.bak
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/bin/sh
|
||||
dest=generated/static/docs
|
||||
typedoc --out $dest src --ignoreCompilerErrors
|
||||
find $dest -name *.html -exec ./scripts/commandline_injector.sh '{}' \;
|
||||
find $dest -name \*.html -exec ./scripts/commandline_injector.sh '{}' \;
|
||||
cp -r $dest build/static/
|
||||
|
|
|
@ -3,4 +3,9 @@
|
|||
# Combine newtab markdown and template
|
||||
|
||||
cd src/static
|
||||
sed "/REPLACETHIS/s/REPLACETHIS/marked newtab.md/e" newtab.template.html > ../../generated/static/newtab.html
|
||||
|
||||
newtab="../../generated/static/newtab.html"
|
||||
|
||||
sed "/REPLACETHIS/,$ d" newtab.template.html > "$newtab"
|
||||
marked newtab.md >> "$newtab"
|
||||
sed "1,/REPLACETHIS/ d" newtab.template.html >> "$newtab"
|
||||
|
|
|
@ -20,6 +20,12 @@ async function add_beta(versionstr) {
|
|||
})
|
||||
}
|
||||
|
||||
function save_manifest(filename, manifest) {
|
||||
// Save file
|
||||
const fs = require('fs')
|
||||
fs.writeFileSync(filename, JSON.stringify(manifest, null, 4))
|
||||
}
|
||||
|
||||
async function main() {
|
||||
let filename, manifest
|
||||
switch (process.argv[2]) {
|
||||
|
@ -28,19 +34,18 @@ async function main() {
|
|||
filename = './src/manifest.json'
|
||||
manifest = require('.' + filename)
|
||||
manifest.version = bump_version(manifest.version, Number(process.argv[3]))
|
||||
save_manifest(filename, manifest)
|
||||
exec(`git add ${filename} && git commit -m 'release ${manifest.version}' && git tag ${manifest.version}`)
|
||||
break
|
||||
case 'beta':
|
||||
filename = './build/manifest.json'
|
||||
manifest = require('.' + filename)
|
||||
manifest.version = await add_beta(manifest.version)
|
||||
save_manifest(filename, manifest)
|
||||
break
|
||||
default:
|
||||
throw "Unknown command!"
|
||||
}
|
||||
|
||||
// Save file
|
||||
const fs = require('fs')
|
||||
fs.writeFile(filename, JSON.stringify(manifest, null, 4), err=>err && console.error(err))
|
||||
}
|
||||
|
||||
main()
|
||||
|
|
|
@ -13,6 +13,7 @@ import * as Fuse from 'fuse.js'
|
|||
import {enumerate} from './itertools'
|
||||
import {toNumber} from './convert'
|
||||
import * as Messaging from './messaging'
|
||||
import {browserBg} from './lib/webext'
|
||||
|
||||
const DEFAULT_FAVICON = browser.extension.getURL("static/defaultFavicon.svg")
|
||||
|
||||
|
@ -55,9 +56,7 @@ export abstract class CompletionSource {
|
|||
return this._state
|
||||
}
|
||||
|
||||
next(inc = 1): boolean {
|
||||
return false
|
||||
}
|
||||
abstract next(inc?: number): boolean
|
||||
|
||||
prev(inc = 1): boolean {
|
||||
return this.next(-1*inc)
|
||||
|
@ -319,8 +318,9 @@ class HistoryCompletionOption extends CompletionOptionHTML implements Completion
|
|||
|
||||
constructor(public value: string, page: browser.history.HistoryItem) {
|
||||
super()
|
||||
// Two character buffer properties prefix
|
||||
// Push prefix before padding so we don't match on whitespace
|
||||
if (! page.title) {
|
||||
page.title = new URL(page.url).host
|
||||
}
|
||||
|
||||
// Push properties we want to fuzmatch on
|
||||
this.fuseKeys.push(page.title, page.url) // weight by page.visitCount
|
||||
|
@ -346,11 +346,6 @@ function sleep(ms: number) {
|
|||
export class HistoryCompletionSource extends CompletionSourceFuse {
|
||||
public options: HistoryCompletionOption[]
|
||||
|
||||
// TODO:
|
||||
// - store the exstr and trigger redraws on user or data input without
|
||||
// callback faffery
|
||||
// - sort out the element redrawing.
|
||||
|
||||
constructor(private _parent) {
|
||||
super(
|
||||
[
|
||||
|
@ -361,45 +356,81 @@ export class HistoryCompletionSource extends CompletionSourceFuse {
|
|||
"HistoryCompletionSource", "History"
|
||||
)
|
||||
|
||||
this.updateOptions()
|
||||
this._parent.appendChild(this.node)
|
||||
}
|
||||
|
||||
private async updateOptions(exstr?: string) {
|
||||
/* console.log('updateOptions', this.optionContainer) */
|
||||
// this sleep stops input from being blocked, but also breaks :open until something is typed
|
||||
// await sleep(0)
|
||||
const history: browser.history.HistoryItem[] =
|
||||
await Messaging.message("commandline_background", "history")
|
||||
public async filter(exstr: string) {
|
||||
this.lastExstr = exstr
|
||||
const [prefix, query] = this.splitOnPrefix(exstr)
|
||||
|
||||
const options = []
|
||||
|
||||
// Get alternative tab, defined as last accessed tab.
|
||||
history.sort((a, b) => { return a.lastVisitTime < b.lastVisitTime ? 1 : -1 })
|
||||
|
||||
for (const page of history) {
|
||||
options.push(new HistoryCompletionOption(
|
||||
page.url,
|
||||
page,
|
||||
))
|
||||
// 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
|
||||
}
|
||||
|
||||
/* console.log('updateOptions end', this.waiting, this.optionContainer) */
|
||||
this.options = options
|
||||
this.options = (await this.scoreOptions(query, 10)).map(
|
||||
page => new HistoryCompletionOption(page.url, page)
|
||||
)
|
||||
|
||||
this.updateChain()
|
||||
}
|
||||
|
||||
async onInput(exstr) {
|
||||
// Schedule an update, if you like. Not very useful for buffers, but
|
||||
// will be for other things.
|
||||
this.updateOptions()
|
||||
updateChain() {
|
||||
// Options are pre-trimmed to the right length.
|
||||
this.options.forEach(option => option.state = 'normal')
|
||||
|
||||
// Call concrete class
|
||||
this.updateDisplay()
|
||||
}
|
||||
|
||||
onInput() {}
|
||||
|
||||
private frecency(item: browser.history.HistoryItem) {
|
||||
// Doesn't actually care about recency yet.
|
||||
return item.visitCount * -1
|
||||
}
|
||||
|
||||
private async scoreOptions(query: string, n: number) {
|
||||
if (! query) {
|
||||
return (await browserBg.topSites.get()).slice(0, n)
|
||||
} else {
|
||||
// Search history, dedupe and sort by frecency
|
||||
let history = await browserBg.history.search({
|
||||
text: query,
|
||||
maxResults: 500,
|
||||
startTime: 0
|
||||
})
|
||||
|
||||
// Remove entries with duplicate URLs
|
||||
const dedupe = new Map()
|
||||
for (const page of history) {
|
||||
if (dedupe.has(page.url)) {
|
||||
if (dedupe.get(page.url).title.length < page.title.length) {
|
||||
dedupe.set(page.url, page)
|
||||
}
|
||||
} else {
|
||||
dedupe.set(page.url, page)
|
||||
}
|
||||
}
|
||||
history = [...dedupe.values()]
|
||||
|
||||
history.sort((a, b) => this.frecency(a) - this.frecency(b))
|
||||
|
||||
return history.slice(0, n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BufferCompletionOption extends CompletionOptionHTML implements CompletionOptionFuse {
|
||||
public fuseKeys = []
|
||||
|
||||
constructor(public value: string, tab: browser.tabs.Tab, isAlternative = false) {
|
||||
constructor(public value: string, tab: browser.tabs.Tab, public isAlternative = false) {
|
||||
super()
|
||||
// Two character buffer properties prefix
|
||||
let pre = ""
|
||||
|
@ -477,7 +508,38 @@ export class BufferCompletionSource extends CompletionSourceFuse {
|
|||
// will be for other things.
|
||||
this.updateOptions()
|
||||
}
|
||||
setStateFromScore(scoredOpts: ScoredOption[]){super.setStateFromScore(scoredOpts, true)}
|
||||
|
||||
setStateFromScore(scoredOpts: ScoredOption[]){
|
||||
super.setStateFromScore(scoredOpts, true)
|
||||
}
|
||||
|
||||
/** Score with fuse unless query is an integer or a single # */
|
||||
scoredOptions(query: string, options = this.options): ScoredOption[] {
|
||||
const args = query.split(/\s+/gu)
|
||||
if (args.length === 1) {
|
||||
if (Number.isInteger(Number(args[0]))) {
|
||||
const index = (Number(args[0]) - 1).mod(options.length)
|
||||
return [{
|
||||
index,
|
||||
option: options[index],
|
||||
score: 0,
|
||||
}]
|
||||
} else if (args[0] === '#') {
|
||||
for (const [index, option] of enumerate(options)) {
|
||||
if (option.isAlternative) {
|
||||
return [{
|
||||
index,
|
||||
option,
|
||||
score: 0,
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If not yet returned...
|
||||
return super.scoredOptions(query, options)
|
||||
}
|
||||
}
|
||||
|
||||
// {{{ UNUSED: MANAGING ASYNC CHANGES
|
||||
|
|
149
src/config.ts
Normal file
149
src/config.ts
Normal file
|
@ -0,0 +1,149 @@
|
|||
// Sketch
|
||||
//
|
||||
// Need an easy way of getting and setting settings
|
||||
// If a setting is not set, the default should probably be returned.
|
||||
// That probably means that binds etc. should be per-key?
|
||||
//
|
||||
// We should probably store all settings in memory, and only load from storage on startup and when we set it
|
||||
//
|
||||
// Really, we'd like a way of just letting things use the variables
|
||||
//
|
||||
const CONFIGNAME = "userconfig"
|
||||
|
||||
type StorageMap = browser.storage.StorageMap
|
||||
|
||||
// make a naked object
|
||||
function o(object){
|
||||
return Object.assign(Object.create(null),object)
|
||||
}
|
||||
|
||||
// TODO: have list of possibilities for settings, e.g. hintmode: reverse | normal
|
||||
let USERCONFIG = o({})
|
||||
const DEFAULTS = o({
|
||||
"nmaps": o({
|
||||
"o": "fillcmdline open",
|
||||
"O": "current_url open",
|
||||
"w": "fillcmdline winopen",
|
||||
"W": "current_url winopen",
|
||||
"t": "fillcmdline tabopen",
|
||||
"]]": "followpage next",
|
||||
"[[": "followpage prev",
|
||||
"[c": "urlincrement -1",
|
||||
"]c": "urlincrement 1",
|
||||
"T": "current_url tabopen",
|
||||
"yy": "clipboard yank",
|
||||
"ys": "clipboard yankshort",
|
||||
"yc": "clipboard yankcanon",
|
||||
"p": "clipboard open",
|
||||
"P": "clipboard tabopen",
|
||||
"j": "scrollline 10",
|
||||
"k": "scrollline -10",
|
||||
"h": "scrollpx -50",
|
||||
"l": "scrollpx 50",
|
||||
"G": "scrollto 100",
|
||||
"gg": "scrollto 0",
|
||||
"H": "back",
|
||||
"L": "forward",
|
||||
"d": "tabclose",
|
||||
"u": "undo",
|
||||
"r": "reload",
|
||||
"R": "reloadhard",
|
||||
"gi": "focusinput -l",
|
||||
"gt": "tabnext_gt",
|
||||
"gT": "tabprev",
|
||||
"g^": "tabfirst",
|
||||
"g$": "tablast",
|
||||
"gr": "reader",
|
||||
"gu": "urlparent",
|
||||
"gU": "urlroot",
|
||||
":": "fillcmdline",
|
||||
"s": "fillcmdline open search",
|
||||
"S": "fillcmdline tabopen search",
|
||||
"M": "gobble 1 quickmark",
|
||||
"xx": "something",
|
||||
// "B": "fillcmdline bufferall",
|
||||
"b": "fillcmdline buffer",
|
||||
"ZZ": "qall",
|
||||
"f": "hint",
|
||||
"F": "hint -b",
|
||||
";i": "hint -i",
|
||||
";I": "hint -I",
|
||||
";y": "hint -y",
|
||||
";p": "hint -p",
|
||||
";;": "hint -;",
|
||||
";#": "hint -#",
|
||||
"I": "mode ignore",
|
||||
"a": "current_url bmark",
|
||||
"A": "bmark",
|
||||
}),
|
||||
"searchengine": "google",
|
||||
"storageloc": "sync",
|
||||
"hintchars": "hjklasdfgyuiopqwertnmzxcvb",
|
||||
"hintorder": "normal",
|
||||
})
|
||||
|
||||
// currently only supports 2D or 1D storage
|
||||
export function get(target, property?){
|
||||
if (property !== undefined){
|
||||
if (USERCONFIG[target] !== undefined){
|
||||
return USERCONFIG[target][property] || DEFAULTS[target][property]
|
||||
}
|
||||
else return DEFAULTS[target][property]
|
||||
}
|
||||
if (typeof DEFAULTS[target] === "object") return Object.assign(o({}),DEFAULTS[target],USERCONFIG[target])
|
||||
else return USERCONFIG[target] || DEFAULTS[target]
|
||||
}
|
||||
|
||||
// if you don't specify a property and you should, this will wipe everything
|
||||
export function set(target, value, property?){
|
||||
if (property !== undefined){
|
||||
if (USERCONFIG[target] === undefined) USERCONFIG[target] = o({})
|
||||
USERCONFIG[target][property] = value
|
||||
} else USERCONFIG[target] = value
|
||||
// Always save
|
||||
save(get("storageloc"))
|
||||
}
|
||||
|
||||
export function unset(target, property?){
|
||||
if (property !== undefined){
|
||||
delete USERCONFIG[target][property]
|
||||
} else delete USERCONFIG[target]
|
||||
save(get("storageloc"))
|
||||
}
|
||||
|
||||
export async function save(storage: "local" | "sync" = "sync"){
|
||||
// let storageobj = storage == "local" ? browser.storage.local : browser.storage.sync
|
||||
// storageobj.set({CONFIGNAME: USERCONFIG})
|
||||
let settingsobj = o({})
|
||||
settingsobj[CONFIGNAME] = USERCONFIG
|
||||
if (storage == "local") browser.storage.local.set(settingsobj)
|
||||
else browser.storage.sync.set(settingsobj)
|
||||
}
|
||||
|
||||
// Read all user configuration on start
|
||||
// Legacy config gets loaded first
|
||||
let legacy_nmaps = {}
|
||||
browser.storage.sync.get("nmaps").then(nmaps => {
|
||||
legacy_nmaps = nmaps["nmaps"]
|
||||
browser.storage.sync.get(CONFIGNAME).then(settings => {
|
||||
schlepp(settings[CONFIGNAME])
|
||||
// Local storage overrides sync
|
||||
browser.storage.local.get(CONFIGNAME).then(settings => {
|
||||
schlepp(settings[CONFIGNAME])
|
||||
USERCONFIG["nmaps"] = Object.assign(legacy_nmaps, USERCONFIG["nmaps"])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function schlepp(settings){
|
||||
// "Import" is a reserved word so this will have to do
|
||||
Object.assign(USERCONFIG,settings)
|
||||
}
|
||||
|
||||
browser.storage.onChanged.addListener(
|
||||
(changes, areaname) => {
|
||||
if (CONFIGNAME in changes) {
|
||||
Object.assign(USERCONFIG, changes[CONFIGNAME].newValue)
|
||||
}
|
||||
}
|
||||
)
|
20
src/dom.ts
20
src/dom.ts
|
@ -224,23 +224,31 @@ export function isVisible (element: Element) {
|
|||
/** Get all elements that match the given selector
|
||||
*
|
||||
* @param selector `the CSS selector to choose elements with
|
||||
* @param filter filter to use to further chose items, or null for all
|
||||
* @param filters filter to use (in thre given order) to further chose
|
||||
* items, or [] for all
|
||||
*/
|
||||
export function getElemsBySelector(selector: string, filter: ElementFilter) {
|
||||
export function getElemsBySelector(selector: string,
|
||||
filters: Array<ElementFilter>) {
|
||||
|
||||
let elems = Array.from(document.querySelectorAll(selector))
|
||||
|
||||
return filter ? elems.filter(filter) : elems
|
||||
for (let filter of filters) {
|
||||
elems = elems.filter(filter)
|
||||
}
|
||||
|
||||
return elems
|
||||
}
|
||||
|
||||
/** Get the nth input element on a page
|
||||
*
|
||||
* @param nth the element index, can be negative to start at the end
|
||||
* @param filter filter to use to further chose items, or null for all
|
||||
* @param filters filter to use (in thre given order) to further chose
|
||||
* items, or [] for all
|
||||
*/
|
||||
export function getNthElement(selectors: string, nth: number,
|
||||
filter: ElementFilter) {
|
||||
filters: Array<ElementFilter>): HTMLElement {
|
||||
|
||||
let inputs = getElemsBySelector(selectors, filter)
|
||||
let inputs = getElemsBySelector(selectors, filters)
|
||||
|
||||
if (inputs.length) {
|
||||
let index = Number(nth).clamp(-inputs.length, inputs.length - 1)
|
||||
|
|
365
src/excmds.ts
365
src/excmds.ts
|
@ -68,6 +68,8 @@ import * as CommandLineBackground from './commandline_background'
|
|||
//#content_helper
|
||||
import * as DOM from './dom'
|
||||
|
||||
import * as config from './config'
|
||||
|
||||
|
||||
/** @hidden */
|
||||
//#background_helper
|
||||
|
@ -106,6 +108,7 @@ 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))
|
||||
// URL constructor doesn't convert +s because they're valid literals in
|
||||
|
@ -148,8 +151,8 @@ function forceURI(maybeURI: string): string {
|
|||
if (e.name !== 'TypeError') throw e
|
||||
}
|
||||
|
||||
// Else search google
|
||||
return searchURL('google', maybeURI).href
|
||||
// Else search $searchengine
|
||||
return searchURL('search', maybeURI).href
|
||||
}
|
||||
|
||||
/** @hidden */
|
||||
|
@ -247,6 +250,9 @@ export async function reloadhard(n = 1) {
|
|||
- else if the first word contains a dot, treat as a domain name
|
||||
- else if the first word is a key of [[SEARCH_URLS]], treat all following terms as search parameters for that provider
|
||||
- else treat as search parameters for google
|
||||
|
||||
Related settings:
|
||||
"searchengine": "google" or any of [[SEARCH_URLS]]
|
||||
*/
|
||||
//#content
|
||||
export function open(...urlarr: string[]) {
|
||||
|
@ -360,6 +366,21 @@ export function urlparent (){
|
|||
}
|
||||
}
|
||||
|
||||
/** Returns the url of links that have a matching rel.
|
||||
|
||||
Don't bind to this: it's an internal function.
|
||||
|
||||
@hidden
|
||||
*/
|
||||
//#content
|
||||
export function geturlsforlinks(rel: string){
|
||||
let elems = document.querySelectorAll("link[rel='" + rel + "']") as NodeListOf<HTMLLinkElement>
|
||||
console.log(rel, elems)
|
||||
if (elems)
|
||||
return Array.prototype.map.call(elems, x => x.href)
|
||||
return []
|
||||
}
|
||||
|
||||
//#background
|
||||
export function zoom(level=0){
|
||||
level = level > 3 ? level / 100 : level
|
||||
|
@ -369,12 +390,12 @@ export function zoom(level=0){
|
|||
//#background
|
||||
export async function reader() {
|
||||
if (await l(firefoxVersionAtLeast(58))) {
|
||||
let aTab = await activeTab()
|
||||
if (aTab.isArticle) {
|
||||
browser.tabs.toggleReaderMode()
|
||||
} // else {
|
||||
// // once a statusbar exists an error can be displayed there
|
||||
// }
|
||||
let aTab = await activeTab()
|
||||
if (aTab.isArticle) {
|
||||
browser.tabs.toggleReaderMode()
|
||||
} // else {
|
||||
// // once a statusbar exists an error can be displayed there
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -448,7 +469,7 @@ export function focusinput(nth: number|string) {
|
|||
fallbackToNumeric = false
|
||||
|
||||
let inputs = DOM.getElemsBySelector(INPUTPASSWORD_selectors,
|
||||
DOM.isSubstantial)
|
||||
[DOM.isSubstantial])
|
||||
|
||||
if (inputs.length) {
|
||||
inputToFocus = <HTMLElement>inputs[0]
|
||||
|
@ -457,7 +478,7 @@ export function focusinput(nth: number|string) {
|
|||
else if (nth === "-b") {
|
||||
|
||||
let inputs = DOM.getElemsBySelector(INPUTTAGS_selectors,
|
||||
DOM.isSubstantial) as HTMLElement[]
|
||||
[DOM.isSubstantial]) as HTMLElement[]
|
||||
|
||||
inputToFocus = inputs.sort(DOM.compareElementArea).slice(-1)[0]
|
||||
}
|
||||
|
@ -468,7 +489,7 @@ export function focusinput(nth: number|string) {
|
|||
|
||||
let index = isNaN(<number>nth) ? 0 : <number>nth
|
||||
inputToFocus = DOM.getNthElement(INPUTTAGS_selectors,
|
||||
index, DOM.isSubstantial)
|
||||
index, [DOM.isSubstantial])
|
||||
}
|
||||
|
||||
if (inputToFocus) inputToFocus.focus()
|
||||
|
@ -482,29 +503,64 @@ document.addEventListener("focusin",e=>{if (DOM.isTextEditable(e.target as HTMLE
|
|||
|
||||
// {{{ TABS
|
||||
|
||||
/** Switch to the next tab by index (position on tab bar), wrapping round.
|
||||
/** Switch to the tab by index (position on tab bar), wrapping round.
|
||||
|
||||
optional increment is number of tabs forwards to move.
|
||||
@param index
|
||||
1-based index of the tab to target. Wraps such that 0 = last tab, -1 =
|
||||
penultimate tab, etc.
|
||||
|
||||
if undefined, return activeTabId()
|
||||
*/
|
||||
/** @hidden */
|
||||
//#background_helper
|
||||
async function tabIndexSetActive(index: number) {
|
||||
tabSetActive(await idFromIndex(index))
|
||||
}
|
||||
|
||||
/** Switch to the next tab, wrapping round.
|
||||
|
||||
If increment is specified, move that many tabs forwards.
|
||||
*/
|
||||
//#background
|
||||
export async function tabnext(increment = 1) {
|
||||
// Get an array of tabs in the current window
|
||||
let current_window = await browser.windows.getCurrent()
|
||||
let tabs = await browser.tabs.query({windowId: current_window.id})
|
||||
|
||||
// Derive the index we want
|
||||
let desiredIndex = ((await activeTab()).index + increment).mod(tabs.length)
|
||||
|
||||
// Find and switch to the tab with that index
|
||||
let desiredTab = tabs.find((tab: any) => {
|
||||
return tab.index === desiredIndex
|
||||
})
|
||||
tabSetActive(desiredTab.id)
|
||||
tabIndexSetActive((await activeTab()).index + increment + 1)
|
||||
}
|
||||
|
||||
/** Switch to the next tab, wrapping round.
|
||||
|
||||
If an index is specified, go to the tab with that number (this mimics the
|
||||
behaviour of `{count}gt` in vim, except that this command will accept a
|
||||
count that is out of bounds (and will mod it so that it is within bounds as
|
||||
per [[tabmove]], etc)).
|
||||
*/
|
||||
//#background
|
||||
export function tabprev(increment = 1) {
|
||||
tabnext(increment * -1)
|
||||
export async function tabnext_gt(index?: number) {
|
||||
if (index === undefined) {
|
||||
tabnext()
|
||||
} else {
|
||||
tabIndexSetActive(index)
|
||||
}
|
||||
}
|
||||
|
||||
/** Switch to the previous tab, wrapping round.
|
||||
|
||||
If increment is specified, move that many tabs backwards.
|
||||
*/
|
||||
//#background
|
||||
export async function tabprev(increment = 1) {
|
||||
tabIndexSetActive((await activeTab()).index - increment + 1)
|
||||
}
|
||||
|
||||
/** Switch to the first tab. */
|
||||
//#background
|
||||
export async function tabfirst() {
|
||||
tabIndexSetActive(1)
|
||||
}
|
||||
|
||||
/** Switch to the last tab. */
|
||||
//#background
|
||||
export async function tablast() {
|
||||
tabIndexSetActive(0)
|
||||
}
|
||||
|
||||
/** Like [[open]], but in a new tab */
|
||||
|
@ -516,24 +572,81 @@ export async function tabopen(...addressarr: string[]) {
|
|||
browser.tabs.create({url: uri})
|
||||
}
|
||||
|
||||
//#background
|
||||
export async function tabduplicate(id?: number){
|
||||
id = id ? id : (await activeTabId())
|
||||
browser.tabs.duplicate(id)
|
||||
}
|
||||
/** Resolve a tab index to the tab id of the corresponding tab in this window.
|
||||
|
||||
//#background
|
||||
export async function tabdetach(id?: number){
|
||||
id = id ? id : (await activeTabId())
|
||||
browser.windows.create({tabId: id})
|
||||
}
|
||||
@param index
|
||||
1-based index of the tab to target. Wraps such that 0 = last tab, -1 =
|
||||
penultimate tab, etc.
|
||||
|
||||
//#background
|
||||
export async function tabclose(ids?: number[] | number) {
|
||||
if (ids !== undefined) {
|
||||
browser.tabs.remove(ids)
|
||||
if undefined, return activeTabId()
|
||||
|
||||
@hidden
|
||||
*/
|
||||
//#background_helper
|
||||
async function idFromIndex(index?: number): Promise<number> {
|
||||
if (index !== undefined) {
|
||||
// Wrap
|
||||
index = (index - 1).mod(
|
||||
(await l(browser.tabs.query({currentWindow: true}))).length)
|
||||
+ 1
|
||||
|
||||
// Return id of tab with that index.
|
||||
return (await l(browser.tabs.query({
|
||||
currentWindow: true,
|
||||
index: index - 1,
|
||||
})))[0].id
|
||||
} else {
|
||||
// Close the current tab
|
||||
return await activeTabId()
|
||||
}
|
||||
}
|
||||
|
||||
/** Close all other tabs in this window */
|
||||
//#background
|
||||
export async function tabonly() {
|
||||
const tabs = await browser.tabs.query({
|
||||
pinned: false,
|
||||
active: false,
|
||||
currentWindow: true
|
||||
})
|
||||
const tabsIds = tabs.map(tab => tab.id)
|
||||
browser.tabs.remove(tabsIds)
|
||||
}
|
||||
|
||||
|
||||
/** Duplicate a tab.
|
||||
|
||||
@param index
|
||||
The 1-based index of the tab to target. index < 1 wraps. If omitted, this tab.
|
||||
*/
|
||||
//#background
|
||||
export async function tabduplicate(index?: number) {
|
||||
browser.tabs.duplicate(await idFromIndex(index))
|
||||
}
|
||||
|
||||
/** Detach a tab, opening it in a new window.
|
||||
|
||||
@param index
|
||||
The 1-based index of the tab to target. index < 1 wraps. If omitted, this tab.
|
||||
*/
|
||||
//#background
|
||||
export async function tabdetach(index?: number) {
|
||||
browser.windows.create({tabId: await idFromIndex(index)})
|
||||
}
|
||||
|
||||
/** Close a tab.
|
||||
|
||||
Known bug: autocompletion will make it impossible to close more than one tab at once if the list of numbers looks enough like an open tab's title or URL.
|
||||
|
||||
@param indexes
|
||||
The 1-based indexes of the tabs to target. indexes < 1 wrap. If omitted, this tab.
|
||||
*/
|
||||
//#background
|
||||
export async function tabclose(...indexes: string[]) {
|
||||
if (indexes.length > 0) {
|
||||
const idsPromise = indexes.map(index => idFromIndex(Number(index)))
|
||||
browser.tabs.remove(await Promise.all(idsPromise))
|
||||
} else {
|
||||
// Close current tab
|
||||
browser.tabs.remove(await activeTabId())
|
||||
}
|
||||
}
|
||||
|
@ -558,17 +671,29 @@ export async function undo(){
|
|||
}
|
||||
}
|
||||
|
||||
/** Move the current tab to be just in front of the index specified.
|
||||
|
||||
Known bug: This supports relative movement, but autocomple doesn't know
|
||||
that yet and will override positive and negative indexes.
|
||||
|
||||
Put a space in front of tabmove if you want to disable completion and have
|
||||
the relative indexes at the command line.
|
||||
|
||||
Binds are unaffected.
|
||||
|
||||
@param index
|
||||
New index for the current tab.
|
||||
|
||||
1 is the first index. 0 is the last index. -1 is the penultimate, etc.
|
||||
*/
|
||||
//#background
|
||||
export async function tabmove(n?: string) {
|
||||
let aTab = await activeTab(),
|
||||
m: number
|
||||
if (!n) {
|
||||
browser.tabs.move(aTab.id, {index: -1})
|
||||
return
|
||||
} else if (n.startsWith("+") || n.startsWith("-")) {
|
||||
m = Math.max(0, Number(n) + aTab.index)
|
||||
} else m = Number(n)
|
||||
browser.tabs.move(aTab.id, {index: m})
|
||||
export async function tabmove(index = "0") {
|
||||
const aTab = await activeTab()
|
||||
let newindex: number
|
||||
if (index.startsWith("+") || index.startsWith("-")) {
|
||||
newindex = Math.max(0, Number(index) + aTab.index)
|
||||
} else newindex = Number(index) - 1
|
||||
browser.tabs.move(aTab.id, {index: newindex})
|
||||
}
|
||||
|
||||
/** Pin the current tab */
|
||||
|
@ -735,14 +860,31 @@ export async function current_url(...strarr: string[]){
|
|||
|
||||
If `excmd == "yank"`, copy the current URL, or if given, the value of toYank, into the system clipboard.
|
||||
|
||||
If `excmd == "yankcanon"`, copy the canonical URL of the current page if it exists, otherwise copy the current URL.
|
||||
|
||||
If `excmd == "yankshort"`, copy the shortlink version of the current URL, and fall back to the canonical then actual URL. Known to work on https://yankshort.neocities.org/.
|
||||
|
||||
Unfortunately, javascript can only give us the `clipboard` clipboard, not e.g. the X selection clipboard.
|
||||
|
||||
*/
|
||||
//#background
|
||||
export async function clipboard(excmd: "open"|"yank"|"tabopen" = "open", ...toYank: string[]) {
|
||||
export async function clipboard(excmd: "open"|"yank"|"yankshort"|"yankcanon"|"tabopen" = "open", ...toYank: string[]) {
|
||||
let content = toYank.join(" ")
|
||||
let url = ""
|
||||
let urls = []
|
||||
switch (excmd) {
|
||||
case 'yankshort':
|
||||
urls = await geturlsforlinks("shortlink")
|
||||
if (urls.length > 0) {
|
||||
messageActiveTab("commandline_frame", "setClipboard", [urls[0]])
|
||||
break
|
||||
}
|
||||
case 'yankcanon':
|
||||
urls = await geturlsforlinks("canonical")
|
||||
if (urls.length > 0) {
|
||||
messageActiveTab("commandline_frame", "setClipboard", [urls[0]])
|
||||
break
|
||||
}
|
||||
case 'yank':
|
||||
await messageActiveTab("commandline_content", "focus")
|
||||
content = (content == "") ? (await activeTab()).url : content
|
||||
|
@ -766,43 +908,46 @@ export async function clipboard(excmd: "open"|"yank"|"tabopen" = "open", ...toYa
|
|||
}
|
||||
|
||||
// {{{ Buffer/completion stuff
|
||||
// TODO: Move autocompletions out of excmds.
|
||||
/** Ported from Vimperator. */
|
||||
|
||||
/** Equivalent to `fillcmdline buffer`
|
||||
|
||||
Sort of Vimperator alias
|
||||
*/
|
||||
//#background
|
||||
export async function tabs() {
|
||||
fillcmdline("buffer")
|
||||
}
|
||||
|
||||
/** Equivalent to `fillcmdline buffer`
|
||||
|
||||
Sort of Vimperator alias
|
||||
*/
|
||||
//#background
|
||||
export async function buffers() {
|
||||
tabs()
|
||||
}
|
||||
|
||||
/** Change active tab */
|
||||
/** Change active tab.
|
||||
|
||||
@param index
|
||||
Starts at 1. 0 refers to last tab, -1 to penultimate tab, etc.
|
||||
|
||||
"#" means the tab that was last accessed in this window
|
||||
*/
|
||||
//#background
|
||||
export async function buffer(n?: number | string) {
|
||||
if (!n || Number(n) == 0) return // Vimperator index starts at 1
|
||||
if (n === "#") {
|
||||
n =
|
||||
export async function buffer(index: number | '#') {
|
||||
if (index === "#") {
|
||||
// Switch to the most recently accessed buffer
|
||||
tabIndexSetActive(
|
||||
(await browser.tabs.query({currentWindow: true})).sort((a, b) => {
|
||||
return a.lastAccessed < b.lastAccessed ? 1 : -1
|
||||
})[1].index + 1
|
||||
}
|
||||
if (Number.isInteger(Number(n))) {
|
||||
tabSetActive(
|
||||
(await browser.tabs.query({
|
||||
currentWindow: true,
|
||||
index: Number(n) - 1,
|
||||
}))[0].id
|
||||
)
|
||||
} else if (Number.isInteger(Number(index))) {
|
||||
tabIndexSetActive(Number(index))
|
||||
}
|
||||
}
|
||||
|
||||
/*/1** Set tab with index of n belonging to window with id of m to active *1/ */
|
||||
/*//#background */
|
||||
/*export async function bufferall(m?: number, n?: number) { */
|
||||
/* // TODO */
|
||||
/*} */
|
||||
|
||||
// }}}
|
||||
|
||||
// }}}
|
||||
|
@ -830,12 +975,9 @@ export async function buffer(n?: number | string) {
|
|||
- [[reset]]
|
||||
*/
|
||||
//#background
|
||||
export async function bind(key: string, ...bindarr: string[]){
|
||||
export function bind(key: string, ...bindarr: string[]){
|
||||
let exstring = bindarr.join(" ")
|
||||
let nmaps = (await browser.storage.sync.get("nmaps"))["nmaps"]
|
||||
nmaps = (nmaps == undefined) ? Object.create(null) : nmaps
|
||||
nmaps[key] = exstring
|
||||
browser.storage.sync.set({nmaps})
|
||||
config.set("nmaps",exstring,key)
|
||||
}
|
||||
|
||||
/** Unbind a sequence of keys so that they do nothing at all.
|
||||
|
@ -859,6 +1001,9 @@ export async function unbind(key: string){
|
|||
*/
|
||||
//#background
|
||||
export async function reset(key: string){
|
||||
config.unset("nmaps",key)
|
||||
|
||||
// Code for dealing with legacy binds
|
||||
let nmaps = (await browser.storage.sync.get("nmaps"))["nmaps"]
|
||||
nmaps = (nmaps == undefined) ? {} : nmaps
|
||||
delete nmaps[key]
|
||||
|
@ -885,6 +1030,46 @@ export async function quickmark(key: string) {
|
|||
await bind("gw" + key, "winopen", address)
|
||||
}
|
||||
|
||||
//#background
|
||||
export function get(target: string, property?: string){
|
||||
console.log(config.get(target,property))
|
||||
}
|
||||
|
||||
/** Set a setting to a value
|
||||
|
||||
Currently, this only supports string settings without any whitespace
|
||||
(i.e. not nmaps.)
|
||||
|
||||
It can be used on any string <-> string settings found [here](/static/docs/modules/_config_.html#defaults)
|
||||
|
||||
*/
|
||||
//#background
|
||||
export function set(setting: string, value?: string){
|
||||
// We don't support setting objects yet
|
||||
if (setting != "nmaps"){
|
||||
if (value !== undefined){
|
||||
config.set(setting,value)
|
||||
} else fillcmdline_notrail("set " + setting + " " + config.get(setting))
|
||||
}
|
||||
}
|
||||
|
||||
//#background
|
||||
export function unset(target: string, property?: string){
|
||||
config.unset(target,property)
|
||||
}
|
||||
|
||||
// not required as we automatically save all config
|
||||
////#background
|
||||
//export function saveconfig(){
|
||||
// config.save(config.get("storageloc"))
|
||||
//}
|
||||
|
||||
////#background
|
||||
//export function mktridactylrc(){
|
||||
// saveconfig()
|
||||
//}
|
||||
|
||||
|
||||
// }}}
|
||||
|
||||
// {{{ HINTMODE
|
||||
|
@ -893,18 +1078,32 @@ export async function quickmark(key: string) {
|
|||
import * as hinting from './hinting_background'
|
||||
|
||||
/** Hint a page.
|
||||
*
|
||||
* Pass -b as first argument to open hinted page in background.
|
||||
* -y copies the link's target to the clipboard.
|
||||
* -p copies an element's text to the clipboard.*/
|
||||
|
||||
@param option
|
||||
- -b open in background
|
||||
- -y copy (yank) link's target to clipboard
|
||||
- -p copy an element's text to the clipboard
|
||||
- -i view an image
|
||||
- -I view an image in a new tab
|
||||
- -; focus an element
|
||||
- -# yank an element's anchor URL to clipboard
|
||||
- -c [selector] hint links that match the css selector
|
||||
- `bind ;c hint -c [class*="expand"],[class="togg"]` works particularly well on reddit and HN
|
||||
|
||||
Related settings:
|
||||
"hintchars": "hjklasdfgyuiopqwertnmzxcvb"
|
||||
"hintorder": "normal" or "reverse"
|
||||
*/
|
||||
//#background
|
||||
export function hint(option?: "-b") {
|
||||
export function hint(option?: string, selectors="") {
|
||||
if (option === '-b') hinting.hintPageOpenInBackground()
|
||||
else if (option === "-y") hinting.hintPageYank()
|
||||
else if (option === "-p") hinting.hintPageTextYank()
|
||||
else if (option === "-i") hinting.hintImage(false)
|
||||
else if (option === "-I") hinting.hintImage(true)
|
||||
else if (option === "-;") hinting.hintFocus()
|
||||
else if (option === "-#") hinting.hintPageAnchorYank()
|
||||
else if (option === "-c") hinting.hintPageSimple(selectors)
|
||||
else hinting.hintPageSimple()
|
||||
}
|
||||
|
||||
|
|
|
@ -10,17 +10,18 @@
|
|||
Redraw on reflow
|
||||
*/
|
||||
|
||||
import {elementsByXPath, isVisible, mouseEvent} from './dom'
|
||||
import * as DOM from './dom'
|
||||
import {log} from './math'
|
||||
import {permutationsWithReplacement, islice, izip, map} from './itertools'
|
||||
import {hasModifiers} from './keyseq'
|
||||
import state from './state'
|
||||
import {messageActiveTab} from './messaging'
|
||||
import * as config from './config'
|
||||
|
||||
/** Simple container for the state of a single frame's hints. */
|
||||
class HintState {
|
||||
public focusedHint: Hint
|
||||
readonly hintHost = document.createElement('div')
|
||||
readonly hintHost = html`<div class="TridactylHintHost">`
|
||||
readonly hints: Hint[] = []
|
||||
public filter = ''
|
||||
public hintchars = ''
|
||||
|
@ -63,16 +64,19 @@ export function hintPage(
|
|||
}
|
||||
|
||||
/** vimperator-style minimal hint names */
|
||||
function* hintnames(hintchars = HINTCHARS): IterableIterator<string> {
|
||||
function* hintnames(hintchars = config.get("hintchars")): IterableIterator<string> {
|
||||
let taglen = 1
|
||||
while (true) {
|
||||
yield* map(permutationsWithReplacement(hintchars, taglen), e=>e.join(''))
|
||||
yield* map(permutationsWithReplacement(hintchars, taglen), e=>{
|
||||
if (config.get("hintorder") == "reverse") e = e.reverse()
|
||||
return e.join('')
|
||||
})
|
||||
taglen++
|
||||
}
|
||||
}
|
||||
|
||||
/** Uniform length hintnames */
|
||||
function* hintnames_uniform(n: number, hintchars = HINTCHARS): IterableIterator<string> {
|
||||
function* hintnames_uniform(n: number, hintchars = config.get("hintchars")): IterableIterator<string> {
|
||||
if (n <= hintchars.length)
|
||||
yield* islice(hintchars[Symbol.iterator](), n)
|
||||
else {
|
||||
|
@ -80,7 +84,10 @@ function* hintnames_uniform(n: number, hintchars = HINTCHARS): IterableIterator<
|
|||
const taglen = Math.ceil(log(n, hintchars.length))
|
||||
// And return first n permutations
|
||||
yield* map(islice(permutationsWithReplacement(hintchars, taglen), n),
|
||||
perm => perm.join(''))
|
||||
perm => {
|
||||
if (config.get("hintorder") == "reverse") perm = perm.reverse()
|
||||
return perm.join('')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -136,9 +143,6 @@ class Hint {
|
|||
}
|
||||
}
|
||||
|
||||
const HINTCHARS = 'hjklasdfgyuiopqwertnmzxcvb'
|
||||
/* const HINTCHARS = 'asdf' */
|
||||
|
||||
/** Show only hints prefixed by fstr. Focus first match */
|
||||
function filter(fstr) {
|
||||
const active: Hint[] = []
|
||||
|
@ -191,24 +195,30 @@ function pushKey(ke) {
|
|||
1. Within viewport
|
||||
2. Not hidden by another element
|
||||
*/
|
||||
function hintables() {
|
||||
return Array.from(document.querySelectorAll(HINTTAGS_selectors)).filter(isVisible)
|
||||
function hintables(selectors=HINTTAGS_selectors) {
|
||||
return DOM.getElemsBySelector(selectors, [DOM.isVisible])
|
||||
}
|
||||
|
||||
function elementswithtext() {
|
||||
return Array.from(document.querySelectorAll(HINTTAGS_text_selectors)).filter(
|
||||
isVisible
|
||||
).filter(hint => {
|
||||
return hint.textContent != ""
|
||||
})
|
||||
|
||||
return DOM.getElemsBySelector("*",
|
||||
[DOM.isVisible, hint => {
|
||||
return hint.textContent != ""
|
||||
}]
|
||||
)
|
||||
}
|
||||
|
||||
/** Get array of images in the viewport
|
||||
*/
|
||||
function hintableImages() {
|
||||
/* return [...elementsByXPath(HINTTAGS)].filter(isVisible) as any as Element[] */
|
||||
return Array.from(document.querySelectorAll(HINTTAGS_img_selectors)).filter(
|
||||
isVisible)
|
||||
return DOM.getElemsBySelector(HINTTAGS_img_selectors, [DOM.isVisible])
|
||||
}
|
||||
|
||||
/** Get arrat of "anchors": elements which have id or name and can be addressed
|
||||
* with the hash/fragment in the URL
|
||||
*/
|
||||
function anchors() {
|
||||
return DOM.getElemsBySelector(HINTTAGS_anchor_selectors, [DOM.isVisible])
|
||||
}
|
||||
|
||||
// CSS selectors. More readable for web developers. Not dead. Leaves browser to care about XML.
|
||||
|
@ -246,25 +256,16 @@ select,
|
|||
[tabindex]
|
||||
`
|
||||
|
||||
const HINTTAGS_text_selectors = `
|
||||
input:not([type=hidden]):not([disabled]),
|
||||
a,
|
||||
area,
|
||||
iframe,
|
||||
textarea,
|
||||
button,
|
||||
p,
|
||||
div,
|
||||
pre,
|
||||
code,
|
||||
span
|
||||
`
|
||||
|
||||
const HINTTAGS_img_selectors = `
|
||||
img,
|
||||
[src]
|
||||
`
|
||||
|
||||
const HINTTAGS_anchor_selectors = `
|
||||
[id],
|
||||
[name]
|
||||
`
|
||||
|
||||
import {activeTab, browserBg, l, firefoxVersionAtLeast} from './lib/webext'
|
||||
|
||||
async function openInBackground(url: string) {
|
||||
|
@ -290,7 +291,7 @@ function simulateClick(target: HTMLElement) {
|
|||
) {
|
||||
browserBg.tabs.create({url: (target as HTMLAnchorElement).href})
|
||||
} else {
|
||||
mouseEvent(target, "click")
|
||||
DOM.mouseEvent(target, "click")
|
||||
// Sometimes clicking the element doesn't focus it sufficiently.
|
||||
target.focus()
|
||||
}
|
||||
|
@ -309,8 +310,8 @@ function hintPageOpenInBackground() {
|
|||
})
|
||||
}
|
||||
|
||||
function hintPageSimple() {
|
||||
hintPage(hintables(), hint=>{
|
||||
function hintPageSimple(selectors=HINTTAGS_selectors) {
|
||||
hintPage(hintables(selectors), hint=>{
|
||||
simulateClick(hint.target)
|
||||
})
|
||||
}
|
||||
|
@ -327,6 +328,20 @@ function hintPageYank() {
|
|||
})
|
||||
}
|
||||
|
||||
/** Hint anchors and yank the URL on selection
|
||||
*/
|
||||
function hintPageAnchorYank() {
|
||||
|
||||
hintPage(anchors(), hint=>{
|
||||
|
||||
let anchorUrl = new URL(window.location.href)
|
||||
|
||||
anchorUrl.hash = hint.target.id || hint.target.name;
|
||||
|
||||
messageActiveTab("commandline_frame", "setClipboard", [anchorUrl.href])
|
||||
})
|
||||
}
|
||||
|
||||
/** Hint images, opening in the same tab, or in a background tab
|
||||
*
|
||||
* @param inBackground opens the image source URL in a background tab,
|
||||
|
@ -366,6 +381,7 @@ addListener('hinting_content', attributeCaller({
|
|||
hintPageSimple,
|
||||
hintPageYank,
|
||||
hintPageTextYank,
|
||||
hintPageAnchorYank,
|
||||
hintPageOpenInBackground,
|
||||
hintImage,
|
||||
hintFocus,
|
||||
|
|
|
@ -19,8 +19,13 @@ export async function hintPageYank() {
|
|||
export async function hintPageTextYank() {
|
||||
return await messageActiveTab('hinting_content', 'hintPageTextYank')
|
||||
}
|
||||
export async function hintPageSimple() {
|
||||
return await messageActiveTab('hinting_content', 'hintPageSimple')
|
||||
|
||||
export async function hintPageAnchorYank() {
|
||||
return await messageActiveTab('hinting_content', 'hintPageAnchorYank')
|
||||
}
|
||||
|
||||
export async function hintPageSimple(selectors?) {
|
||||
return await messageActiveTab('hinting_content', 'hintPageSimple',[selectors])
|
||||
}
|
||||
|
||||
export async function hintPageOpenInBackground() {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"manifest_version": 2,
|
||||
"name": "Tridactyl",
|
||||
"version": "1.5.1",
|
||||
"version": "1.6.1",
|
||||
"icons": {
|
||||
"64": "static/logo/Tridactyl_64px.png",
|
||||
"100": "static/logo/Tridactyl_100px.png",
|
||||
|
|
|
@ -3,83 +3,10 @@
|
|||
differs from Vim in that no map may be a prefix of another map (e.g. 'g' and 'gg' cannot both be maps). This simplifies the parser.
|
||||
*/
|
||||
|
||||
// Normal-mode mappings.
|
||||
// keystr -> ex_str
|
||||
// TODO: Move these into a tridactyl-wide state namespace
|
||||
// TODO: stop stealing keys from "insert mode"
|
||||
// r -> refresh page is particularly unhelpful
|
||||
// Can't stringify a map -> just use an object
|
||||
export const DEFAULTNMAPS = {
|
||||
"o": "fillcmdline open",
|
||||
"O": "current_url open",
|
||||
"w": "fillcmdline winopen",
|
||||
"W": "current_url winopen",
|
||||
"t": "fillcmdline tabopen",
|
||||
//["t": "fillcmdline tabopen", // for now, use mozilla completion
|
||||
"]]": "followpage next",
|
||||
"[[": "followpage prev",
|
||||
"[c": "urlincrement -1",
|
||||
"]c": "urlincrement 1",
|
||||
"T": "current_url tabopen",
|
||||
"yy": "clipboard yank",
|
||||
"p": "clipboard open",
|
||||
"P": "clipboard tabopen",
|
||||
"j": "scrollline 10",
|
||||
"k": "scrollline -10",
|
||||
"h": "scrollpx -50",
|
||||
"l": "scrollpx 50",
|
||||
"G": "scrollto 100",
|
||||
"gg": "scrollto 0",
|
||||
"H": "back",
|
||||
"L": "forward",
|
||||
"d": "tabclose",
|
||||
"u": "undo",
|
||||
"r": "reload",
|
||||
"R": "reloadhard",
|
||||
"gi": "focusinput -l",
|
||||
"gt": "tabnext",
|
||||
"gT": "tabprev",
|
||||
"gr": "reader",
|
||||
"gu": "urlparent",
|
||||
"gU": "urlroot",
|
||||
":": "fillcmdline",
|
||||
"s": "fillcmdline open google",
|
||||
"S": "fillcmdline tabopen google",
|
||||
"M": "gobble 1 quickmark",
|
||||
"xx": "something",
|
||||
// "B": "fillcmdline bufferall",
|
||||
"b": "fillcmdline buffer",
|
||||
"ZZ": "qall",
|
||||
"f": "hint",
|
||||
"F": "hint -b",
|
||||
";i": "hint -i",
|
||||
";I": "hint -I",
|
||||
";y": "hint -y",
|
||||
";p": "hint -p",
|
||||
";;": "hint -;",
|
||||
"I": "mode ignore",
|
||||
"a": "current_url bmark",
|
||||
"A": "bmark",
|
||||
// Special keys must be prepended with 🄰
|
||||
// ["🄰Backspace", "something"],
|
||||
}
|
||||
import * as config from '../config'
|
||||
|
||||
let nmaps = Object.assign(Object.create(null), DEFAULTNMAPS)
|
||||
let nmaps = config.get("nmaps")
|
||||
|
||||
// Allow config to be changed in settings
|
||||
// TODO: make this more general
|
||||
browser.storage.sync.get("nmaps").then(lazyloadconfig)
|
||||
async function lazyloadconfig(storageResult){
|
||||
nmaps = Object.assign(Object.create(null), DEFAULTNMAPS, storageResult.nmaps)
|
||||
console.log(nmaps)
|
||||
}
|
||||
|
||||
browser.storage.onChanged.addListener(
|
||||
(changes, areaname) => {
|
||||
if (areaname == "sync") {
|
||||
browser.storage.sync.get("nmaps").then(lazyloadconfig)
|
||||
}
|
||||
})
|
||||
|
||||
// Split a string into a number prefix and some following keys.
|
||||
function keys_split_count(keys: string[]){
|
||||
|
@ -96,7 +23,7 @@ function keys_split_count(keys: string[]){
|
|||
// Given a valid keymap, resolve it to an ex_str
|
||||
function resolve_map(map) {
|
||||
// TODO: This needs to become recursive to allow maps to be defined in terms of other maps.
|
||||
return nmaps[map]
|
||||
return config.get("nmaps")[map]
|
||||
}
|
||||
|
||||
// Valid keystr to ex_str by splitting count, resolving keystr and appending count as final argument.
|
||||
|
@ -116,7 +43,7 @@ function possible_maps(keys): string[] {
|
|||
let [count, keystr] = keys_split_count(keys)
|
||||
|
||||
// Short circuit or search maps.
|
||||
if (Object.getOwnPropertyNames(nmaps).includes(keystr)) {
|
||||
if (Object.getOwnPropertyNames(config.get("nmaps")).includes(keystr)) {
|
||||
return [keystr,]
|
||||
} else {
|
||||
// Efficiency: this can be short-circuited.
|
||||
|
@ -126,7 +53,7 @@ function possible_maps(keys): string[] {
|
|||
|
||||
// A list of maps that start with the fragment.
|
||||
export function completions(fragment): string[] {
|
||||
let posskeystrs = Array.from(Object.keys(nmaps))
|
||||
let posskeystrs = Array.from(Object.keys(config.get("nmaps")))
|
||||
return posskeystrs.filter((key)=>key.startsWith(fragment))
|
||||
}
|
||||
|
||||
|
|
|
@ -66,6 +66,10 @@ input {
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
#completions table tr td {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#completions img {
|
||||
display: inline;
|
||||
vertical-align: middle;
|
||||
|
|
|
@ -21,5 +21,16 @@ span.TridactylHint {
|
|||
transition: unset;
|
||||
}
|
||||
|
||||
.TridactylHintElem { background-color: yellow; }
|
||||
.TridactylHintActive { background-color: #88FF00; }
|
||||
.TridactylHintElem {
|
||||
background-color: rgba(255, 255, 0, 0.25);
|
||||
outline: 1px solid #8F5902;
|
||||
}
|
||||
|
||||
.TridactylHintActive {
|
||||
background-color: #88FF00;
|
||||
outline: 1px solid #CC0000;
|
||||
}
|
||||
|
||||
div.TridactylHintHost {
|
||||
position: static !important;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue