~tychi/code-mirror-shield

9267ca202d98d4366b178378ab37878e86630c1d — Tyler Childs 7 months ago 083f5df
feature: add autosave backup functionality
4 files changed, 153 insertions(+), 67 deletions(-)

M client.lock.json
M public/editor.bundle.js
M public/editor.js
M server.js
M client.lock.json => client.lock.json +0 -1
@@ 77,7 77,6 @@
  "https://esm.sh/@codemirror/lang-css": "f3b71966aa75132128e8a0c415e59bd96620d697b1c5b51619c24945418ce1d7",
  "https://esm.sh/@codemirror/lang-html": "8d9a06fed93624841b39e58eb7e7fe9f51b24247eda5e156ac1ef9ae5f842793",
  "https://esm.sh/@codemirror/lang-javascript": "714f46688fed08019202f2dc4c06049ce021467264e20e87f8df270d6e6ce993",
  "https://esm.sh/@codemirror/text": "b2dc393b8c489623905242b37a5304fea11cd6dfd27e65385ee53e7f0984c7cb",
  "https://esm.sh/diffhtml": "f1b48c5bdbb132c3e3be42df7ada0d964b814756dba4f9655e1ba5d00ee0d683",
  "https://esm.sh/fast-equals@2.0.4": "92ecadf7e117c0d565df9fa66283217294cc8c9f80ee1a9c38d1f14f2e1f0104",
  "https://thelanding.page/tag/tag.js": "4ce4546980b23770c215bbd79923634f253ddf8914bd111beb0a6f43141ce464",

