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 antonva-containers
This commit is contained in:
commit
78ba957cde
19 changed files with 2327 additions and 1930 deletions
47
.tridactylrc
Normal file
47
.tridactylrc
Normal file
|
@ -0,0 +1,47 @@
|
|||
" bovine3dom's dogfood
|
||||
|
||||
" Move this to $XDG_CONFIG_DIR/tridactyl/tridactylrc (that's
|
||||
" ~/.config/tridactyl/tridactylrc to mere mortals) or ~/.tridactylrc and
|
||||
" install the native messenger (:installnative in Tridactyl). Run :source to
|
||||
" get it in the browser, or just restart.
|
||||
|
||||
" NB: If you want "vim-like" behaviour where removing a line from
|
||||
" here makes the setting disappear, uncomment the line below.
|
||||
|
||||
"sanitise tridactyllocal tridactylsync
|
||||
|
||||
"
|
||||
" Binds
|
||||
"
|
||||
|
||||
" Comment toggler for Reddit and Hacker News
|
||||
bind ;c hint -c [class*="expand"],[class="togg"]
|
||||
|
||||
"
|
||||
" Misc settings
|
||||
"
|
||||
|
||||
set editorcmd st vim
|
||||
|
||||
" Sane hinting mode
|
||||
set hintfiltermode vimperator-reflow
|
||||
set hintchars 4327895610
|
||||
|
||||
" Make Tridactyl work on more sites at the expense of some security
|
||||
set csp clobber
|
||||
|
||||
" Make quickmarks for the sane Tridactyl issue view
|
||||
quickmark t https://github.com/cmcaine/tridactyl/issues?utf8=%E2%9C%93&q=sort%3Aupdated-desc+
|
||||
|
||||
"
|
||||
" URL redirects
|
||||
"
|
||||
|
||||
" New reddit is bad
|
||||
autocmd DocStart www.reddit.com urlmodify -t www old
|
||||
" Mosquito nets won't make themselves
|
||||
autocmd DocStart www.amazon.co.uk urlmodify -t www smile
|
||||
|
||||
|
||||
" This will have to do until someone writes us a nice syntax file :)
|
||||
" vim: set filetype=vim:
|
59
CHANGELOG.md
59
CHANGELOG.md
|
@ -1,12 +1,69 @@
|
|||
# Tridactyl changelog
|
||||
|
||||
## Release 1.11.0 / Unreleased
|
||||
## Release 1.11.2 / 2018-05-11
|
||||
|
||||
- Hotfix to prevent "config undefined" errors on browser start if no rc file was found
|
||||
- It was mysteriously only reproducible sometimes...
|
||||
- Make newtab changelog a bit wider
|
||||
|
||||
## Release 1.11.1 / 2018-05-11
|
||||
|
||||
- **Add "tridactylrc" support**
|
||||
- Stick a bunch of commands you want to run at startup in one of:
|
||||
- `$XDG_CONFIG_DIR/tridactyl/tridactylrc`
|
||||
- `~/.config/tridactyl/tridactylrc`
|
||||
- `~/.tridactylrc`
|
||||
- [Example file available here](https://github.com/cmcaine/tridactyl/blob/master/.tridactylrc)
|
||||
- You can run any file you want with `source [absolute path to file]`. Bonus points if you can think of something sensible to do with `source` in an `autocmd`.
|
||||
- If you want vim-style configuration where nothing persists except that which is in the rc file, simply add `sanitise tridactyllocal tridactylsync` to the top of your rc file.
|
||||
- Only whole-line comments are supported at the moment, in the VimL style where lines start with a quote mark: "
|
||||
|
||||
- Native messenger updated to 0.1.3
|
||||
- Add rc file reader
|
||||
- Add ability to read environment variables
|
||||
- Make read understand ~ and environment variables (used in `source`)
|
||||
|
||||
- Readme updated
|
||||
- Add statistics page and `guiset`
|
||||
|
||||
- Bug fixes
|
||||
- `guiset` can now cope with multiple Firefox instances running simultaneously provided they are started with profiles explicitly via the command line.
|
||||
|
||||
- Deprecations
|
||||
- Remove buffers,tabs as promised
|
||||
- Inform people pressing `I` of the new bind
|
||||
|
||||
## Release 1.11.0 / 2018-05-09
|
||||
|
||||
- You can now edit the Firefox GUI from Tridactyl with `guiset`. You must restart Firefox after using `guiset` to see the effects.
|
||||
- e.g, `guiset gui none` or `guiset gui full`.
|
||||
- see all the options with `help guiset` and following the links.
|
||||
- **Only minimally tested. Back up your precious userChrome.css if you care about it!**
|
||||
|
||||
- You can now choose to bypass [CSP](https://en.wikipedia.org/wiki/Content_Security_Policy) on all sites with `set csp clobber`. If you change your mind, just `unset csp`, and restart your browser.
|
||||
- This, for example, allows Tridactyl to run on pages such as https://raw.githubusercontent.com/cmcaine/tridactyl/master/CHANGELOG.md, but it could also allow other scripts to run on pages, making the Internet as dangerous as it was about 2 or 3 years ago before CSP was introduced.
|
||||
- Once this [bug](https://bugzilla.mozilla.org/show_bug.cgi?id=1267027) in Firefox is fixed, you won't have to clobber CSP.
|
||||
|
||||
- Tridactyl will no longer update while the browser is running in an attempt to fix issues where the add-on would be unresponsive after an update; instead, it will only update on browser launch.
|
||||
- This includes manual updates via `about:addons`. You'll need to restart the browser after clicking "Check for updates".
|
||||
|
||||
- `set newtab news.bbc.co.uk` etc. now looks much less janky
|
||||
|
||||
- Minor new features
|
||||
- Add !s alias for silent exclaim
|
||||
- `termite` and `terminator` support with `set editorcmd auto`
|
||||
- Allow binding <Esc> (not recommended...)
|
||||
- AMO explains why we need each new permission
|
||||
- Native messenger documentation improved, making it clear that we haven't reimplemented IRC in the browser.
|
||||
|
||||
- Minor bug fixes
|
||||
- Remove pixel gap under command bar (#442)
|
||||
- Native installer no longer requires pip and supports Debian's `which`
|
||||
- Help page links are more legible on rubbish screens
|
||||
- Turn 'q' and 'qall' into aliases
|
||||
- Fix typo regarding binding of special keys on help page
|
||||
- `focusinput` is now better at finding elements to focus
|
||||
|
||||
## Release 1.10.1 / 2018-05-04
|
||||
|
||||
- Add tabcloseallto{right,left} bound to `gx0` and `gx$`
|
||||
|
|
|
@ -2,6 +2,18 @@
|
|||
|
||||
set -e
|
||||
|
||||
echoerr() {
|
||||
red="\033[31m"
|
||||
normal="\e[0m"
|
||||
echo -e "$red$@$normal" >&2
|
||||
}
|
||||
|
||||
sedEscape() {
|
||||
sed 's/[&/\]/\\&/g' <<< "$@"
|
||||
}
|
||||
|
||||
trap "echoerr 'Failed to install!'" ERR
|
||||
|
||||
# To install, curl -fsSl 'url to this script' | bash
|
||||
|
||||
XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config/tridactyl}"
|
||||
|
@ -35,21 +47,17 @@ else
|
|||
curl -sS --create-dirs -o "$native_file" "$native_loc"
|
||||
fi
|
||||
|
||||
native_file_escaped=$(sed 's/[&/\]/\\&/g' <<< "$native_file_final")
|
||||
|
||||
sed -i.bak "s/REPLACE_ME_WITH_SED/$native_file_escaped/" "$manifest_file"
|
||||
sed -i.bak "s/REPLACE_ME_WITH_SED/$(sedEscape "$native_file_final")/" "$manifest_file"
|
||||
chmod +x $native_file
|
||||
|
||||
# Requirements for native messenger
|
||||
python_path=$(which python3)
|
||||
pip_path=$(which pip3)
|
||||
python_file_escaped=$(sed 's/[&/\]/\\&/g' <<< "$python_path")
|
||||
if [[ -x "$python_path" ]] && [[ -x "$pip_path" ]]; then
|
||||
sed -i.bak "1s/.*/#!$python_file_escaped/" "$native_file"
|
||||
python_path=$(which python3) || python_path=""
|
||||
if [[ -x "$python_path" ]]; then
|
||||
sed -i.bak "1s/.*/#!$(sedEscape "$python_path")/" "$native_file"
|
||||
mv "$native_file" "$native_file_final"
|
||||
else
|
||||
echo "Error: Python 3 and pip3 must exist in PATH."
|
||||
echo "Please install them and run this script again."
|
||||
echoerr "Error: Python 3 must exist in PATH."
|
||||
echoerr "Please install it and run this script again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
|
|
@ -8,8 +8,7 @@ import struct
|
|||
import subprocess
|
||||
import tempfile
|
||||
|
||||
VERSION = "0.1.1"
|
||||
|
||||
VERSION = "0.1.3"
|
||||
|
||||
class NoConnectionError(Exception):
|
||||
""" Exception thrown when stdin cannot be read """
|
||||
|
@ -22,11 +21,10 @@ def eprint(*args, **kwargs):
|
|||
print(*args, file=sys.stderr, flush=True, **kwargs)
|
||||
|
||||
|
||||
def _getenv(variable, default):
|
||||
def getenv(variable, default):
|
||||
""" Get an environment variable value, or use the default provided """
|
||||
return os.environ.get(variable) or default
|
||||
|
||||
|
||||
def getMessage():
|
||||
"""Read a message from stdin and decode it.
|
||||
|
||||
|
@ -66,6 +64,43 @@ def sendMessage(encodedMessage):
|
|||
sys.stdout.buffer.flush()
|
||||
|
||||
|
||||
def findUserConfigFile():
|
||||
""" Find a user config file, if it exists. Return the file path, or None
|
||||
if not found
|
||||
"""
|
||||
home = os.path.expanduser('~')
|
||||
config_dir = getenv('XDG_CONFIG_HOME', os.path.expanduser('~/.config'))
|
||||
|
||||
# Will search for files in this order
|
||||
candidate_files = [
|
||||
os.path.join(config_dir, "tridactyl", "tridactylrc"),
|
||||
os.path.join(home, '.tridactylrc')
|
||||
]
|
||||
|
||||
config_path = None
|
||||
|
||||
# find the first path in the list that exists
|
||||
for path in candidate_files:
|
||||
if os.path.isfile(path):
|
||||
config_path = path
|
||||
break
|
||||
|
||||
return config_path
|
||||
|
||||
|
||||
def getUserConfig():
|
||||
# look it up freshly each time - the user could have moved or killed it
|
||||
cfg_file = findUserConfigFile()
|
||||
|
||||
# no file, return
|
||||
if not cfg_file:
|
||||
return None
|
||||
|
||||
# for now, this is a simple file read, but if the files can
|
||||
# include other files, that will need more work
|
||||
return open(cfg_file, 'r').read()
|
||||
|
||||
|
||||
def handleMessage(message):
|
||||
""" Generate reply from incoming message. """
|
||||
cmd = message["cmd"]
|
||||
|
@ -74,6 +109,13 @@ def handleMessage(message):
|
|||
if cmd == 'version':
|
||||
reply = {'version': VERSION}
|
||||
|
||||
elif cmd == 'getconfig':
|
||||
file_content = getUserConfig()
|
||||
if file_content:
|
||||
reply['content'] = file_content
|
||||
else:
|
||||
reply['code'] = 'File not found'
|
||||
|
||||
elif cmd == 'run':
|
||||
commands = message["command"]
|
||||
|
||||
|
@ -92,7 +134,7 @@ def handleMessage(message):
|
|||
|
||||
elif cmd == 'read':
|
||||
try:
|
||||
with open(message["file"], "r") as file:
|
||||
with open(os.path.expandvars(os.path.expanduser(message["file"])), "r") as file:
|
||||
reply['content'] = file.read()
|
||||
reply['code'] = 0
|
||||
except FileNotFoundError:
|
||||
|
@ -116,6 +158,9 @@ def handleMessage(message):
|
|||
file.write(message["content"])
|
||||
reply['content'] = filepath
|
||||
|
||||
elif cmd == 'env':
|
||||
reply['content'] = getenv(message["var"], "")
|
||||
|
||||
else:
|
||||
reply = {'cmd': 'error', 'error': 'Unhandled message'}
|
||||
eprint('Unhandled message: {}'.format(message))
|
||||
|
|
3631
package-lock.json
generated
3631
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -117,7 +117,7 @@ See `:help bind` for details about this command.
|
|||
|
||||
- Navigation to any about:\* pages using `:open` requires the native messenger.
|
||||
- Firefox will not load Tridactyl on addons.mozilla.org, about:\*, some file:\* URIs, view-source:\*, or data:\*. On these pages Ctrl-L (or F6), Ctrl-Tab and Ctrl-W are your escape hatches.
|
||||
- Tridactyl does not currently support changing/hiding the Firefox GUI, but you can do it yourself by changing your userChrome. We've developed [quite a good one](src/static/userChrome-minimal.css) that makes windowed Firefox behave more like full-screen mode, but it's well commented, so you can make your own.
|
||||
- Tridactyl now supports changing the Firefox GUI if you have the native messenger installed via `guiset`. There's quite a few options available, but `guiset gui none` is probably what you want, perhaps followed up with `guiset tabs always`.
|
||||
|
||||
## Frequently asked questions
|
||||
|
||||
|
@ -195,6 +195,9 @@ See `:help bind` for details about this command.
|
|||
|
||||
Press `j` and see if you scroll down :) There's no status line yet: see [#210](https://github.com/cmcaine/tridactyl/issues/210).
|
||||
|
||||
- Does anyone actually use Tridactyl?
|
||||
|
||||
In addition to the developers, some other people do. Mozilla keeps tabs on them [here](https://addons.mozilla.org/en-US/firefox/addon/tridactyl-vim/statistics/?last=30).
|
||||
|
||||
|
||||
## Contributing
|
||||
|
|
|
@ -27,6 +27,7 @@ import * as gobble_mode from "./parsers/gobblemode"
|
|||
import * as input_mode from "./parsers/inputmode"
|
||||
import * as itertools from "./itertools"
|
||||
import * as keyseq from "./keyseq"
|
||||
import * as request from "./requests"
|
||||
import * as native from "./native_background"
|
||||
import * as msgsafe from "./msgsafe"
|
||||
import state from "./state"
|
||||
|
@ -47,8 +48,37 @@ import * as webext from "./lib/webext"
|
|||
keydown_background,
|
||||
native,
|
||||
keyseq,
|
||||
request,
|
||||
msgsafe,
|
||||
state,
|
||||
webext,
|
||||
l: prom => prom.then(console.log).catch(console.error),
|
||||
})
|
||||
|
||||
// This should be removed once https://bugzilla.mozilla.org/show_bug.cgi?id=1267027 is fixed
|
||||
let cspListener
|
||||
if (config.get("csp") == "clobber") {
|
||||
cspListener = browser.webRequest.onHeadersReceived.addListener(
|
||||
request.addurltocsp,
|
||||
{ urls: ["<all_urls>"], types: ["main_frame"] },
|
||||
["blocking", "responseHeaders"],
|
||||
)
|
||||
}
|
||||
browser.storage.onChanged.addListener((changes, areaname) => {
|
||||
if (config.get("csp") == "clobber") {
|
||||
cspListener = browser.webRequest.onHeadersReceived.addListener(
|
||||
request.addurltocsp,
|
||||
{ urls: ["<all_urls>"], types: ["main_frame"] },
|
||||
["blocking", "responseHeaders"],
|
||||
)
|
||||
} else {
|
||||
// This doesn't work. :(
|
||||
// browser.webRequest.onHeadersReceived.removeListener(cspListener)
|
||||
}
|
||||
})
|
||||
|
||||
// Prevent Tridactyl from being updated while it is running in the hope of fixing #290
|
||||
browser.runtime.onUpdateAvailable.addListener(_ => {})
|
||||
|
||||
// Try to load an RC file
|
||||
excmds.source_quiet()
|
||||
|
|
|
@ -123,6 +123,7 @@ const DEFAULTS = o({
|
|||
";#": "hint -#",
|
||||
";v": "hint -W exclaim_quiet mpv",
|
||||
"<S-Insert>": "mode ignore",
|
||||
I: "fillcmdline Ignore mode is now toggled by pressing <S-Insert>",
|
||||
a: "current_url bmark",
|
||||
A: "bmark",
|
||||
zi: "zoom 0.1 true",
|
||||
|
@ -262,6 +263,10 @@ const DEFAULTS = o({
|
|||
|
||||
// number of most recent results to ask Firefox for. We display the top 20 or so most frequently visited ones.
|
||||
historyresults: "50",
|
||||
|
||||
// Security settings
|
||||
|
||||
csp: "untouched", // change this to "clobber" to ruin the CSP of all sites and make Tridactyl run a bit better on some of them, e.g. raw.github*
|
||||
})
|
||||
|
||||
/** Given an object and a target, extract the target if it exists, else return undefined
|
||||
|
|
29
src/config_rc.ts
Normal file
29
src/config_rc.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
import * as Controller from "./controller"
|
||||
import * as Native from "./native_background"
|
||||
import Logger from "./logging"
|
||||
const logger = new Logger("rc")
|
||||
|
||||
export async function source(filename = "auto") {
|
||||
let rctext = ""
|
||||
if (filename == "auto") {
|
||||
rctext = await Native.getrc()
|
||||
} else {
|
||||
rctext = (await Native.read(filename)).content
|
||||
}
|
||||
if (rctext === undefined) return false
|
||||
runRc(rctext)
|
||||
return true
|
||||
}
|
||||
|
||||
export async function runRc(rc: string) {
|
||||
for (let cmd of rcFileToExCmds(rc)) {
|
||||
await Controller.acceptExCmd(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
export function rcFileToExCmds(rcText: string): string[] {
|
||||
const excmds = rcText.split("\n")
|
||||
|
||||
// Remove comment lines
|
||||
return excmds.filter(x => x != "" && !x.trim().startsWith('"'))
|
||||
}
|
|
@ -6,21 +6,27 @@ import * as CSS from "css"
|
|||
// type: "rule", selectors: [string], declarations: [
|
||||
// {type: "declaration", property: string, value: string}
|
||||
|
||||
/** Find rules in sheet that match selector */
|
||||
export function findCssRules(
|
||||
ruleToFind: string,
|
||||
selector: string,
|
||||
sheet: CSS.Stylesheet,
|
||||
): number[] {
|
||||
const filtSheet = [...sheet.stylesheet.rules.entries()].filter(x => {
|
||||
const rule = x[1]
|
||||
return (
|
||||
rule.type == "rule" &&
|
||||
rule["selectors"].filter(sel => sel == ruleToFind).length > 0
|
||||
)
|
||||
return rule.type == "rule" && rule["selectors"].indexOf(selector) > -1
|
||||
})
|
||||
return filtSheet.map(x => x[0])
|
||||
}
|
||||
|
||||
// TODO: make this more flexible with cleverer matching of selectors, merging of options
|
||||
/** Rulename -> { name: <selector>, options: { <option-name>: <css-string> } }
|
||||
*
|
||||
* Multi-level map of rulename, options available for rule and css for each option.
|
||||
*
|
||||
* *findCssRules and changeSingleCss rely on the selector not containing a comma.*
|
||||
*
|
||||
* TODO: make this more flexible with cleverer matching of selectors, merging of options
|
||||
*
|
||||
*/
|
||||
export const potentialRules = {
|
||||
hoverlink: {
|
||||
name: `statuspanel[type="overLink"]`,
|
||||
|
@ -107,6 +113,10 @@ export const potentialRules = {
|
|||
//
|
||||
// was just a simple show/hide if the characters appeared in the setting
|
||||
|
||||
/** Rules that index into potentialRules or metaRules
|
||||
*
|
||||
* Please don't add cycles to this table :)
|
||||
*/
|
||||
export const metaRules = {
|
||||
gui: {
|
||||
none: {
|
||||
|
@ -127,14 +137,17 @@ export const metaRules = {
|
|||
tabs: {
|
||||
none: {
|
||||
tabstoolbar: "none",
|
||||
navtoolboxunfocused: "hide",
|
||||
},
|
||||
always: {
|
||||
tabstoolbar: "show",
|
||||
tabstoolbarunfocused: "show",
|
||||
navtoolboxunfocused: "show",
|
||||
},
|
||||
autohide: {
|
||||
tabstoolbar: "show",
|
||||
tabstoolbarunfocused: "hide",
|
||||
navtoolboxunfocused: "hide",
|
||||
},
|
||||
},
|
||||
navbar: {
|
||||
|
@ -151,39 +164,40 @@ export const metaRules = {
|
|||
},
|
||||
}
|
||||
|
||||
/** Add desired non-meta rule to stylesheet replacing existing rule with the same selector if present */
|
||||
export function changeSingleCss(
|
||||
rulename: string,
|
||||
optionname: string,
|
||||
sheet: CSS.Stylesheet,
|
||||
): CSS.Stylesheet {
|
||||
const ruleInds = findCssRules(potentialRules[rulename]["name"], sheet)
|
||||
const desRule =
|
||||
potentialRules[rulename]["name"] +
|
||||
" {" +
|
||||
potentialRules[rulename]["options"][optionname] +
|
||||
"}"
|
||||
const miniSheet = CSS.parse(desRule).stylesheet.rules[0]
|
||||
if (ruleInds.length > 0) {
|
||||
sheet.stylesheet.rules[ruleInds[0]] = miniSheet
|
||||
const selector = potentialRules[rulename]["name"]
|
||||
const newRule = `${selector} {
|
||||
${potentialRules[rulename]["options"][optionname]}
|
||||
}`
|
||||
const miniSheet = CSS.parse(newRule).stylesheet.rules[0]
|
||||
|
||||
// Find pre-existing rules
|
||||
const oldRuleIndexes = findCssRules(selector, sheet)
|
||||
if (oldRuleIndexes.length > 0) {
|
||||
sheet.stylesheet.rules[oldRuleIndexes[0]] = miniSheet
|
||||
} else {
|
||||
sheet.stylesheet.rules = sheet.stylesheet.rules.concat(miniSheet)
|
||||
}
|
||||
|
||||
return sheet
|
||||
}
|
||||
|
||||
/** Apply rule to stylesheet. rulename, optionname identify a rule. They may be meta rules */
|
||||
export function changeCss(
|
||||
rulename: string,
|
||||
optionname: string,
|
||||
sheet: CSS.Stylesheet,
|
||||
): CSS.Stylesheet {
|
||||
if (rulename in metaRules) {
|
||||
for (let rule of Object.keys(metaRules[rulename][optionname])) {
|
||||
const metarule = metaRules[rulename][optionname]
|
||||
for (let rule of Object.keys(metarule)) {
|
||||
// have a metarule call itself for hours of fun
|
||||
sheet = changeCss(
|
||||
rule,
|
||||
metaRules[rulename][optionname][rule],
|
||||
sheet,
|
||||
)
|
||||
sheet = changeCss(rule, metarule[rule], sheet)
|
||||
}
|
||||
} else sheet = changeSingleCss(rulename, optionname, sheet)
|
||||
return sheet
|
||||
|
|
103
src/excmds.ts
103
src/excmds.ts
|
@ -97,7 +97,6 @@ import * as Logging from "./logging"
|
|||
const logger = new Logging.Logger("excmds")
|
||||
import Mark from "mark.js"
|
||||
import * as CSS from "css"
|
||||
import * as semverCompare from "semver-compare"
|
||||
|
||||
//#content_helper
|
||||
// {
|
||||
|
@ -118,6 +117,7 @@ import { ModeName } from "./state"
|
|||
import * as keydown from "./keydown_background"
|
||||
import { activeTab, firefoxVersionAtLeast, openInNewTab } from "./lib/webext"
|
||||
import * as CommandLineBackground from "./commandline_background"
|
||||
import * as rc from "./config_rc"
|
||||
|
||||
//#background_helper
|
||||
import * as Native from "./native_background"
|
||||
|
@ -167,7 +167,7 @@ export async function getinput() {
|
|||
*/
|
||||
//#background
|
||||
export async function editor() {
|
||||
if (!await nativegate()) return
|
||||
if (!await Native.nativegate()) return
|
||||
const file = (await Native.temp(await getinput())).content
|
||||
fillinput((await Native.editor(file)).content)
|
||||
// TODO: add annoying "This message was written with [Tridactyl](https://addons.mozilla.org/en-US/firefox/addon/tridactyl-vim/)"
|
||||
|
@ -217,17 +217,17 @@ export async function guiset(rule: string, option: string) {
|
|||
// Could potentially fall back to sending minimal example to clipboard if native not installed
|
||||
|
||||
// Check for native messenger and make sure we have a plausible profile directory
|
||||
if (!await nativegate("0.1.1")) return
|
||||
if (!await Native.nativegate("0.1.1")) return
|
||||
let profile_dir = ""
|
||||
if (config.get("profiledir") === "auto") {
|
||||
if (["linux", "openbsd", "mac"].includes((await browser.runtime.getPlatformInfo()).os)) profile_dir = await Native.getProfileDir()
|
||||
else {
|
||||
fillcmdline("Please set your profile directory (found on about:support) via `set profiledir [profile directory]`")
|
||||
return
|
||||
}
|
||||
} else profile_dir = config.get("profiledir")
|
||||
if (config.get("profiledir") === "auto" && ["linux", "openbsd", "mac"].includes((await browser.runtime.getPlatformInfo()).os)) {
|
||||
try {
|
||||
profile_dir = await Native.getProfileDir()
|
||||
} catch (e) {}
|
||||
} else {
|
||||
profile_dir = config.get("profiledir")
|
||||
}
|
||||
if (profile_dir == "") {
|
||||
logger.error("Profile not found.")
|
||||
fillcmdline("Please set your profile directory (found on about:support) via `set profiledir [profile directory]`")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -241,7 +241,8 @@ export async function guiset(rule: string, option: string) {
|
|||
// Modify and write new CSS
|
||||
if (cssstr === "") cssstr = `@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");`
|
||||
let stylesheet = CSS.parse(cssstr)
|
||||
let stylesheetDone = CSS.stringify(css_util.changeCss(rule, option, stylesheet))
|
||||
// Trim due to https://github.com/reworkcss/css/issues/114
|
||||
let stylesheetDone = CSS.stringify(css_util.changeCss(rule, option, stylesheet)).trim()
|
||||
Native.write(profile_dir + "/chrome/userChrome.css", stylesheetDone)
|
||||
}
|
||||
|
||||
|
@ -263,37 +264,11 @@ export function cssparse(...css: string[]) {
|
|||
//#background
|
||||
export async function nativeopen(url: string, ...firefoxArgs: string[]) {
|
||||
if (firefoxArgs.length === 0) firefoxArgs = ["--new-tab"]
|
||||
if (await nativegate()) {
|
||||
if (await Native.nativegate()) {
|
||||
Native.run(config.get("browser") + " " + firefoxArgs.join(" ") + " " + url)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used internally to gate off functions that use the native messenger. Gives a helpful error message in the command line if the native messenger is not installed, or is the wrong version.
|
||||
*/
|
||||
//#background
|
||||
export async function nativegate(version = "0", interactive = true): Promise<Boolean> {
|
||||
if (["win", "android"].includes((await browser.runtime.getPlatformInfo()).os)) {
|
||||
if (interactive == true) fillcmdline("# Tridactyl's native messenger doesn't support your operating system, yet.")
|
||||
return false
|
||||
}
|
||||
try {
|
||||
const actualVersion = await Native.getNativeMessengerVersion()
|
||||
if (actualVersion !== undefined) {
|
||||
if (semverCompare(version, actualVersion) > 0) {
|
||||
if (interactive == true) fillcmdline("# Please update to native messenger " + version + ", for example by running `:updatenative`.")
|
||||
// TODO: add update procedure and document here.
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} else if (interactive == true) fillcmdline("# Native messenger not found. Please run `:installnative` and follow the instructions.")
|
||||
return false
|
||||
} catch (e) {
|
||||
if (interactive == true) fillcmdline("# Native messenger not found. Please run `:installnative` and follow the instructions.")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run command in /bin/sh (unless you're on Windows), and print the output in the command line. Non-zero exit codes and stderr are ignored, currently.
|
||||
*
|
||||
|
@ -337,6 +312,34 @@ export async function installnative() {
|
|||
fillcmdline("# Installation command copied to clipboard. Please paste and run it in your shell to install the native messenger.")
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs an RC file from disk.
|
||||
*
|
||||
* If no argument given, it will try to open ~/.tridactylrc, ~/.config/tridactylrc or $XDG_CONFIG_HOME/tridactyl/tridactylrc in reverse order.
|
||||
*
|
||||
* The RC file is just a bunch of Tridactyl excmds (i.e, the stuff on this help page). Settings persist in local storage; add `sanitise tridactyllocal tridactylsync` to make it more Vim like. There's an [example file](https://www.github.com/cmcaine/tridactyl/master/.tridactylrc) if you want it.
|
||||
*
|
||||
* @param fileArr the file to open. Must be an absolute path, but can contain environment variables and things like ~.
|
||||
*/
|
||||
//#background
|
||||
export async function source(...fileArr: string[]) {
|
||||
const file = fileArr.join(" ") || undefined
|
||||
if (await Native.nativegate("0.1.3")) if (!await rc.source(file)) logger.error("Could not find RC file")
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as [[source]] but suppresses all errors
|
||||
*/
|
||||
//#background
|
||||
export async function source_quiet(...fileArr: string[]) {
|
||||
try {
|
||||
const file = fileArr.join(" ") || undefined
|
||||
if (await Native.nativegate("0.1.3", false)) rc.source(file)
|
||||
} catch (e) {
|
||||
logger.info("Automatic loading of RC file failed.")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the native messenger if it is installed, using our GitHub repo. This is run every time Tridactyl is updated.
|
||||
*
|
||||
|
@ -344,7 +347,7 @@ export async function installnative() {
|
|||
*/
|
||||
//#background
|
||||
export async function updatenative(interactive = true) {
|
||||
if (await nativegate("0", interactive)) {
|
||||
if (await Native.nativegate("0", interactive)) {
|
||||
if ((await browser.runtime.getPlatformInfo()).os === "mac") {
|
||||
if (interactive) logger.error("Updating the native messenger on OSX is broken. Please use `:installnative` instead.")
|
||||
return
|
||||
|
@ -1688,26 +1691,6 @@ export async function clipboard(excmd: "open" | "yank" | "yankshort" | "yankcano
|
|||
CommandLineBackground.hide()
|
||||
}
|
||||
|
||||
// {{{ Buffer/completion stuff
|
||||
|
||||
/** Equivalent to `fillcmdline buffer`
|
||||
|
||||
Sort of Vimperator alias
|
||||
*/
|
||||
//#background
|
||||
export async function tabs() {
|
||||
fillcmdline("Deprecated. If anyone actually uses this, they should file an issue on GitHub.")
|
||||
}
|
||||
|
||||
/** Equivalent to `fillcmdline buffer`
|
||||
|
||||
Sort of Vimperator alias
|
||||
*/
|
||||
//#background
|
||||
export async function buffers() {
|
||||
tabs()
|
||||
}
|
||||
|
||||
/** Change active tab.
|
||||
|
||||
@param index
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"manifest_version": 2,
|
||||
"name": "Tridactyl",
|
||||
"version": "1.10.1",
|
||||
"version": "1.11.2",
|
||||
"icons": {
|
||||
"64": "static/logo/Tridactyl_64px.png",
|
||||
"100": "static/logo/Tridactyl_100px.png",
|
||||
|
|
|
@ -2,13 +2,23 @@
|
|||
* Background functions for the native messenger interface
|
||||
*/
|
||||
|
||||
import * as semverCompare from "semver-compare"
|
||||
import * as config from "./config"
|
||||
|
||||
import Logger from "./logging"
|
||||
const logger = new Logger("native")
|
||||
|
||||
const NATIVE_NAME = "tridactyl"
|
||||
type MessageCommand = "version" | "run" | "read" | "write" | "temp" | "mkdir"
|
||||
type MessageCommand =
|
||||
| "version"
|
||||
| "run"
|
||||
| "read"
|
||||
| "write"
|
||||
| "temp"
|
||||
| "mkdir"
|
||||
| "eval"
|
||||
| "getconfig"
|
||||
| "env"
|
||||
interface MessageResp {
|
||||
cmd: string
|
||||
version: number | null
|
||||
|
@ -41,6 +51,18 @@ async function sendNativeMsg(
|
|||
}
|
||||
}
|
||||
|
||||
export async function getrc(): Promise<string> {
|
||||
const res = await sendNativeMsg("getconfig", {})
|
||||
|
||||
if (res.content && !res.error) {
|
||||
logger.info(`Successfully retrieved fs config:\n${res.content}`)
|
||||
return res.content
|
||||
} else {
|
||||
// Have to make this a warning as async exceptions apparently don't get caught
|
||||
logger.info(`Error in retrieving config: ${res.error}`)
|
||||
}
|
||||
}
|
||||
|
||||
export async function getNativeMessengerVersion(
|
||||
quiet = false,
|
||||
): Promise<number> {
|
||||
|
@ -80,10 +102,12 @@ export async function getBestEditor(): Promise<string> {
|
|||
"xterm -class tridactyl_editor -e",
|
||||
"uxterm -class tridactyl_editor -e",
|
||||
"urxvt -e",
|
||||
// "terminator -e", // NB: requires command to be in quotes, which breaks the others
|
||||
// so terminator is not supported.
|
||||
"alacritty -e", // alacritty is nice but takes ages to start and doesn't support class
|
||||
"cool-retro-term -e",
|
||||
// Terminator and termite require -e commands to be in quotes, hence the extra quote at the end.
|
||||
// The closing quote is implemented in the editor function.
|
||||
'terminator -e "',
|
||||
'termite --class tridactyl_editor -e "',
|
||||
"dbus-launch gnome-terminal --",
|
||||
// I wanted to put hyper.js here as a joke but you can't start it running a command,
|
||||
// which is a far better joke: a terminal emulator that you can't send commands to.
|
||||
|
@ -120,6 +144,59 @@ export async function getBestEditor(): Promise<string> {
|
|||
return cmd
|
||||
}
|
||||
|
||||
/**
|
||||
* Used internally to gate off functions that use the native messenger. Gives a
|
||||
* helpful error message in the command line if the native messenger is not
|
||||
* installed, or is the wrong version.
|
||||
*
|
||||
* @arg version: A string representing the minimal required version.
|
||||
* @arg interactive: True if a message should be displayed on version mismatch.
|
||||
* @return false if the required version is higher than the currently available
|
||||
* native messenger version.
|
||||
*/
|
||||
export async function nativegate(
|
||||
version = "0",
|
||||
interactive = true,
|
||||
): Promise<Boolean> {
|
||||
if (
|
||||
["win", "android"].includes(
|
||||
(await browser.runtime.getPlatformInfo()).os,
|
||||
)
|
||||
) {
|
||||
if (interactive == true)
|
||||
logger.error(
|
||||
"# Tridactyl's native messenger doesn't support your operating system, yet.",
|
||||
)
|
||||
return false
|
||||
}
|
||||
try {
|
||||
const actualVersion = await getNativeMessengerVersion()
|
||||
if (actualVersion !== undefined) {
|
||||
if (semverCompare(version, actualVersion) > 0) {
|
||||
if (interactive == true)
|
||||
logger.error(
|
||||
"# Please update to native messenger " +
|
||||
version +
|
||||
", for example by running `:updatenative`.",
|
||||
)
|
||||
// TODO: add update procedure and document here.
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} else if (interactive == true)
|
||||
logger.error(
|
||||
"# Native messenger not found. Please run `:installnative` and follow the instructions.",
|
||||
)
|
||||
return false
|
||||
} catch (e) {
|
||||
if (interactive == true)
|
||||
logger.error(
|
||||
"# Native messenger not found. Please run `:installnative` and follow the instructions.",
|
||||
)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export async function inpath(cmd) {
|
||||
return (await run("which " + cmd.split(" ")[0])).code === 0
|
||||
}
|
||||
|
@ -142,7 +219,11 @@ export async function editor(file: string, content?: string) {
|
|||
config.get("editorcmd") == "auto"
|
||||
? await getBestEditor()
|
||||
: config.get("editorcmd")
|
||||
await run(editorcmd + " " + file)
|
||||
// Dirty hacks for termite and terminator support part 2.
|
||||
const e = editorcmd.split(" ")[0]
|
||||
if (e === "termite" || e === "terminator") {
|
||||
await run(editorcmd + " " + file + '"')
|
||||
} else await run(editorcmd + " " + file)
|
||||
return await read(file)
|
||||
}
|
||||
|
||||
|
@ -161,24 +242,84 @@ export async function mkdir(dir: string, exist_ok: boolean) {
|
|||
export async function temp(content: string) {
|
||||
return sendNativeMsg("temp", { content })
|
||||
}
|
||||
|
||||
export async function run(command: string) {
|
||||
let msg = await sendNativeMsg("run", { command })
|
||||
logger.info(msg)
|
||||
return msg
|
||||
}
|
||||
|
||||
/** Evaluates a string in the native messenger. This has to be python code. If
|
||||
* you want to run shell strings, use run() instead.
|
||||
*/
|
||||
export async function pyeval(command: string): Promise<MessageResp> {
|
||||
return sendNativeMsg("eval", { command })
|
||||
}
|
||||
|
||||
export async function getenv(variable: string) {
|
||||
let v = await getNativeMessengerVersion()
|
||||
if (!await nativegate("0.1.2", false)) {
|
||||
throw `Error: getenv needs native messenger v>=0.1.2. Current: ${v}`
|
||||
}
|
||||
return (await sendNativeMsg("env", { var: variable })).content
|
||||
}
|
||||
|
||||
/** This returns the commandline that was used to start firefox.
|
||||
You'll get both firefox binary (not necessarily an absolute path) and flags */
|
||||
export async function ffargs(): Promise<string[]> {
|
||||
// Using ' and + rather that ` because we don't want newlines
|
||||
let output = await pyeval(
|
||||
'handleMessage({"cmd": "run", ' +
|
||||
'"command": "ps -p " + str(os.getppid()) + " -oargs="})["content"]',
|
||||
)
|
||||
return output.content.trim().split(" ")
|
||||
}
|
||||
|
||||
export async function getProfileDir() {
|
||||
// First, see if we can get the profile from the arguments that were given
|
||||
// to Firefox
|
||||
let args = await ffargs()
|
||||
|
||||
// --profile <path>: Start with profile at <path>
|
||||
let prof = args.indexOf("--profile")
|
||||
if (prof >= 0) return args[prof + 1]
|
||||
|
||||
// -P <profile>: Start with <profile>
|
||||
// -P : Start with profile manager
|
||||
// Apparently, this argument is case-insensitive
|
||||
let profileName = "*"
|
||||
prof = args.indexOf("-P")
|
||||
if (prof < 0) prof = args.indexOf("-p")
|
||||
// args.length -1 because we need to make sure -P was given a value
|
||||
if (prof >= 0 && prof < args.length - 1) profileName = args[prof + 1]
|
||||
|
||||
// Find active profile directory automatically by seeing where the lock exists
|
||||
let hacky_profile_finder = "find ../../../.mozilla/firefox -name lock"
|
||||
let home = "../../.."
|
||||
try {
|
||||
// We try not to use a relative path because ~/.local (the directory where
|
||||
// the native messenger currently sits) might actually be a symlink
|
||||
home = await getenv("HOME")
|
||||
} catch (e) {}
|
||||
let hacky_profile_finder = `find "${home}/.mozilla/firefox" -maxdepth 2 -path '*.${profileName}/lock'`
|
||||
if ((await browser.runtime.getPlatformInfo()).os === "mac")
|
||||
hacky_profile_finder =
|
||||
"find ../../../Library/'Application Support'/Firefox/Profiles -maxdepth 2 -name .parentlock"
|
||||
let profilecmd = await run(hacky_profile_finder)
|
||||
if (profilecmd.code != 0) {
|
||||
return ""
|
||||
} else
|
||||
return profilecmd.content
|
||||
.split("/")
|
||||
.slice(0, -1)
|
||||
.join("/")
|
||||
throw new Error("Profile not found")
|
||||
} else {
|
||||
// Remove trailing newline
|
||||
profilecmd.content = profilecmd.content.trim()
|
||||
if (profilecmd.content.split("\n").length > 1) {
|
||||
throw new Error(
|
||||
"Multiple profiles in use. Can't tell which one you want. `set profiledir`, close other Firefox profiles or remove zombie lock files.",
|
||||
)
|
||||
} else {
|
||||
// Get parent directory of lock file
|
||||
return profilecmd.content
|
||||
.split("/")
|
||||
.slice(0, -1)
|
||||
.join("/")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
32
src/requests.ts
Normal file
32
src/requests.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import * as config from "./config"
|
||||
|
||||
export function addurltocsp(response) {
|
||||
let headers = response["responseHeaders"]
|
||||
let cspind = headers.findIndex(
|
||||
header => header.name == "Content-Security-Policy",
|
||||
)
|
||||
// if it's found
|
||||
if (cspind > -1) {
|
||||
// Split the csp header up so we can manage it individually.
|
||||
let csparr = [headers[cspind]["value"].split("; ")][0]
|
||||
|
||||
for (let i = 0; i < csparr.length; i++) {
|
||||
// Add 'unsafe-inline' as a directive since we use it
|
||||
if (csparr[i].indexOf("style-src") > -1) {
|
||||
if (csparr[i].indexOf("'self'") > -1) {
|
||||
csparr[i] = csparr[i].replace(
|
||||
"'self'",
|
||||
"'self' 'unsafe-inline'",
|
||||
)
|
||||
}
|
||||
}
|
||||
// Remove the element if it's a sandbox directive
|
||||
if (csparr[i] === "sandbox") {
|
||||
csparr.splice(i, 1)
|
||||
}
|
||||
}
|
||||
// Join the header up after clobberin'
|
||||
headers[cspind]["value"] = csparr.join("; ")
|
||||
}
|
||||
return { responseHeaders: headers }
|
||||
}
|
|
@ -29,7 +29,6 @@ input[id^="spoiler"]:checked + label {
|
|||
background: #ccc;
|
||||
}
|
||||
input[id^="spoiler"] ~ .spoiler {
|
||||
width: 90%;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
|
|
|
@ -12,7 +12,7 @@ Tridactyl has to override your new tab page due to WebExtension limitations. You
|
|||
|
||||
- If you're enjoying Tridactyl (or not), please leave a review on [addons.mozilla.org][amo].
|
||||
|
||||
- **Breaking change to default settings:** ignore mode is now bound to `<S-Insert>` for both entering and leaving the mode. Previous binds are unbound.
|
||||
- **Manual intervention required. The next update will require the "cookies" permission:** we need the permission so that we can support opening links in the same container that they were opened in. Please keep an eye out for the tiny desaturated yellow exclamation mark on Firefox's hamburger menu that will notify you that the update is available. Click it to update.
|
||||
|
||||
- **NB:** Tridactyl can now run external programs on Linux and OSX if you decide to install an additional executable. Just run `:installnative` to get going, and then Ctrl-i `<C-i>` in a text box to open your editor.
|
||||
|
||||
|
@ -44,7 +44,7 @@ REPLACE_ME_WITH_THE_CHANGE_LOG_USING_SED
|
|||
|
||||
- You can only navigate to most about:*\file:* pages if you have Tridactyl's native executable installed.
|
||||
- Firefox will not load Tridactyl on addons.mozilla.org, about:\*, some file:\* URIs, view-source:\*, or data:\*. On these pages Ctrl-L (or F6), Ctrl-Tab and Ctrl-W are your escape hatches.
|
||||
- Tridactyl does not currently support changing/hiding the Firefox GUI, but you can do it yourself by changing your userChrome. There is an example file available on our repository [[2]].
|
||||
- You can change the Firefox GUI with `guiset` (e.g. `guiset gui none`) if you have the native messenger installed, or you can do it yourself by changing your userChrome. There is an example file available on our repository [[2]].
|
||||
- Tridactyl cannot capture key presses until web pages are loaded. You can use `:reloadall` to reload all tabs to make life more bearable, or flip `browser.sessionstore.restore_tabs_lazily` to false in `about:config`.
|
||||
|
||||
## Why do I see this here?
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<html lang="en" style="display: none;">
|
||||
<head>
|
||||
<script src="newtab.js"></script>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" href="newtab.css">
|
||||
<link rel="stylesheet" href="content.css">
|
||||
|
|
|
@ -889,7 +889,7 @@ body {
|
|||
}
|
||||
|
||||
a {
|
||||
color: green;
|
||||
color: #05a805;
|
||||
}
|
||||
|
||||
code {
|
||||
|
|
4
src/tridactyl.d.ts
vendored
4
src/tridactyl.d.ts
vendored
|
@ -50,3 +50,7 @@ declare function html(
|
|||
strings: TemplateStringsArray,
|
||||
...values: any[]
|
||||
): HTMLElement
|
||||
|
||||
declare namespace browser.webRequest {
|
||||
function filterResponseData(requestId: string): any
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue