embed-files: draw the tab dir files on all pages

This commit is contained in:
Omar Rizwan 2021-07-02 02:59:29 -07:00
parent 1dd5136415
commit 08d2b5d1d4
3 changed files with 147 additions and 125 deletions

View file

@ -166,6 +166,52 @@ const routeWithContents = (function() {
return routeWithContents;
})();
// Returns a 'writable directory' object; its routeForRoot and
// routeForFilename properties can be wired up as routes to create a
// generally writable folder.
function createWritableDirectory() {
const dir = {}; // Map<Path, {content: String, x, y}>
return {
directory: dir,
routeForRoot: {
usage: 'ls $0',
async readdir({path}) {
// filter out keys not from this path prefix,
// get just last component of keys (filename)
return { entries: [".", "..",
...Object.keys(dir)
.filter(key => key.startsWith(path))
.map(key => key.substr(key.lastIndexOf("/") + 1))] };
},
getattr() {
return {
st_mode: unix.S_IFDIR | 0777, // writable so you can create/rm evals
st_nlink: 3,
st_size: 0,
};
},
},
routeForFilename: {
usage: ['echo "2 + 2" > $0',
'cat $0.result'],
async mknod({path, mode}) {
dir[path] = '';
return {};
},
async unlink({path}) {
delete dir[path];
return {};
},
...routeWithContents(
async ({path}) => dir[path],
async ({path}, buf) => { dir[path] = buf; }
)
}
};
}
function routeDirectoryForChildren(path) {
function depth(p) { return p === '/' ? 0 : (p.match(/\//g) || []).length; }
@ -234,6 +280,8 @@ Routes["/tabs/by-id"] = {
const tabIdDirectory = createWritableDirectory();
Routes["/tabs/by-id/#TAB_ID"] = routeDefer(() => {
// we deferred construction of this route so that we have all the
// children to enumerate by now.
const childrenRoute = routeDirectoryForChildren("/tabs/by-id/#TAB_ID");
return {
...tabIdDirectory.routeForRoot, // so getattr is inherited
@ -250,17 +298,7 @@ Routes["/tabs/by-id/#TAB_ID"] = routeDefer(() => {
}
};
});
Routes["/tabs/by-id/#TAB_ID/:FILENAME"] = {
...tabIdDirectory.routeForFilename,
async mknod(req) {
const ret = tabIdDirectory.routeForFilename.mknod(req);
// TODO: put icon on page
return ret;
}
};
// TODO: can I trigger 1. nav to Finder and 2. nav to Terminal from toolbar click?
// TODO: how can i let them drag files in?
Routes["/tabs/by-id/#TAB_ID/:FILENAME"] = tabIdDirectory.routeForFilename;
(function() {
const routeForTab = (readHandler, writeHandler) => routeWithContents(async ({tabId}) => {
@ -305,49 +343,6 @@ Routes["/tabs/by-id/#TAB_ID/:FILENAME"] = {
)
};
})();
function createWritableDirectory() {
const dir = {};
return {
directory: dir,
routeForRoot: {
usage: 'ls $0',
async readdir({path}) {
// get just last component of keys (filename)
return { entries: [".", "..",
...Object.keys(dir).map(
key => key.substr(key.lastIndexOf("/") + 1)
)] };
},
getattr() {
return {
st_mode: unix.S_IFDIR | 0777, // writable so you can create/rm evals
st_nlink: 3,
st_size: 0,
};
},
},
routeForFilename: {
usage: ['echo "2 + 2" > $0',
'cat $0.result'],
async mknod({path, mode}) {
dir[path] = '';
return {};
},
async unlink({path}) {
delete dir[path];
return {};
},
...routeWithContents(
async ({path}) => dir[path],
async ({path}, buf) => { dir[path] = buf; }
)
}
};
}
(function() {
const evals = createWritableDirectory();
Routes["/tabs/by-id/#TAB_ID/evals"] = evals.routeForRoot;

View file

@ -1,6 +1,27 @@
// Content script that injects a file manager into tabs.
if (chrome.extension.getBackgroundPage) {
// When running in background script:
// 'Server' that manages the files for all tabs.
document.body.insertAdjacentHTML('beforeend', `
// TODO: can I trigger 1. nav to Finder and 2. nav to Terminal from toolbar click?
// accept requests from the page
browser.runtime.onMessage.addListener(async (request, sender) => {
const {entries} = await Routes["/tabs/by-id/#TAB_ID"].readdir({path: `/tabs/by-id/${sender.tab.id}`});
return entries;
});
// send the file list to the page
// receive events from the page of
// they dragged a new file in,
// or they moved a file,
// or they double-clicked a file
} else {
// When running in page:
// Content script that injects a file manager into the page.
document.body.insertAdjacentHTML('beforeend', `
<style>
.--tabfs-file-container .--tabfs-file {
/* fixed makes sense if it's a property of the tab; */
@ -17,84 +38,90 @@ document.body.insertAdjacentHTML('beforeend', `
<div class="--tabfs-file-container">
</div>
`);
const container = document.getElementsByClassName('--tabfs-file-container')[0];
const container = document.getElementsByClassName('--tabfs-file-container')[0];
const icons = {};
const icons = {};
let frontierX = 0, frontierY = 0;
function addFile(name, x, y, file) {
if (!x) {
x = frontierX; frontierX += 64;
y = 0;
}
let frontierX = 0, frontierY = 0;
const addFile = function(name, x, y, file) {
// TODO: report into the extension
container.insertAdjacentHTML('beforeend', `
if (!x) {
x = frontierX; frontierX += 64;
y = 0;
}
container.insertAdjacentHTML('beforeend', `
<div class="--tabfs-file">${name}</div>
`);
const icon = container.lastElementChild;
icon.style.left = `${x}px`; icon.style.top = `${y}px`;
const icon = container.lastElementChild;
icon.style.left = `${x}px`; icon.style.top = `${y}px`;
// from https://htmldom.dev/make-a-draggable-element/:
// from https://htmldom.dev/make-a-draggable-element/:
// The current position of mouse
let mouseX = 0;
let mouseY = 0;
// The current position of mouse
let mouseX = 0;
let mouseY = 0;
// Handle the mousedown event
// that's triggered when user drags the element
const mouseDownHandler = function(e) {
// Get the current mouse position
mouseX = e.clientX;
mouseY = e.clientY;
// Attach the listeners to `document`
document.addEventListener('mousemove', mouseMoveHandler);
document.addEventListener('mouseup', mouseUpHandler);
// Handle the mousedown event
// that's triggered when user drags the element
const mouseDownHandler = function(e) {
// Get the current mouse position
mouseX = e.clientX;
mouseY = e.clientY;
// Attach the listeners to `document`
document.addEventListener('mousemove', mouseMoveHandler);
document.addEventListener('mouseup', mouseUpHandler);
e.preventDefault();
};
const mouseMoveHandler = function(e) {
// How far the mouse has been moved
const dx = e.clientX - mouseX;
const dy = e.clientY - mouseY;
// Set the position of element
icon.style.top = `${icon.offsetTop + dy}px`;
icon.style.left = `${icon.offsetLeft + dx}px`;
// Reassign the position of mouse
mouseX = e.clientX;
mouseY = e.clientY;
e.preventDefault();
};
const mouseUpHandler = function() {
// Remove the handlers of `mousemove` and `mouseup`
document.removeEventListener('mousemove', mouseMoveHandler);
document.removeEventListener('mouseup', mouseUpHandler);
};
icon.addEventListener('mousedown', mouseDownHandler);
};
// ask for what the files are
chrome.runtime.sendMessage({hello: 'hello'}, function(response) {
response.forEach(name => addFile(name));
});
document.body.addEventListener('dragenter', function(e) {
e.preventDefault();
};
const mouseMoveHandler = function(e) {
// How far the mouse has been moved
const dx = e.clientX - mouseX;
const dy = e.clientY - mouseY;
// Set the position of element
icon.style.top = `${icon.offsetTop + dy}px`;
icon.style.left = `${icon.offsetLeft + dx}px`;
// Reassign the position of mouse
mouseX = e.clientX;
mouseY = e.clientY;
});
document.body.addEventListener('dragover', function(e) {
e.preventDefault();
};
});
document.body.addEventListener('dragleave', function(e) {
e.preventDefault();
});
const mouseUpHandler = function() {
// Remove the handlers of `mousemove` and `mouseup`
document.removeEventListener('mousemove', mouseMoveHandler);
document.removeEventListener('mouseup', mouseUpHandler);
};
icon.addEventListener('mousedown', mouseDownHandler);
document.body.addEventListener('drop', function(e) {
// bubble thing
e.preventDefault(); // stops browser nav to that file
for (let file of [...e.dataTransfer.files]) {
addFile(file.name, e.clientX, e.clientY, file);
}
});
}
['a.js', 'b.html', 'c.js'].forEach(name => addFile(name));
document.body.addEventListener('dragenter', function(e) {
e.preventDefault();
});
document.body.addEventListener('dragover', function(e) {
e.preventDefault();
});
document.body.addEventListener('dragleave', function(e) {
e.preventDefault();
});
document.body.addEventListener('drop', function(e) {
// bubble thing
e.preventDefault(); // stops browser nav to that file
for (let file of [...e.dataTransfer.files]) {
addFile(file.name, e.clientX, e.clientY, file);
}
});

View file

@ -14,7 +14,7 @@
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
"background": {
"scripts": ["vendor/browser-polyfill.js", "background.js"],
"scripts": ["vendor/browser-polyfill.js", "background.js", "embed-files.js"],
"persistent": true
},
"content_scripts": [{