M public/editor.bundle.js => public/editor.bundle.js +31 -12
@@ 17866,11 17866,14 @@ import('https://esm.sh/fast-equals@2.0.4').then(({ deepEqual  })=>equal = deepEq
);
const editors = {
};
const autosave = upload.bind(null, 'autosave');
const save1 = upload.bind(null, 'save');
function createEditor(selector, flags = {
}) {
    const $ = tag(selector);
    mount1($, flags);
    autosave($, {
    onSave($, flags);
    onAutosave($, {
        every: 5
    });
}


@@ 17907,23 17910,39 @@ function mount1($, flags) {
        });
    });
}
function autosave($, { every  }) {
    setInterval(()=>each($, save)
function onAutosave($, { every  }) {
    setInterval(()=>each($, (target)=>{
            autosave(target.id, $);
        })
    , every * 1000);
    function save(target) {
        const currentState = $.read();
        const copy = currentState[target.id];
        console.log({
            copy
}
function onSave($, _flags) {
    $.on('click', '[data-save]', (event)=>{
        save1(event.target.id, $);
    });
}
async function upload(mode, pathname, $) {
    const currentState = $.read();
    const { value  } = currentState[pathname] || {
    };
    if (value) {
        const response = await fetch(pathname, {
            method: 'POST',
            headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({
                mode,
                value
            })
        });
        console.log(response);
    }
}
function persist(target, $, flags) {
function persist(target, $, _flags) {
    return (transaction)=>{
        if (transaction.changes.inserted.length < 0) return;
        console.log({
            transaction
        });
        const { id  } = target;
        const { view  } = editors[id];
        const value = view.state.doc.toString();

M public/editor.js => public/editor.js +37 -12
@@ 3,7 3,6 @@ import {
  EditorView,
  basicSetup
} from "https://esm.sh/@codemirror/basic-setup"
import Text from "https://esm.sh/@codemirror/text"
import {
  css
} from "https://esm.sh/@codemirror/lang-css"


@@ 18,11 17,15 @@ import tag

const editors = {}

const autosave = upload.bind(null, 'autosave')
const save = upload.bind(null, 'save')

export default function createEditor(selector, flags = {}) {
  const $ = tag(selector)

  mount($, flags)
  autosave($, { every: 5 })
  onSave($, flags)
  onAutosave($, { every: 5 })
}

const config = {


@@ 64,22 67,44 @@ function mount($, flags) {
  })
}

function autosave($, { every }) {
  setInterval(() => each($, save), every * 1000)
function onAutosave($, { every }) {
  setInterval(() => each($, (target) => {
		autosave(target.id, $)
	}), every * 1000)
}

function onSave($, _flags) {
	$.on('click', '[data-save]', (event) => {
		save(event.target.id, $)
	})
}

  function save(target) {
		const currentState = $.read()
		const copy = currentState[target.id]
async function upload(mode, pathname, $) {
	const currentState = $.read()
	const { value } = currentState[pathname] || {}

    // persist to some back up location
		console.log({ copy })
  }
	if(value) {
		// persist to some back up location
		const response = await fetch(pathname, {
			method: 'POST',
			headers: {
				'Accept': 'application/json',
				'Content-Type': 'application/json'
			},
			body: JSON.stringify({
				mode,
				value
			})
		});
		
		console.log(response)
	}
}
function persist(target, $, flags) {

function persist(target, $, _flags) {
	return (transaction) => {
		if(transaction.changes.inserted.length < 0) return

		console.log({ transaction })
		const { id } = target
		const { view } = editors[id]
		const value = view.state.doc.toString()

M server.js => server.js +85 -42
@@ 1,54 1,97 @@
import { serve } from "https://deno.land/std@0.114.0/http/server.ts";

const methods = {
	'GET': handleGet,
	'POST': handlePost,
}

const modes = {
	'autosave': autosave,
	'save': save,
}

async function autosave(pathname, params) {
	const { value } = params
console.log('why')
	await Deno.writeTextFile(`./public${pathname}.autosave`, value)
	return editor(request)
}

async function save(pathname, params) {
	const { value } = params
	await Deno.writeTextFile(`./public${pathname}`, value)
	return editor(request)
}

async function handleRequest(request) {
  const { pathname } = new URL(request.url);

  if (pathname.startsWith("/public")) {
    const file = await Deno.readFile(`.${pathname}`)
		const extension = pathname.split('.').slice(-1)
    return new Response(file, {
      headers: {
        "content-type": getType(extension),
      },
    })
  }

  return new Response(`
	return await (methods[request.method] || methods['POST'])(request)
}

async function handlePost(request) {
	const { pathname } = new URL(request.url);
	const params = await request.json()

	return (modes[params.mode] ||	function(){})(pathname, params)
}

async function handleGet(request) {
	const { pathname } = new URL(request.url);

	const isAutosave = pathname.split('.').slice(-1) === 'autosave'
	const extensionPosition = isAutosave ? -2 : -1

	if (pathname.startsWith('/public')) {
		const file = await Deno.readFile(`.${pathname}`)
		const extension = pathname.split('.').slice(extensionPosition)
		return new Response(file, {
			headers: {
				'content-type': getType(extension),
			},
		})
	}

	return editor(request)
}

function editor(request) {
	const { pathname } = new URL(request.url);

	return new Response(`
			<!doctype html>
      <html lang="en">
        <head>
          <meta charset="utf-8">
          <title>
            ${pathname}
          </title>
        </head>
        <body>
          <main
            class="source-code"
            id="${pathname}"
          ></main>
          <script type="module">
            import createEditor
              from '/public/editor.bundle.js'

            console.log({ createEditor })

            createEditor('.source-code')
          </script>
        </body>
      </html>
			<html lang="en">
				<head>
					<meta charset="utf-8">
					<title>
						${pathname}
					</title>
				</head>
				<body>
					<main
						class="source-code"
						id="${pathname}"
					></main>
					<script type="module">
						import createEditor
							from '/public/editor.bundle.js'

						console.log({ createEditor })

						createEditor('.source-code')
					</script>
				</body>
			</html>
		`,
    {
      headers: {
        "content-type": getType('html'),
      },
    },
  )
		{
			headers: {
				"content-type": getType('html'),
			},
		},
	)
}

const types = {
	'css': 'text/css; charset=utf-8',
  'html': 'text/html; charset=utf-8',
	'html': 'text/html; charset=utf-8',
	'js': 'text/javascript; charset=utf-8'
}