diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..ed3a0d3e Binary files /dev/null and b/.DS_Store differ diff --git a/README.md b/README.md index 786f289f..62830660 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ If you've ever used jsfiddle, jsbin, dabblet, liveweave, codepen, cssdeck, cssde Version ------------- -1.1.42 +1.1.50 License ------------- @@ -25,7 +25,7 @@ kodeWeave uses a number of open source projects to work properly: * [Codemirror v6](http://codemirror.net/) - Awesome web-based text editor * [Emmet](http://emmet.io/) - Codemirror Plugin for Zen Coding * [Font Awesome v5](https://fontawesome.com/) - Beautiful icon library and toolkit -* [Pico.css](https://picocss.com/) - Pico.css is a minimal css framework for semantic html +* [daisyUI](https://daisyui.com/) - A Tailwind CSS Component Library (used for the toggle switch) * [Tailwind CSS](https://tailwindcss.com/) - A low-level CSS framework that's entirely utility-first and provides users with low-level CSS classes in PostCSS that can be used to define components and designs independently. * [JSZip](https://stuk.github.io/jszip/) - Package zip files locally in javascript * [FileSaver.js](https://github.com/eligrey/FileSaver.js/) - JSZip comes prebuilt with this. Allows us to save files locally in Javascript diff --git a/demo.json b/demo.json index 2de9beec..7be6e128 100644 --- a/demo.json +++ b/demo.json @@ -1 +1,18 @@ -{"version":"1.1.43","settings":{"theme":false,"fontSize":"16","autoupdate":true,"console":true,"scratchpad":""},"pages":[{"name":"index","title":"An attractive title","description":"The most attractive description ever!","libraries":["https://cdnjs.cloudflare.com/ajax/libs/picocss/1.5.7/pico.classless.min.css","https://michaelsboost.com/TailwindCSSMod/tailwind-mod.min.css","https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css"],"html":"
\n
\n \n\n \n\n \n\n \n\n
\n Made with ❤️ and ☕️ by Michael Schwartz\n
\n
\n
\n\n\n\n
copied to clipboard
","css":"","javascript":"// project json\nlet appJSON = {\n theme: true\n};\n\n// check if user can copy images in js\nif (!window.navigator || !window.navigator.clipboard) {\n assets.innerHTML = 'Clipboard API not supported!';\n}\n\n// toggle switch\ndocument.getElementById('switch').onchange = () => {\n // if switch is checked\n if (document.getElementById('switch').checked) {\n document.querySelector('nav span').style.display = 'none';\n replace.style.display = 'none';\n return false;\n }\n\n // if switch is not checked\n document.querySelector('nav span').style.display = 'block';\n replace.style.display = 'block';\n};\n\n// convert button click\nconvert.onclick = () => {\n // remove lines that contain string if switch is checked\n if (document.getElementById('switch').checked) {\n let str = input.value;\n let lines = str.split('\\n');\n let result = '';\n for (let i in lines) {\n let line = lines[i];\n if (line.indexOf(look.value) > -1) {\n // ignore lines containing the string you're looking for\n } else {\n result += line + \"\\n\";\n }\n }\n input.value = result.trim();\n\n return false;\n }\n\n // remove only words containing the string with whatever word user chooses\n input.value = input.value.toString().split(look.value).join(replace.value);\n\n // copy convertion to clipboard\n navigator.clipboard.writeText(input.value);\n\n // notify user change is copied to clipboard\n notification.style.display = 'block';\n setTimeout(() => {\n notification.style.display = 'none';\n }, 1000);\n};\n\n// toggle theme\npickTheme = val => {\n val = val.toString().toLowerCase();\n const elm = document.querySelector('[data-theme]');\n\n if (val === 'light') {\n elm.setAttribute('data-theme', val);\n icon.textContent = '🌙';\n appJSON.theme = true;\n }\n if (val === 'dark') {\n elm.setAttribute('data-theme', val);\n icon.textContent = '🌞';\n appJSON.theme = false;\n }\n\n // remember theme in localStorage\n localStorage.setItem('JSStringReplacer', JSON.stringify(appJSON));\n};\n\nicon.onclick = () => { (appJSON.theme) ? pickTheme('dark') : pickTheme('light'); };\n\n// check localStorage\nif (localStorage.getItem('JSStringReplacer')) {\n appJSON = JSON.parse(localStorage.getItem('JSStringReplacer'));\n (appJSON.theme) ? pickTheme('light') : pickTheme('dark');\n}"},{"name":"hello","title":"An attractive title","description":"The most attractive description ever!","libraries":["https://cdnjs.cloudflare.com/ajax/libs/picocss/1.5.7/pico.classless.min.css","https://michaelsboost.com/TailwindCSSMod/tailwind-mod.min.css","https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css"],"html":"
\n
\n
\n

my awesome title

\n

my awesome sub title

\n
\n \n

\n Lorem ipsum dolor, sit amet consectetur adipisicing elit. Unde qui voluptas fugit assumenda exercitationem rerum excepturi earum facere dignissimos praesentium ullam quidem ipsam dolores, eveniet alias minus? Laboriosam, amet enim?\n

\n
\n
","css":"","javascript":""},{"name":"notepad","title":"An attractive title","description":"The most attractive description ever!","libraries":["https://cdnjs.cloudflare.com/ajax/libs/picocss/1.5.7/pico.classless.min.css","https://michaelsboost.com/TailwindCSSMod/tailwind-mod.min.css","https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css"],"html":"\n\n
","css":"","javascript":"document.querySelector('html').setAttribute('data-theme', 'light')\n\n// variables\nconst code = `
\n \n \n

\n An Attractive Title\n
\n

\n\n

\n Lorem, ipsum dolor sit amet consectetur adipisicing elit. Ut et fugit odio vitae quisquam accusamus, dolor ipsa voluptatem perspiciatis! Dicta possimus culpa tempora repellendus accusamus, doloribus corporis harum accusantium. \n

\n
`\n\naddtodo = () => {\n output.insertAdjacentHTML(\"afterbegin\", code)\n}"}]} \ No newline at end of file +{ + "version": "1.1.50", + "settings": { + "autoupdate": true, + "console": true, + "fontSize": "16" + }, + "title": "TradingView Widget", + "description": "A sample TradingView Widget", + "meta": "", + "libraries": [ + "https://cdnjs.cloudflare.com/ajax/libs/picocss/2.0.6/pico.min.css" + ], + "markdown": "", + "html": "\n
\n \n
\n", + "css": "", + "javascript": "new TradingView.widget({\n \"width\": \"100%\",\n \"height\": window.innerHeight,\n \"symbol\": \"COINBASE:BTCUSD\",\n \"interval\": \"1\",\n \"timezone\": \"Etc/UTC\",\n \"theme\": \"dark\",\n \"style\": \"1\",\n \"locale\": \"en\",\n \"toolbar_bg\": \"#f1f3f6\",\n \"enable_publishing\": false,\n \"hide_side_toolbar\": false,\n \"allow_symbol_change\": true,\n \"details\": true,\n \"studies\": [\n \"BB@tv-basicstudies\",\n \"Volume@tv-basicstudies\",\n \"VWAP@tv-basicstudies\"\n ],\n \"container_id\": \"tradingview_0b60e\"\n});" +} \ No newline at end of file diff --git a/go/.DS_Store b/go/.DS_Store new file mode 100644 index 00000000..1ade3a32 Binary files /dev/null and b/go/.DS_Store differ diff --git a/go/app.js b/go/app.js index 96cbffe4..322aed03 100644 --- a/go/app.js +++ b/go/app.js @@ -1,8 +1,10 @@ importJS = url => { - let script = document.createElement('script') - script.src = url - script.setAttribute('defer', '') - document.head.appendChild(script) -} -importJS('js/libraries.js') -importJS('bundle.js') \ No newline at end of file + let script = document.createElement("script"); + script.src = url; + script.setAttribute("defer", ""); + document.head.appendChild(script); +}; +importJS("js/libraries.js"); +importJS("libraries/tailwind/tailwind.min.js"); +// setTimeout(() => importJS("script.js"), 100); +setTimeout(() => importJS("bundle.js"), 100); \ No newline at end of file diff --git a/go/bundle.js b/go/bundle.js index 407ff856..158a955b 100644 --- a/go/bundle.js +++ b/go/bundle.js @@ -5,10 +5,6 @@ The data structure for documents. @nonabstract */ class Text { - /** - @internal - */ - constructor() { } /** Get the line description around the given position. */ @@ -29,6 +25,7 @@ Replace a range of the text with the given content. */ replace(from, to, text) { + [from, to] = clip(this, from, to); let parts = []; this.decompose(0, from, parts, 2 /* Open.To */); if (text.length) @@ -46,6 +43,7 @@ Retrieve the text between the given points. */ slice(from, to = this.length) { + [from, to] = clip(this, from, to); let parts = []; this.decompose(from, to, parts, 0); return TextNode.from(parts, to - from); @@ -103,7 +101,8 @@ return new LineCursor(inner); } /** - @internal + Return the document as a string, using newline characters to + separate lines. */ toString() { return this.sliceString(0); } /** @@ -116,6 +115,10 @@ return lines; } /** + @internal + */ + constructor() { } + /** Create a `Text` instance for the given array of lines. */ static of(text) { @@ -141,7 +144,7 @@ for (let i = 0;; i++) { let string = this.text[i], end = offset + string.length; if ((isLine ? line : end) >= target) - return new Line(offset, end, line, string); + return new Line$1(offset, end, line, string); offset = end + 1; line++; } @@ -167,6 +170,7 @@ replace(from, to, text) { if (!(text instanceof TextLeaf)) return super.replace(from, to, text); + [from, to] = clip(this, from, to); let lines = appendText(this.text, appendText(text.text, sliceText(this.text, 0, from)), to); let newLen = this.length + text.length - (to - from); if (lines.length <= 32 /* Tree.Branch */) @@ -174,6 +178,7 @@ return TextNode.from(TextLeaf.split(lines, []), newLen); } sliceString(from, to = this.length, lineSep = "\n") { + [from, to] = clip(this, from, to); let result = ""; for (let pos = 0, i = 0; pos <= to && i < this.text.length; i++) { let line = this.text[i], end = pos + line.length; @@ -242,6 +247,7 @@ } } replace(from, to, text) { + [from, to] = clip(this, from, to); if (text.lines < this.lines) for (let i = 0, pos = 0; i < this.children.length; i++) { let child = this.children[i], end = pos + child.length; @@ -264,6 +270,7 @@ return super.replace(from, to, text); } sliceString(from, to = this.length, lineSep = "\n") { + [from, to] = clip(this, from, to); let result = ""; for (let i = 0, pos = 0; i < this.children.length && pos <= to; i++) { let child = this.children[i], end = pos + child.length; @@ -485,7 +492,11 @@ } next(skip = 0) { let { done, lineBreak, value } = this.inner.next(skip); - if (done) { + if (done && this.afterBreak) { + this.value = ""; + this.afterBreak = false; + } + else if (done) { this.done = true; this.value = ""; } @@ -515,7 +526,7 @@ This type describes a line in the document. It is created on-demand when lines are [queried](https://codemirror.net/6/docs/ref/#state.Text.lineAt). */ - class Line { + class Line$1 { /** @internal */ @@ -547,6 +558,10 @@ */ get length() { return this.to - this.from; } } + function clip(text, from, to) { + from = Math.max(0, Math.min(text.length, from)); + return [from, Math.max(from, Math.min(text.length, to))]; + } // Compressed representation of the Grapheme_Cluster_Break=Extend // information from @@ -1314,12 +1329,12 @@ The anchor of the range—the side that doesn't move when you extend it. */ - get anchor() { return this.flags & 16 /* RangeFlag.Inverted */ ? this.to : this.from; } + get anchor() { return this.flags & 32 /* RangeFlag.Inverted */ ? this.to : this.from; } /** The head of the range, which is moved when the range is [extended](https://codemirror.net/6/docs/ref/#state.SelectionRange.extend). */ - get head() { return this.flags & 16 /* RangeFlag.Inverted */ ? this.from : this.to; } + get head() { return this.flags & 32 /* RangeFlag.Inverted */ ? this.from : this.to; } /** True when `anchor` and `head` are at the same position. */ @@ -1330,14 +1345,14 @@ the character before its position, 1 the character after, and 0 means no association. */ - get assoc() { return this.flags & 4 /* RangeFlag.AssocBefore */ ? -1 : this.flags & 8 /* RangeFlag.AssocAfter */ ? 1 : 0; } + get assoc() { return this.flags & 8 /* RangeFlag.AssocBefore */ ? -1 : this.flags & 16 /* RangeFlag.AssocAfter */ ? 1 : 0; } /** The bidirectional text level associated with this cursor, if any. */ get bidiLevel() { - let level = this.flags & 3 /* RangeFlag.BidiLevelMask */; - return level == 3 ? null : level; + let level = this.flags & 7 /* RangeFlag.BidiLevelMask */; + return level == 7 ? null : level; } /** The goal column (stored vertical offset) associated with a @@ -1346,8 +1361,8 @@ lines of different length. */ get goalColumn() { - let value = this.flags >> 5 /* RangeFlag.GoalColumnOffset */; - return value == 33554431 /* RangeFlag.NoGoalColumn */ ? undefined : value; + let value = this.flags >> 6 /* RangeFlag.GoalColumnOffset */; + return value == 16777215 /* RangeFlag.NoGoalColumn */ ? undefined : value; } /** Map this range through a change, producing a valid range in the @@ -1376,8 +1391,9 @@ /** Compare this range to another range. */ - eq(other) { - return this.anchor == other.anchor && this.head == other.head; + eq(other, includeAssoc = false) { + return this.anchor == other.anchor && this.head == other.head && + (!includeAssoc || !this.empty || this.assoc == other.assoc); } /** Return a JSON-serializable object representing the range. @@ -1427,14 +1443,17 @@ return EditorSelection.create(this.ranges.map(r => r.map(change, assoc)), this.mainIndex); } /** - Compare this selection to another selection. + Compare this selection to another selection. By default, ranges + are compared only by position. When `includeAssoc` is true, + cursor ranges must also have the same + [`assoc`](https://codemirror.net/6/docs/ref/#state.SelectionRange.assoc) value. */ - eq(other) { + eq(other, includeAssoc = false) { if (this.ranges.length != other.ranges.length || this.mainIndex != other.mainIndex) return false; for (let i = 0; i < this.ranges.length; i++) - if (!this.ranges[i].eq(other.ranges[i])) + if (!this.ranges[i].eq(other.ranges[i], includeAssoc)) return false; return true; } @@ -1507,18 +1526,18 @@ safely ignore the optional arguments in most situations. */ static cursor(pos, assoc = 0, bidiLevel, goalColumn) { - return SelectionRange.create(pos, pos, (assoc == 0 ? 0 : assoc < 0 ? 4 /* RangeFlag.AssocBefore */ : 8 /* RangeFlag.AssocAfter */) | - (bidiLevel == null ? 3 : Math.min(2, bidiLevel)) | - ((goalColumn !== null && goalColumn !== void 0 ? goalColumn : 33554431 /* RangeFlag.NoGoalColumn */) << 5 /* RangeFlag.GoalColumnOffset */)); + return SelectionRange.create(pos, pos, (assoc == 0 ? 0 : assoc < 0 ? 8 /* RangeFlag.AssocBefore */ : 16 /* RangeFlag.AssocAfter */) | + (bidiLevel == null ? 7 : Math.min(6, bidiLevel)) | + ((goalColumn !== null && goalColumn !== void 0 ? goalColumn : 16777215 /* RangeFlag.NoGoalColumn */) << 6 /* RangeFlag.GoalColumnOffset */)); } /** Create a selection range. */ static range(anchor, head, goalColumn, bidiLevel) { - let flags = ((goalColumn !== null && goalColumn !== void 0 ? goalColumn : 33554431 /* RangeFlag.NoGoalColumn */) << 5 /* RangeFlag.GoalColumnOffset */) | - (bidiLevel == null ? 3 : Math.min(2, bidiLevel)); - return head < anchor ? SelectionRange.create(head, anchor, 16 /* RangeFlag.Inverted */ | 8 /* RangeFlag.AssocAfter */ | flags) - : SelectionRange.create(anchor, head, (head > anchor ? 4 /* RangeFlag.AssocBefore */ : 0) | flags); + let flags = ((goalColumn !== null && goalColumn !== void 0 ? goalColumn : 16777215 /* RangeFlag.NoGoalColumn */) << 6 /* RangeFlag.GoalColumnOffset */) | + (bidiLevel == null ? 7 : Math.min(6, bidiLevel)); + return head < anchor ? SelectionRange.create(head, anchor, 32 /* RangeFlag.Inverted */ | 16 /* RangeFlag.AssocAfter */ | flags) + : SelectionRange.create(anchor, head, (head > anchor ? 8 /* RangeFlag.AssocBefore */ : 0) | flags); } /** @internal @@ -1555,6 +1574,9 @@ size](https://codemirror.net/6/docs/ref/#state.EditorState^tabSize), [editor attributes](https://codemirror.net/6/docs/ref/#view.EditorView^editorAttributes), and [update listeners](https://codemirror.net/6/docs/ref/#view.EditorView^updateListener). + + Note that `Facet` instances can be used anywhere where + [`FacetReader`](https://codemirror.net/6/docs/ref/#state.FacetReader) is expected. */ class Facet { constructor( @@ -1582,6 +1604,11 @@ this.extensions = typeof enables == "function" ? enables(this) : enables; } /** + Returns a facet reader for this facet, which can be used to + [read](https://codemirror.net/6/docs/ref/#state.EditorState.facet) it but not to define values for it. + */ + get reader() { return this; } + /** Define a new facet. */ static define(config = {}) { @@ -2159,7 +2186,10 @@ is(type) { return this.type == type; } /** Define a new effect type. The type parameter indicates the type - of values that his effect holds. + of values that his effect holds. It should be a type that + doesn't include `undefined`, since that is used in + [mapping](https://codemirror.net/6/docs/ref/#state.StateEffect.map) to indicate that an effect is + removed. */ static define(spec = {}) { return new StateEffectType(spec.map || (v => v)); @@ -2476,9 +2506,9 @@ } return spec == tr ? tr : Transaction.create(state, tr.changes, tr.selection, spec.effects, spec.annotations, spec.scrollIntoView); } - const none$2 = []; + const none$3 = []; function asArray$1(value) { - return value == null ? none$2 : Array.isArray(value) ? value : [value]; + return value == null ? none$3 : Array.isArray(value) ? value : [value]; } /** @@ -2630,7 +2660,8 @@ else { startValues = tr.startState.values.slice(); } - new EditorState(conf, tr.newDoc, tr.newSelection, startValues, (state, slot) => slot.update(state, tr), tr); + let selection = tr.startState.facet(allowMultipleSelections) ? tr.newSelection : tr.newSelection.asSingle(); + new EditorState(conf, tr.newDoc, selection, startValues, (state, slot) => slot.update(state, tr), tr); } /** Create a [transaction spec](https://codemirror.net/6/docs/ref/#state.TransactionSpec) that @@ -3034,7 +3065,7 @@ /** A range associates a value with a range of positions. */ - let Range$1 = class Range { + class Range$1 { constructor( /** The range's start position. @@ -3056,9 +3087,9 @@ @internal */ static create(from, to, value) { - return new Range(from, to, value); + return new Range$1(from, to, value); } - }; + } function cmpRange(a, b) { return a.from - b.from || a.value.startSide - b.value.startSide; } @@ -3302,8 +3333,7 @@ static compare(oldSets, newSets, /** This indicates how the underlying data changed between these - ranges, and is needed to synchronize the iteration. `from` and - `to` are coordinates in the _new_ space, after these changes. + ranges, and is needed to synchronize the iteration. */ textDiff, comparator, /** @@ -3365,7 +3395,9 @@ let curTo = Math.min(cursor.to, to); if (cursor.point) { let active = cursor.activeForPoint(cursor.to); - let openCount = cursor.pointFrom < from ? active.length + 1 : Math.min(active.length, openRanges); + let openCount = cursor.pointFrom < from ? active.length + 1 + : cursor.point.startSide < 0 ? active.length + : Math.min(active.length, openRanges); iterator.point(pos, curTo, cursor.point, active, openCount, cursor.pointRank); openRanges = Math.min(cursor.openEnd(curTo), active.length); } @@ -3392,6 +3424,19 @@ build.add(range.from, range.to, range.value); return build.finish(); } + /** + Join an array of range sets into a single set. + */ + static join(sets) { + if (!sets.length) + return RangeSet.empty; + let result = sets[sets.length - 1]; + for (let i = sets.length - 2; i >= 0; i--) { + for (let layer = sets[i]; layer != RangeSet.empty; layer = layer.nextLayer) + result = new RangeSet(layer.chunkPos, layer.chunk, result, Math.max(layer.maxPoint, result.maxPoint)); + } + return result; + } } /** The empty set of ranges. @@ -3414,6 +3459,18 @@ an array of [`Range`](https://codemirror.net/6/docs/ref/#state.Range) objects. */ class RangeSetBuilder { + finishChunk(newArrays) { + this.chunks.push(new Chunk(this.from, this.to, this.value, this.maxPoint)); + this.chunkPos.push(this.chunkStart); + this.chunkStart = -1; + this.setMaxPoint = Math.max(this.setMaxPoint, this.maxPoint); + this.maxPoint = -1; + if (newArrays) { + this.from = []; + this.to = []; + this.value = []; + } + } /** Create an empty builder. */ @@ -3431,18 +3488,6 @@ this.setMaxPoint = -1; this.nextLayer = null; } - finishChunk(newArrays) { - this.chunks.push(new Chunk(this.from, this.to, this.value, this.maxPoint)); - this.chunkPos.push(this.chunkStart); - this.chunkStart = -1; - this.setMaxPoint = Math.max(this.setMaxPoint, this.maxPoint); - this.maxPoint = -1; - if (newArrays) { - this.from = []; - this.to = []; - this.value = []; - } - } /** Add a range. Ranges should be added in sorted (by `from` and `value.startSide`) order. @@ -3709,7 +3754,8 @@ } addActive(trackOpen) { let i = 0, { value, to, rank } = this.cursor; - while (i < this.activeRank.length && this.activeRank[i] <= rank) + // Organize active marks by rank first, then by size + while (i < this.activeRank.length && (rank - this.activeRank[i] || to - this.activeTo[i]) > 0) i++; insert(this.active, i, value); insert(this.activeTo, i, to); @@ -3802,7 +3848,7 @@ let end = diff < 0 ? a.to + dPos : b.to, clipEnd = Math.min(end, endB); if (a.point || b.point) { if (!(a.point && b.point && (a.point == b.point || a.point.eq(b.point)) && - sameValues(a.activeForPoint(a.to + dPos), b.activeForPoint(b.to)))) + sameValues(a.activeForPoint(a.to), b.activeForPoint(b.to)))) comparator.comparePoint(pos, clipEnd, a.point, b.point); } else { @@ -3947,7 +3993,7 @@ return C + id.toString(36) } - // :: (union, union<[StyleModule], StyleModule>) + // :: (union, union<[StyleModule], StyleModule>, ?{nonce: ?string}) // // Mount the given set of modules in the given DOM root, which ensures // that the CSS rules defined by the module are available in that @@ -3960,33 +4006,36 @@ // modules. If you call this function multiple times for the same root // in a way that changes the order of already mounted modules, the old // order will be changed. - static mount(root, modules) { - (root[SET] || new StyleSet(root)).mount(Array.isArray(modules) ? modules : [modules]); + // + // If a Content Security Policy nonce is provided, it is added to + // the ` - - - ${project.pages[app.activePage].html} - - ${showConsole} - -`; - }; + const newInput = document.createElement("input"); + newInput.type = "text"; + newInput.placeholder = + "https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.15.2/Sortable.min.js"; + newInput.setAttribute("data", "library"); + newInput.className = "w-full p-3 pr-0 rounded-md rounded-r-none bg-gray-800"; + newInput.value = result; + newInput.onkeyup = () => { + // Update the value of the librariesArray at the corresponding index + librariesArray[index] = newInput.value.trim(); + app.updatePreview(autoupdate.checked); + }; - const showConsole = project.settings.console ? `\n` : ''; - const htmlCode = generateHtmlCode(theme, showConsole, consoleStyle); - - previewElm.innerHTML = ''; - const frame = document.createElement('iframe'); - frame.setAttribute('id', 'preview'); - frame.setAttribute('title', project.pages[app.activePage].title); - frame.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-downloads allow-forms allow-modals allow-pointer-lock allow-popups'); - previewElm.appendChild(frame); - const previewFrame = document.getElementById('preview'); - const preview = previewFrame.contentDocument || previewFrame.contentWindow.document; - - preview.open(); - preview.write(htmlCode); - preview.close(); - }, - renderPagePreviews: initPage => { - const elm = document.querySelector('html[data-theme]'); - const theme = project.settings.theme ? 'dark' : 'light'; - elm.setAttribute('data-theme', theme); - const css = project.pages[initPage].css; + const deleteButton = document.createElement("button"); + deleteButton.className = + // "delete-button p-3 bg-red-400 rounded-md rounded-l-none"; + "delete-button p-3 bg-gray-800 rounded-md rounded-l-none"; + deleteButton.innerHTML = ''; + deleteButton.onclick = () => { + // Remove the library from the array by its index + project.libraries.splice(index, 1); + // Re-render the libraries array + app.displayLibrariesArray(); + app.updatePreview(autoupdate.checked); + }; - const generateHtmlCode = theme => { - let libraryTags = ''; - const libraries = project.pages[initPage].libraries; - libraries.forEach(library => { - if (library.endsWith('.js')) { - libraryTags += ` - `; - } else if (library.endsWith('.css')) { - libraryTags += ` - `; - } else { - // Assuming it's a Google font - libraryTags += ` - `; - } - }); - return ` - - - ${project.pages[initPage].title} - - - - ${libraryTags} - - - - ${project.pages[initPage].html} - -`; - }; - const htmlCode = generateHtmlCode(theme); - - const outputPage = (pageid, pageframe) => { - const container = document.getElementById(`${pageid}`); - container.innerHTML = ''; // Clear existing content - const frame = document.createElement('iframe'); - frame.setAttribute('id', pageframe); - frame.setAttribute('title', project.pages[initPage].title); - frame.className = "transform scale-50"; // Apply Tailwind classes for transform - frame.style.width = "200%"; - frame.style.height = "200%"; - frame.style.transformOrigin = "top left"; // Set transform origin to center - frame.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-downloads allow-forms allow-modals allow-pointer-lock allow-popups'); - container.appendChild(frame); - const previewFrame = document.getElementById(`${pageframe}`); - const previewDoc = previewFrame.contentDocument || previewFrame.contentWindow.document; - previewDoc.open(); - previewDoc.write(htmlCode); - previewDoc.close(); + newNav.appendChild(sortButton); + newNav.appendChild(newInput); + newNav.appendChild(deleteButton); + sortLibrariesContainer.appendChild(newNav); }; - outputPage(`previewElm${initPage}`, `preview${initPage}`); - }, - - // share weave - shareWeave: () => { - let cssLibrary = ""; - let jsLibrary = ""; - - const libraries = project.pages[app.activePage].libraries; - - libraries.forEach(library => { - if (library.endsWith('.js')) { - jsLibrary += `${library};`; - } else if (library.endsWith('.css')) { - cssLibrary += `${library};`; - } else { - // Assuming it's a Google font, treat it as CSS - cssLibrary += `${library};`; - } + // Embed each library into a new input field and delete button + librariesArray.forEach((input, index) => { + embedArray(librariesArray[index], index); }); - - let data = { - title: projectTitle.value, - description: description.value, - html: ` - -${htmlEditor.state.doc.toString()}`, - css: `/* Shared from kodeWeave: https://michaelsboost.com/kodeWeave/ */ -${cssEditor.state.doc.toString()}`, - js: `// Shared from kodeWeave: https://michaelsboost.com/kodeWeave/ - -${jsEditor.state.doc.toString()}`, - css_external: cssLibrary, - js_external: jsLibrary, - editors: '000', // Set HTML and JS editors open, CSS editor closed - layout: 'left' // Set the layout to left - }; - - // Remove the trailing semicolon - if (cssLibrary !== "") { - cssLibrary = cssLibrary.slice(0, -1); - } - if (jsLibrary !== "") { - jsLibrary = jsLibrary.slice(0, -1); - } - - let JSONstring = JSON.stringify(data).replace(/"/g, """).replace(/'/g, "&apos"); - - let form = - '
' + - '' + - '' + - '
'; - - // Append click then remove - document.body.innerHTML += form; - document.querySelector('form').submit(); - document.querySelector('form').remove(); - }, - - // export project json - exportProjectFile: () => { - let blob = new Blob([JSON.stringify(project)], { - type: "application/json" - }); - saveAs(blob, `${project.pages[app.activePage].name.toString().toLowerCase().replace(/ /g,"")}-kodeWeave.json`); - }, - - // export zip file - exportZip: () => { - const theme = project.settings.theme ? 'dark' : 'light'; - - let zip = new JSZip(); - let readmeCode = `${project.pages[0].title} -=================== - -${project.pages[0].description} - -Version -------------- - -0.0.1 - -License -------------- - -MIT - -This app was created and exported with [kodeWeave](https://michaelsboost.github.io/kodeWeave/)`; - - let licenseStr = `The MIT License (MIT) -Copyright (c) ${new Date().getFullYear()} John Doe - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE.`; + // Initialize SortableJS if it hasn't been initialized yet + if (!sortable) { + sortable = new Sortable(sortLibrariesContainer, { + handle: '[data-sort]', // Selector for the handle element + animation: 150, // Animation duration in milliseconds + onEnd: (event) => { + // Update the libraries array after sorting + const startIndex = event.oldIndex; + const endIndex = event.newIndex; + const movedLibrary = librariesArray.splice(startIndex, 1)[0]; + librariesArray.splice(endIndex, 0, movedLibrary); + app.updatePreview(autoupdate.checked); + app.createPageButtonList(); + } + }); + } - zip.file(`README.md`, readmeCode); - zip.file(`LICENSE.md`, licenseStr); - - project.pages.forEach(page => { - const css = page.css ? `` : ''; + // Check if the last input field is empty, and append an additional empty input field if needed + if ( + librariesArray.length === 0 || + librariesArray[librariesArray.length - 1].trim() !== "" + ) { + embedArray("", librariesArray.length); + } + }, - let libraryTags = ''; - page.libraries.forEach(library => { - if (library.endsWith('.js')) { - libraryTags += `\n`; - } else if (library.endsWith('.css')) { - libraryTags += `\n`; - } else { - // Assuming it's a Google font - libraryTags += `\n`; - } - }); - - zip.file(`${page.name}/js/${page.name}.js`, `${page.javascript}`); - zip.file(`${page.name}/css/${page.name}.css`, `${page.css}`); - zip.file(`${page.name}/${page.name}.html`, ` - - - ${page.title} - - - - - - - - - - - - - - - - - - ${libraryTags} - ${css} - - - ${page.html} - - - -`); + // Ajax function to download over http + getFile: (url, callback) => { + var xhr = new XMLHttpRequest(); + xhr.open("GET", url); + xhr.send(); - // Add libraries for the current page - /* - // Fetch and add external libraries - for (const library of page.libraries) { - const fileType = library.endsWith('.js') ? 'js' : 'css'; - app.getFile(library, content => { - zip.folder(`${page.name}/${fileType}_external`).file(library.split('/').pop(), content); - }); + xhr.onreadystatechange = data => { + if (xhr.readyState !== 4) { + return; } - */ - }); - // save kodeWeave project file in export - zip.file('project-kodeWeave.json', JSON.stringify(project)); - - let content = zip.generate({type:"blob"}); - saveAs(content, 'project.zip'); + if (xhr.status === 200) { + callback(xhr.responseText); + } else { + console.warn("request_error"); + } + }; }, - - // zooming and panning function + + // zooming and panning function initZoomPan: () => { // variables - document.querySelector('[data-device]'); - let canvas = document.querySelector('[data-canvas]'); - let canvasH = parseFloat(canvas.clientHeight); - let canvasW = parseFloat(canvas.clientWidth / 2); + let canvas = document.getElementById('previewElm'); + parseFloat(canvas.clientHeight); + parseFloat(canvas.clientWidth / 2); // init panzoom let instance = panzoom(canvas, { @@ -152297,7 +156889,7 @@ SOFTWARE.`; }); let centerCanvas = () => { - let canvas = document.querySelector('[data-canvas]'); + let canvas = document.getElementById('previewElm'); let canvasH = parseFloat(canvas.clientHeight); let canvasW = parseFloat(canvas.clientWidth / 2); canvasW = parseFloat(canvas.clientWidth); @@ -152362,10 +156954,6 @@ SOFTWARE.`; zoomRatio // initial zoom ); instance.moveTo(initialXPos, initialYPos); - - // display size - viewx.value = parseInt(canvas.style.width); - viewy.value = parseInt(canvas.style.height); }; centerCanvas(); @@ -152375,43 +156963,28 @@ SOFTWARE.`; if (zoomIcon.getAttribute('data-zoom') === 'true') { canvas.selection = false; instance.pause(); - zoomIcon.innerHTML = ''; + zoomIcon.innerHTML = ''; zoomIcon.setAttribute('data-zoom', false); fill.classList.add('hidden'); } else { canvas.selection = true; instance.resume(); - zoomIcon.innerHTML = ''; + zoomIcon.innerHTML = ''; zoomIcon.setAttribute('data-zoom', true); fill.classList.remove('hidden'); } }; - // rotate canvas - let rotateview = () => { - canvasW = parseFloat(canvas.clientWidth); - canvasH = parseFloat(canvas.clientHeight); - - canvas.style.width = canvasH + 'px'; - canvas.style.height = canvasW + 'px'; - centerCanvas(); - }; + // display size + let displaySize = () => { + viewx.value = parseInt(canvas.style.width); + viewy.value = parseInt(canvas.style.height); + }; // reset canvas dimentions and center it let resetCanvas = (w, h) => { - canvasW = w; - canvasH = h; - - if (canvasW > canvasH) { - // landscape - canvas.style.width = canvasW + 'px'; - canvas.style.height = canvasH + 'px'; - centerCanvas(); - return false - } - - canvas.style.width = canvasH + 'px'; - canvas.style.height = canvasW + 'px'; + canvas.style.width = w + 'px'; + canvas.style.height = h + 'px'; centerCanvas(); }; @@ -152419,26 +156992,29 @@ SOFTWARE.`; // dimensions of Galaxy S8+ used mobilep.onclick = () => { resetCanvas(360, 740); - rotateview(); + displaySize(); }; mobilel.onclick = () => { - resetCanvas(360, 740); + resetCanvas(740, 360); + displaySize(); }; // reset canvas dimensions and center it - // dimensions of iPad Mini used + // dimensions of iPad Pro used tabletp.onclick = () => { - resetCanvas(1024, 768); - rotateview(); + resetCanvas(1024, 1366); + displaySize(); }; tabletl.onclick = () => { - resetCanvas(1024, 768); + resetCanvas(1366, 1024); + displaySize(); }; // reset canvas dimensions and center it - // 2012 macbook pro dimensions used + // 2015 15-inch retina macbook pro dimensions used desktopsize.onclick = () => { - resetCanvas(1440, 834); + resetCanvas(1920, 1200); + displaySize(); }; // manually reset canvas dimensions and center it @@ -152449,317 +157025,336 @@ SOFTWARE.`; resetCanvas(viewx.value, viewy.value); }; }, - - // Function to handle storage and display of library/framework - fetchSuggestions: searchText => { - fetch(`https://api.cdnjs.com/libraries?search=${searchText}&fields=filename,description,version`) - .then(response => { - if (!response.ok) { - throw new Error('Network response was not ok'); - } - return response.json(); - }) - .then(data => { - if (data && data.results && data.results.length > 0) { - const libraries = data.results.map(result => result); - app.displaySuggestions(libraries); - } - }) - .catch(error => { - console.error('Error fetching data:', error); - }); - }, - displaySuggestions: suggestions => { - const suggestionsList = document.getElementById('suggestions'); - suggestionsList.innerHTML = ''; // Clear previous suggestions - suggestions.forEach(result => { - const listItem = document.createElement('li'); - listItem.className = 'list-none'; - listItem.innerHTML = `
- ${result.name} - ${result.version} -
-
${result.description}

`; - listItem.onclick = () => { - // Add the clicked suggestion to the libraries array - const url = result.latest; // Assuming 'latest' holds the URL - project.pages[app.activePage].libraries.push(url); - // Clear the suggestions list - suggestionsList.innerHTML = ''; - // Display the libraries display - app.displayLibrariesArray(); - app.updatePreview(autoupdate.checked); - app.createPageButtonList(); - }; - suggestionsList.appendChild(listItem); + // toggle side nav + toggleSideNav: e => { + const hideLists = () => { + document.querySelectorAll('#sidenav ul').forEach(list => { + list.classList.add('hidden'); + }); + }; + + const openSideNav = targetId => { + hideLists(); + document.getElementById(targetId).classList.remove('hidden'); + }; + + // Only shows main side navigation + document.querySelectorAll(`[data-openSide=${e}]`).forEach(btn => { + btn.onclick = () => { + openSideNav(e); + }; + }); + + // Open sub menu in side navigation + document.querySelectorAll(`[data-closeSide=${e}]`).forEach(btn => { + btn.onclick = () => { + hideLists(); + mainsidenav.classList.remove('hidden'); // Assuming mainsidenav is defined somewhere + }; }); + }, + + // load and save settings + loadSettings: () => { + projectTitle.value = project.title; + projectDesc.value = project.description; + projectMeta.value = project.meta; + fz.value = project.settings.fontSize; + toggleconsole.checked = (project.settings.console) ? true : false; }, - displayLibrariesArray: () => { - const librariesArray = project.pages[app.activePage].libraries; - let sortLibrariesContainer = document.getElementById('sortLibraries'); - sortLibrariesContainer.innerHTML = ''; - const embedArray = (result, index) => { - const newNav = document.createElement('nav'); - newNav.setAttribute('data-index', index); - - const sortButton = document.createElement('button'); - sortButton.className = 'w-auto border-0 bg-transparent text-current py-2'; - sortButton.innerHTML = ''; - sortButton.setAttribute('data-sort', index); - - const newInput = document.createElement('input'); - newInput.type = 'text'; - newInput.placeholder = 'https://website.com/index.css/.js'; - newInput.setAttribute('data', 'library'); - newInput.className = 'rounded-r-none py-2'; - newInput.value = result; - newInput.onkeyup = () => { - // Update the value of the librariesArray at the corresponding index - librariesArray[index] = newInput.value.trim(); - app.updatePreview(autoupdate.checked); - app.createPageButtonList(); + saveSettings: () => { + const handleKeyup = (element, property) => { + element.onkeyup = () => { + project[property] = element.value; + localStorage.setItem('kodeWeave', JSON.stringify(project)); }; + }; - const deleteButton = document.createElement('button'); - deleteButton.className = 'delete-button w-auto border-0 bg-red-400 rounded-l-none py-2'; - deleteButton.innerHTML = ''; - deleteButton.onclick = () => { - // Remove the library from the array by its index - project.pages[app.activePage].libraries.splice(index, 1); - // Re-render the libraries array - app.createPageButtonList(); - app.displayLibrariesArray(); - app.updatePreview(autoupdate.checked); - }; + handleKeyup(projectTitle, "title"); + handleKeyup(projectDesc, "description"); + handleKeyup(projectMeta, "meta"); + + // Update project values immediately + project.title = projectTitle.value; + project.description = projectDesc.value; + project.meta = projectMeta.value; + fz.onkeyup = () => { + project.settings.fontSize = fz.value; + localStorage.setItem('kodeWeave', JSON.stringify(project)); + }; + }, + + // Function to update previews + updatePreview: (runManually = false) => { + app.updateStorage(); + const generateHtmlCode = () => { + const tailwindStyle = + ".wrapper_yOR7u {left: 0!important; width: 100%!important; border-radius: 15px 15px 0 0!important; z-index: 99999999;} .btn_yOR7u { cursor: pointer; background: inherit; padding: 0 0.5rem; margin: inherit; margin-right: 0px; border: inherit; color: #fff!important; } .nav_yOR7u {padding-bottom: 14px!important;} .line_yOR7u {background: inherit!important;}"; + const consoleStyle = ``; + const addConsoleCSS = project.settings.console ? consoleStyle : ""; + const showConsole = project.settings.console + ? `` + : ""; - newNav.appendChild(sortButton); - newNav.appendChild(newInput); - newNav.appendChild(deleteButton); - sortLibrariesContainer.appendChild(newNav); + let libraryTags = ''; + const libraries = project.libraries; + libraries.forEach(library => { + if (library.endsWith('.js')) { + libraryTags += ` + `; + } else if (library.endsWith('.css')) { + libraryTags += ` + `; + } else { + // Assuming it's a Google font + libraryTags += ` + `; + } + }); + + // render html + return ` + + + ${project.title} + + + + ${libraryTags} + ${addConsoleCSS} + ${showConsole} + + + + ${project.html} + + + +`; }; - // Embed each library into a new input field and delete button - librariesArray.forEach((input, index) => { - embedArray(librariesArray[index], index); - }); - - // Initialize SortableJS if it hasn't been initialized yet - if (!sortable) { - sortable = new Sortable(sortLibrariesContainer, { - handle: '[data-sort]', // Selector for the handle element - animation: 150, // Animation duration in milliseconds - onEnd: (event) => { - // Update the libraries array after sorting - const startIndex = event.oldIndex; - const endIndex = event.newIndex; - const movedLibrary = librariesArray.splice(startIndex, 1)[0]; - librariesArray.splice(endIndex, 0, movedLibrary); - app.updatePreview(autoupdate.checked); - app.createPageButtonList(); - } - }); - } + // Clear existing content in the preview element + previewElm.innerHTML = ``; + + // Get the content document of the iframe + const previewFrame = document.getElementById("preview"); + const previewDoc = + previewFrame.contentDocument || previewFrame.contentWindow.document; + + // Open, write HTML code, and close the content document + previewDoc.open(); + previewDoc.write(generateHtmlCode()); + previewDoc.close(); + }, - // Check if the last input field is empty, and append an additional empty input field if needed - if (librariesArray.length === 0 || librariesArray[librariesArray.length - 1].trim() !== '') { - embedArray('', librariesArray.length); - } - app.updatePreview(autoupdate.checked); - app.createPageButtonList(); + // Update localStorage + updateStorage: () => { + project.settings.autoupdate = autoupdate.checked; + project.settings.console = toggleconsole.checked; + project.settings.fontSize = fz.value; + + project.markdown = mdEditor.state.doc.toString(); + project.html = htmlEditor.state.doc.toString(); + project.css = cssEditor.state.doc.toString(); + project.javascript = jsEditor.state.doc.toString(); + + localStorage.setItem('kodeWeave', JSON.stringify(project)); }, - // initalize application function - init: () => { - if (!localStorage.getItem('kodeWeave')) { - // project json - project = { - "version": "1.1.43", - "settings": { - "theme": false, - "fontSize": 16, - "autoupdate": true, - "console": true, - "scratchpad": "", - }, - "pages": [{ - "name": "index", - "title": "An attractive title", - "description": "The most attractive description ever!", - "libraries": ['https://cdnjs.cloudflare.com/ajax/libs/picocss/1.5.7/pico.classless.min.css', 'https://michaelsboost.com/TailwindCSSMod/tailwind-mod.min.css', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css', 'https://michaelsboost.github.io/kodeWeave/go/libraries/tailwind/tailwind.min.js'], - "html": "", - "css": "", - "javascript": "" - }] - }; - } else { - project = JSON.parse(localStorage.getItem('kodeWeave')); - - // Make first page as active page - app.activePage = 0; - - // Update settings - autoupdate.checked = project.settings.autoupdate; - const runParent = run.parentElement; - autoupdate.checked ? runParent.classList.add('hidden') : runParent.classList.remove('hidden'); - toggleconsole.checked = project.settings.console; - fz.value = project.settings.fontSize; - theme.checked = project.settings.theme; - scratchpad.value = project.settings.scratchpad; - let cmEditor = document.querySelectorAll('.cm-editor'); - cmEditor.forEach((child) => { - child.style.fontSize = `${fz.value}px`; - }); - - // Load code into appropriate editors - htmlEditor.dispatch({ - changes: { - from: 0, - to: htmlEditor.state.doc.toString().length, - insert: project.pages[0].html, - }, - }); - cssEditor.dispatch({ - changes: { - from: 0, - to: cssEditor.state.doc.toString().length, - insert: project.pages[0].css, - }, - }); - jsEditor.dispatch({ + newProject: () => { + projectTitle.value = ""; + projectDesc.value = ""; + projectMeta.value = ""; + project.libraries = []; + app.displayLibrariesArray(); + + // Load code into appropriate editors + function clearEditor(editor) { + editor.dispatch({ changes: { from: 0, - to: jsEditor.state.doc.toString().length, - insert: project.pages[0].javascript, + to: editor.state.doc.toString().length, + insert: "", }, }); - - // render previews - app.createPageButtonList(); - app.activatePage(0); - app.displayLibrariesArray(); - app.updatePreview(autoupdate.checked); } - document.getElementById('sortLibraries'); - const searchBox = document.getElementById('searchBox'); - const suggestionsList = document.getElementById('suggestions'); - searchBox.onkeyup = () => { - const searchText = searchBox.value.trim(); - suggestionsList.innerHTML = ""; - if (searchText.length <= 0) { - suggestionsList.innerHTML = ""; - return false; - } else { - app.fetchSuggestions(searchText); - } - }; - addanother.onclick = () => app.displayLibrariesArray(); - logit.onclick = () => console.log(JSON.stringify(project.pages[app.activePage])); - - // init zooming and panning - app.initZoomPan(); - - // display libraries array - app.displayLibrariesArray(); - - // Define an array of objects containing element IDs, corresponding project properties, and event types - const elementProperties = [ - { id: 'autoupdate', property: 'settings.autoupdate', event: 'change' }, - { id: 'toggleconsole', property: 'settings.console', event: 'change' }, - { id: 'fa', property: 'settings.fontawesome', event: 'change' }, - { id: 'fz', property: 'settings.fontSize', events: ['keyup', 'change'] }, - { id: 'theme', property: 'settings.theme', event: 'change' }, - { id: 'css', property: 'settings.css', event: 'change' } - ]; - - // Function to handle saving project data on keyup or change event - const handleProjectDataChange = (element, property) => { - let value; - if (element.type === 'checkbox') { - value = element.checked; + clearEditor(mdEditor); + clearEditor(htmlEditor); + clearEditor(cssEditor); + clearEditor(jsEditor); + + app.updatePreview(autoupdate.checked); + }, + + // export project json + exportProjectFile: () => { + let blob = new Blob([JSON.stringify(project, null, 2)], { + type: "application/json" + }); + saveAs(blob, `${project.title.toString().toLowerCase().replace(/ /g,"")}-kodeWeave.json`); + }, + + // Exports zip file + exportZip: () => { + let zip = new JSZip(); + let licenseStr = `The MIT License (MIT) +Copyright (c) ${new Date().getFullYear()} John Doe + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.`; + + zip.file("README.md", project.markdown); + zip.file("LICENSE.md", licenseStr); + let cssBundle = '/* imports */\n'; + let jsBundleFiles = ""; + project.libraries.forEach(library => { + if (library.endsWith('.js')) { + jsBundleFiles += `kWExportJSFiles.importJS("${library}");\n `; + } else if (library.endsWith('.css')) { + cssBundle += `@import url('${library}');\n`; } else { - value = element.value; - } - // Update the project data - const nestedProperties = property.split('.'); - let obj = project; - for (const prop of nestedProperties.slice(0, -1)) { - obj = obj[prop]; - if (obj === undefined) break; // Stop if any nested property is undefined - } - obj[nestedProperties.slice(-1)[0]] = value; - }; - - // Loop through the array and attach event handlers to the elements - elementProperties.forEach(({ id, property, event, events }) => { - const element = document.getElementById(id); - if (element) { - const handler = () => { - handleProjectDataChange(element, property); - if (element === autoupdate) { - run.parentElement.classList.toggle('hidden', autoupdate.checked); - } - if (element === fz) { - let cmEditor = document.querySelectorAll('.cm-editor'); - cmEditor.forEach((child) => { - child.style.fontSize = `${fz.value}px`; - }); - } - app.updatePreview(autoupdate.checked); - }; - if (events) { - events.forEach(ev => element.addEventListener(ev, handler)); - } else { - element.addEventListener(event, handler); - } + cssBundle += `@import url('${library}');\n`; } }); - - // Define an array of objects containing element IDs, corresponding project properties, and event types - const elementProperties2 = [ - { id: 'projectTitle', property: 'title', events: ['keyup', 'change'] }, - { id: 'description', property: 'description', events: ['keyup', 'change'] }, - { id: 'scratchpad', property: 'scratchpad', events: ['keyup', 'change'] } - ]; - - // Function to handle saving project data - const savePageData = (element, property) => { - // Update the project data - const currentPage = project.pages[app.activePage]; - currentPage[property] = element.value; - app.updatePreview(autoupdate.checked); + + // add project css after libraries have been added in a single css file + cssBundle += project.css; + + zip.file("css/index.css", project.css); + zip.file("css/bundle.css", cssBundle); + zip.file("js/index.js", project.javascript); + zip.file("js/bundle.js", `const kWExportJSFiles = { + importJS: url => { + let script = document.createElement("script"); + script.src = url; + script.setAttribute("defer", ""); + document.head.appendChild(script); + }, + + init: () => { + ${jsBundleFiles} + setTimeout(() => kWExportJSFiles.importJS("js/index.js"), 100); + } +}; + +kWExportJSFiles.init();`); + zip.file("index.html", ` + + + ${project.title} + + + + + + + + + + + + + + + + + + + + ${project.html} + + + +`); + + // finally export the project as zip file + let content = zip.generate({ type: "blob" }); + saveAs(content, `${project.title.toString().toLowerCase().replace(/ /g,"")}-kodeWeave.zip`); + }, + + // share weave + shareWeave: () => { + const data = { + title: project.title, + description: project.description, + html: project.html, + css: project.css, + js: project.javascript, + css_external: project.libraries.filter(lib => lib.endsWith('.css')).join(';'), + js_external: project.libraries.filter(lib => lib.endsWith('.js')).join(';'), + editors: '000', + layout: 'left' }; + + // Stringify the JSON object and escape quotes + const JSONstring = JSON.stringify(data) + .replace(/"/g, """) + .replace(/'/g, "'"); + + // Create form element + const form = ` +
+ + +
`; + + // Append form to the document body and submit + document.body.insertAdjacentHTML('beforeend', form); + document.querySelector('form').submit(); + }, + + // Initiate function + init: () => { + // Place app name and version + document.getElementById("appName").textContent = app.appName; + document.getElementById("appVersion").textContent = app.appVersion; + document.getElementById("appUrl").href = app.appUrl; + document.getElementById("appLicense").href = app.appLicense; + + // load project meta data + app.loadSettings(); - // Loop through the array and attach event listeners to the elements for each event type - elementProperties2.forEach(({ id, property, events }) => { - const element = document.getElementById(id); - if (element) { - events.forEach(event => { - element.addEventListener(event, () => { - savePageData(element, property); - app.updatePreview(autoupdate.checked); - }); - }); - } - }); - - // function to add a page - addPage.onclick = app.addPage; - - // Function to init a new project - initNewProject.onclick = () => { - localStorage.clear(); - location.reload(true); - }; - + // save project meta data + app.saveSettings(); + + // init toggle settings for side nav + app.toggleSideNav('settings'); + + // toggle menu + menu.onchange = () => { + document.querySelector('label[for=menu]').classList.toggle('text-blue-500'); + sidenav.classList.toggle('hidden'); + }; + // Create a function to dynamically create and append the navbar const createNavbar = container => { // Create the main nav element const navbar = document.createElement('nav'); - navbar.className = 'absolute bottom-10 inset-x-0 px-4 text-center inline-block overflow-auto'; + // navbar.className = 'absolute bottom-10 inset-x-0 px-4 text-center inline-block overflow-auto'; const navbar2 = document.createElement('nav'); - navbar2.className = 'absolute bottom-0 inset-x-0 px-4 text-center inline-block overflow-auto'; + // navbar2.className = 'absolute bottom-0 inset-x-0 px-4 text-center inline-block overflow-auto'; // Create the ul element const ul = document.createElement('ul'); @@ -152887,103 +157482,154 @@ SOFTWARE.`; // Function to cut the selected text const cutSelection = editor => { - const { state, dispatch } = editor; - const { selection } = state; - const selectedText = state.sliceDoc(selection.main.from, selection.main.to); - navigator.clipboard.writeText(selectedText); - dispatch(state.update({ - changes: { from: selection.main.from, to: selection.main.to, insert: '' } - })); + const { state, dispatch } = editor; + const { selection } = state; + const selectedText = state.sliceDoc(selection.main.from, selection.main.to); + navigator.clipboard.writeText(selectedText); + dispatch(state.update({ + changes: { from: selection.main.from, to: selection.main.to, insert: '' } + })); }; // Function to copy the selected text const copySelection = editor => { - const { state } = editor; - const { selection } = state; - const selectedText = state.sliceDoc(selection.main.from, selection.main.to); - navigator.clipboard.writeText(selectedText); + const { state } = editor; + const { selection } = state; + const selectedText = state.sliceDoc(selection.main.from, selection.main.to); + navigator.clipboard.writeText(selectedText); }; // Function to paste text at the cursor position const pasteText = async editor => { - const { state, dispatch } = editor; - try { - const text = await navigator.clipboard.readText(); - if (text) { - const { selection } = state; - dispatch(state.update({ changes: { from: selection.main.from, to: selection.main.to, insert: text } })); - } else { - console.log('Clipboard is empty or does not contain text.'); - } - } catch (error) { - console.error('Failed to paste text:', error); + const { state, dispatch } = editor; + try { + const text = await navigator.clipboard.readText(); + if (text) { + const { selection } = state; + dispatch(state.update({ changes: { from: selection.main.from, to: selection.main.to, insert: text } })); + } else { + console.log('Clipboard is empty or does not contain text.'); } + } catch (error) { + console.error('Failed to paste text:', error); + } }; // Function to select all text in the active editor const selectAll = editor => { - const { state, dispatch } = editor; - const { doc } = state; - const selection = { anchor: 0, head: doc.length }; - dispatch(state.update({ selection })); + const { state, dispatch } = editor; + const { doc } = state; + const selection = { anchor: 0, head: doc.length }; + dispatch(state.update({ selection })); }; - - // init tabs - const tabButtons = document.querySelectorAll('[data-toggletab]'); - const tabContent = document.querySelectorAll('[data-tabcontent]'); - tabButtons.forEach(button => { - button.addEventListener('click', function() { - // Check if the clicked button already has the text-blue-500 class - const isAlreadyActive = this.classList.contains('text-blue-500'); - - // Remove text-blue-500 from all buttons - tabButtons.forEach((btn) => btn.classList.remove('text-blue-500')); - - // Hide all tab contents - tabContent.forEach((tab) => tab.classList.add('hidden')); - - if (!isAlreadyActive) { - // If the clicked button wasn't active, add text-blue-500 and show corresponding tab - this.classList.add('text-blue-500'); - const tabName = this.getAttribute('data-toggletab'); - const selectedTab = document.querySelector(`[data-tabcontent="${tabName}"]`); - selectedTab.classList.remove('hidden'); - - // Check if the selected tab is the 'pages' tab - if (tabName === 'pages') { - // push demo to the display tab - app.createPageButtonList(); + + // init tabs + const tabButtons = document.querySelectorAll("[data-toggletab]"); + const tabContent = document.querySelectorAll("[data-tabcontent]"); + + tabButtons.forEach(button => { + button.addEventListener("click", function () { + // Check if the clicked button already has the text-blue-500 class + const isAlreadyActive = this.classList.contains("text-blue-500"); + + // Remove text-blue-500 from all buttons + tabButtons.forEach(btn => btn.classList.remove("text-blue-500", "border-b", "border-blue-500")); + + // Hide all tab contents + tabContent.forEach(tab => tab.classList.add("hidden")); + + if (!isAlreadyActive) { + // If the clicked button wasn't active, add text-blue-500 and show corresponding tab + this.classList.add("text-blue-500", "border-b", "border-blue-500"); + const tabName = this.getAttribute("data-toggletab"); + const selectedTab = document.querySelector( + `[data-tabcontent="${tabName}"]` + ); + selectedTab.classList.remove("hidden"); + + if (tabName === 'markdown') { + document.querySelector('[data-editorHTMLNavbar]').innerHTML = ''; + document.querySelector('[data-editorCSSNavbar]').innerHTML = ''; + document.querySelector('[data-editorJSNavbar]').innerHTML = ''; + activeEditor = mdEditor; + createNavbar("editorMDNavbar"); } - // remember the active editor if (tabName === 'html') { - document.querySelector('[data-editorJSNavbar]').innerHTML = ''; + document.querySelector('[data-editorMDNavbar]').innerHTML = ''; document.querySelector('[data-editorCSSNavbar]').innerHTML = ''; + document.querySelector('[data-editorJSNavbar]').innerHTML = ''; activeEditor = htmlEditor; createNavbar("editorHTMLNavbar"); } if (tabName === 'css') { + document.querySelector('[data-editorMDNavbar]').innerHTML = ''; document.querySelector('[data-editorHTMLNavbar]').innerHTML = ''; document.querySelector('[data-editorJSNavbar]').innerHTML = ''; activeEditor = cssEditor; createNavbar("editorCSSNavbar"); } - if (tabName === 'js') { + if (tabName === 'javascript') { + document.querySelector('[data-editorMDNavbar]').innerHTML = ''; document.querySelector('[data-editorHTMLNavbar]').innerHTML = ''; document.querySelector('[data-editorCSSNavbar]').innerHTML = ''; activeEditor = jsEditor; createNavbar("editorJSNavbar"); } - } else { - // If the clicked button was already active, show the random tab - const randomTab = document.querySelector('[data-tabcontent="menu"]'); - randomTab.classList.remove('hidden'); - } - }); - }); + } else { + // If the clicked button was already active, hide it's content + const tabName = this.getAttribute("data-toggletab"); + const selectedTab = document.querySelector( + `[data-toggletab="${tabName}"]`); + const selectedTabContent = document.querySelector( + `[data-tabcontent="${tabName}"]`); + selectedTab.classList.remove("text-blue-500", "border-b", "border-blue-500"); + selectedTabContent.classList.add("hidden"); + } + }); + }); + + // init zooming and panning + app.initZoomPan(); - // init preview via onclick - run.onclick = () => app.updatePreview(autoupdate.checked); + // toggle auto update + autoupdate.onchange = () => { + run.classList.toggle('hidden', autoupdate.checked); + app.updatePreview(autoupdate.checked); + }; + // toggle console + toggleconsole.onchange = () => { + project.settings.console = toggleconsole.checked; + app.updatePreview(autoupdate.checked); + }; + + // displays and handles libraries array + app.displayLibrariesArray(); + document.getElementById("sortLibraries"); + const searchBox = document.getElementById("searchBox"); + const suggestionsList = document.getElementById("suggestions"); + const searchFunc = () => { + const searchText = searchBox.value.trim(); + suggestionsList.innerHTML = ""; + if (!searchBox.value) { + suggestionsList.innerHTML = ""; + return false; + } + + if (searchText.length <= 0) { + suggestionsList.innerHTML = ""; + return false; + } else { + app.fetchSuggestions(searchText); + } + }; + searchBox.onkeyup = () => searchFunc(); + searchBox.onchange = () => searchFunc(); + addanother.onclick = () => app.displayLibrariesArray(); + + // init new project + newProj.onclick = () => app.newProject(); + // function to load json file document.getElementById('importProject').onchange = () => { let reader = new FileReader(); @@ -152991,70 +157637,61 @@ SOFTWARE.`; reader.onload = e => { // grab file project = JSON.parse(e.target.result); - - // Make first page as acrive page - app.activePage = 0; + if (app.appVersion.localeCompare(project.version) > 0) { + alert("Version must be 1.1.50 or greater!"); + return false; + } // Update settings - autoupdate.checked = (project.settings.autoupdate) ? true : false; + autoupdate.checked = (project.settings.autoupdate) ? false : false; toggleconsole.checked = (project.settings.console) ? true : false; - fz.value = project.settings.fontSize; - theme.checked = (project.settings.theme) ? true : false; - scratchpad.value = project.settings.scratchpad; + document.getElementById("fz").value = project.settings.fontSize; + document.getElementById("projectTitle").value = project.title; + document.getElementById("projectDesc").value = project.description; + document.getElementById("projectMeta").value = project.meta; let cmEditor = document.querySelectorAll('.cm-editor'); cmEditor.forEach((child) => { child.style.fontSize = `${fz.value}px`; }); // Load code into appropriate editors - htmlEditor.dispatch({ - changes: { - from: 0, - to: htmlEditor.state.doc.toString().length, - insert: project.pages[0].html, - }, - }); - cssEditor.dispatch({ - changes: { - from: 0, - to: cssEditor.state.doc.toString().length, - insert: project.pages[0].css, - }, - }); - jsEditor.dispatch({ - changes: { - from: 0, - to: jsEditor.state.doc.toString().length, - insert: project.pages[0].javascript, - }, - }); + function dispatchChanges(editor, content) { + editor.dispatch({ + changes: { + from: 0, + to: editor.state.doc.toString().length, + insert: content, + }, + }); + } + dispatchChanges(mdEditor, project.markdown); + dispatchChanges(htmlEditor, project.html); + dispatchChanges(cssEditor, project.css); + dispatchChanges(jsEditor, project.javascript); // render previews - app.createPageButtonList(); - app.activatePage(0); app.displayLibrariesArray(); app.updatePreview(autoupdate.checked); }; reader.readAsText(document.getElementById('importProject').files[0]); }; - // init preview - setTimeout(app.updatePreview, 300); - - // export project file - exportProjectFile.onclick = () => app.exportProjectFile(); - - // export zip file - exportZip.onclick = () => app.exportZip(); - } + // export project file + exportProj.onclick = () => app.exportProjectFile(); + + // export project as zip file + exportZip.onclick = () => app.exportZip(); + + // function to share weave + shareWeave.onclick = () => app.shareWeave(); + } }; // check if FileReader API is available if (!window.FileReader) { - alert('File API & FileReader API not supported!'); + alert("File API & FileReader API not supported!"); } - // initialize application app.init(); })(); diff --git a/go/css/style.css b/go/css/style.css index 01f41190..6cca361d 100644 --- a/go/css/style.css +++ b/go/css/style.css @@ -1,44 +1,184 @@ /* imports */ -@import url('../libraries/pico/pico.classless.min.css'); -@import url('../libraries/tailwind/tailwind-mod.min.css'); -@import url('../libraries/font-awesome/css/all.min.css'); +@import url("../libraries/font-awesome/css/all.min.css"); +@import url("../libraries/daisyui/daisyui.css"); +@import url("../libraries/tailwind/tailwind.min.css"); /* editor */ -.cm-editor { - height: 100%; -} -.ͼ1.cm-focused { - outline: none; -} -[data-theme=light] .ͼ2 .cm-activeLine { - background-color: hsla(200, 100%, 65%, 0.267); -} -[data-theme=dark] .ͼ2 .cm-activeLine { - background-color: hsla(200, 100%, 90%, 0.267); -} -.ͼ2 .cm-gutters { - border-right-color: hsla(200, 100%, 90%, 0.267); - background: transparent; -} -.ͼ2 .cm-activeLineGutter { - background: #cceeff2e; -} -.ͼ1 .cm-panel.cm-search [name=close] { - display: inline-block; - width: auto; - border-radius: 999999rem; - color: inherit; - font-size: 2rem; - font-weight: 400; - padding: .2rem .75rem; -} - -/* canvas/preview */ -[data-canvas] { - background-color: hsl(0, 0%, 100%); -} -[data-canvas] iframe { - width: 100%; - height: 100%; - border: 0; -} \ No newline at end of file +.cm-editor, .ͼg, .ͼl, .ͼ5 {color: #9cdcfe;} +.ͼ1.cm-focused {outline: 1px dotted #212121;} +.ͼ1 {position: relative !important; box-sizing: border-box; display: flex !important; flex-direction: column;} +.ͼ1 .cm-scroller {display: flex !important; align-items: flex-start !important; font-family: monospace; line-height: 1.4; height: 100%; overflow-x: auto; position: relative; z-index: 0;} +.ͼ1 .cm-content[contenteditable=true] {-webkit-user-modify: read-write-plaintext-only;} +.ͼ1 .cm-content {margin: 0; flex-grow: 2; flex-shrink: 0; display: block; white-space: pre; word-wrap: normal; box-sizing: border-box; padding: 4px 0; outline: none;} +.ͼ1 .cm-lineWrapping {white-space: pre-wrap; white-space: break-spaces; word-break: break-word; overflow-wrap: anywhere; flex-shrink: 1;} +.ͼ2 .cm-content {caret-color: black;} +.ͼ3 .cm-content {caret-color: white;} +.ͼ1 .cm-line {display: block; padding: 0 2px 0 6px;} +.ͼ1 .cm-layer > * {position: absolute;} +.ͼ1 .cm-layer {contain: size style;} +.ͼ2 .cm-selectionBackground, .ͼ3 .cm-selectionBackground {background: #3a3d41;} +.ͼ2.cm-focused .cm-selectionBackground, .ͼ3.cm-focused .cm-selectionBackground {background: #264f78;} +.ͼo.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .ͼo .cm-selectionBackground, .ͼo .cm-content ::selection {background: #3E4451;} +.ͼ1 .cm-cursorLayer {pointer-events: none;} +.ͼ1.cm-focused .cm-cursorLayer {animation: steps(1) cm-blink 1.2s infinite;} +@keyframes cm-blink {50% {opacity: 0;}} +@keyframes cm-blink2 {50% {opacity: 0;}} +.ͼ1 .cm-cursor, .ͼ1 .cm-dropCursor {border-left: 1.2px solid black; margin-left: -0.6px; pointer-events: none;} +.ͼ1 .cm-cursor {display: none;} +.ͼ3 .cm-cursor {border-left-color: #444;} +.ͼ1 .cm-dropCursor {position: absolute;} +.ͼ1.cm-focused .cm-cursor {display: block;} +.ͼ2 .cm-activeLine {background-color: #cceeff44;} +.ͼ3 .cm-activeLine {background-color: #99eeff33;} +.ͼ2 .cm-specialChar {color: red;} +.ͼ3 .cm-specialChar {color: #f78;} +.ͼ1 .cm-gutters {flex-shrink: 0; display: flex; height: 100%; box-sizing: border-box; left: 0; z-index: 200;} +.ͼ2 .cm-gutters {background-color: transparent; color: #6c6c6c; border-right: 1px solid #ddd;} +.ͼ3 .cm-gutters {background-color: #333338; color: #ccc;} +.ͼ1 .cm-gutter {display: flex !important; flex-direction: column; flex-shrink: 0; box-sizing: border-box; min-height: 100%; overflow: hidden;} +.ͼ1 .cm-gutterElement {box-sizing: border-box;} +.ͼ1 .cm-lineNumbers .cm-gutterElement {padding: 0 3px 0 5px; min-width: 20px; text-align: right; white-space: nowrap;} +.ͼ2 .cm-activeLineGutter {color: #e2f2ff; background: transparent;} +.ͼ1 .cm-tab {display: inline-block; overflow: hidden; vertical-align: bottom;} +.ͼ1 .cm-widgetBuffer {vertical-align: text-top; height: 1em; width: 0; display: inline;} +.ͼ1 .cm-placeholder {color: #888; display: inline-block; vertical-align: top;} +.ͼ1 .cm-highlightSpace:before {content: attr(data-display); position: absolute; pointer-events: none; color: #888;} +.ͼ1 .cm-highlightTab {background-image: url('data:image/svg+xml,'); background-size: auto 100%; background-position: right 90%; background-repeat: no-repeat;} +.ͼ1 .cm-trailingSpace {background-color: #ff332255;} +.ͼ1 .cm-button {vertical-align: middle; color: inherit; font-size: 70%; padding: .2em 1em; border-radius: 1px;} +.ͼ2 .cm-button:active {background-image: linear-gradient(#b4b4b4, #d0d3d6);} +.ͼ2 .cm-button {background-image: linear-gradient(#eff1f5, #d9d9df); border: 1px solid #888;} +.ͼ3 .cm-button:active {background-image: linear-gradient(#111, #333);} +.ͼ3 .cm-button {background-image: linear-gradient(#393939, #111); border: 1px solid #888;} +.ͼ1 .cm-textfield {vertical-align: middle; color: inherit; font-size: 70%; border: 1px solid silver; padding: .2em .5em;} +.ͼ2 .cm-textfield {background-color: white;} +.ͼ3 .cm-textfield {border: 1px solid #555; background-color: inherit;} +.ͼ1 .cm-diagnostic {padding: 3px 6px 3px 8px; margin-left: -1px; display: block; white-space: pre-wrap;} +.ͼ1 .cm-diagnostic-error {border-left: 5px solid #d11;} +.ͼ1 .cm-diagnostic-warning {border-left: 5px solid orange;} +.ͼ1 .cm-diagnostic-info {border-left: 5px solid #999;} +.ͼ1 .cm-diagnosticAction {font: inherit; border: none; padding: 2px 4px; background-color: #444; color: white; border-radius: 3px; margin-left: 8px; cursor: pointer;} +.ͼ1 .cm-diagnosticSource {font-size: 70%; opacity: 0.7;} +.ͼ1 .cm-lintRange {background-position: left bottom; background-repeat: repeat-x; padding-bottom: 0.7px;} +.ͼ1 .cm-lintRange-error {background-image: url('data:image/svg+xml,%3Cpath%20d%3D%22m0%202.5%20l2%20-1.5%20l1%200%20l2%201.5%20l1%200%22%20stroke%3D%22%23d11%22%20fill%3D%22none%22%20stroke-width%3D%22.7%22%2F%3E');} +.ͼ1 .cm-lintRange-warning {background-image: url('data:image/svg+xml,%3Cpath%20d%3D%22m0%202.5%20l2%20-1.5%20l1%200%20l2%201.5%20l1%200%22%20stroke%3D%22orange%22%20fill%3D%22none%22%20stroke-width%3D%22.7%22%2F%3E');} +.ͼ1 .cm-lintRange-info {background-image: url('data:image/svg+xml,%3Cpath%20d%3D%22m0%202.5%20l2%20-1.5%20l1%200%20l2%201.5%20l1%200%22%20stroke%3D%22%23999%22%20fill%3D%22none%22%20stroke-width%3D%22.7%22%2F%3E');} +.ͼ1 .cm-lintRange-active {background-color: #ffdd9980;} +.ͼ1 .cm-tooltip-lint {padding: 0; margin: 0;} +.ͼ1 .cm-lintPoint:after {content: ""; position: absolute; bottom: 0; left: -2px; border-left: 3px solid transparent; border-right: 3px solid transparent; border-bottom: 4px solid #d11;} +.ͼ1 .cm-lintPoint {position: relative;} +.ͼ1 .cm-lintPoint-warning:after {border-bottom-color: orange;} +.ͼ1 .cm-lintPoint-info:after {border-bottom-color: #999;} +.ͼ1 .cm-panel.cm-panel-lint ul [aria-selected] u {text-decoration: underline;} +.ͼ1 .cm-panel.cm-panel-lint ul [aria-selected] {background-color: #ddd;} +.ͼ1 .cm-panel.cm-panel-lint ul:focus [aria-selected] {background: #bdf; background-color: Highlight; color: white; color: HighlightText;} +.ͼ1 .cm-panel.cm-panel-lint ul u {text-decoration: none;} +.ͼ1 .cm-panel.cm-panel-lint ul {max-height: 100px; overflow-y: auto; padding: 0; margin: 0;} +.ͼ1 .cm-panel.cm-panel-lint [name=close] {position: absolute; top: 0; right: 2px; background: inherit; border: none; font: inherit; padding: 0; margin: 0;} +.ͼ1 .cm-panel.cm-panel-lint {position: relative;} +.ͼ1 .emmet-tracker {text-decoration: underline 1px green;} +.ͼ1 .emmet-preview {font-size: 0.9em;} +.ͼ1 .cm-completionIcon-emmet::after {content: " "; background: url("data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjM5MyIgdmlld0JveD0iMCAwIDM5MyAzOTMiIHdpZHRoPSIzOTMiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0iYSIgeDE9Ijk4LjYwODY5JSIgeDI9IjUwJSIgeTE9IjUwJSIgeTI9IjUwJSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjY2VmYmFkIi8+PHN0b3Agb2Zmc2V0PSIuODM2OTcyNTM1IiBzdG9wLWNvbG9yPSIjY2VmYmFkIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9IjAiLz48L2xpbmVhckdyYWRpZW50PjxwYXRoIGlkPSJiIiBkPSJtMTE2IDQ4IDEwNSA2M3YzNC45ODVsLTc1LTQ0Ljk5OTI4OXY2Ny42NjcyODlsNDkuNSAyOS4zNDctNDkuNSAyOS4zNDZ2NjcuNjY4Mjg5bDc1LTQ1LjAwMDI4OXYzNC45ODZsLTEwNSA2M3ptMTQ1IDg3IDEwNSA2My0xMDUgNjN2LTM0Ljk4Nmw0Ni42OTA0ODEtMjguMDE0LTQ2LjY5MDQ4MS0yOC4wMTR6Ii8+PC9kZWZzPjxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCIgdHJhbnNmb3JtPSJtYXRyaXgoLjcwNzEwNjc4IC0uNzA3MTA2NzggLjcwNzEwNjc4IC43MDcxMDY3OCAtODEuMzkyOTY1IDE5Ni41KSI+PGNpcmNsZSBjeD0iMTk2LjUiIGN5PSIxOTYuNSIgZmlsbD0iIzI1MjcyYiIgcj0iMTk2LjUiLz48dXNlIGZpbGw9IiM5MmMxMzEiIHhsaW5rOmhyZWY9IiNiIi8+PHVzZSBmaWxsPSJ1cmwoI2EpIiB4bGluazpocmVmPSIjYiIvPjwvZz48L3N2Zz4=") center/contain no-repeat; display: inline-block; width: 11px; height: 11px; vertical-align: middle;} +.ͼ1 .cm-gutter-lint .cm-gutterElement {padding: .2em; background:} +.ͼ1 .cm-gutter-lint {width: 1.4em;} +.ͼ1 .cm-lint-marker {width: 1em; height: 1em;} +.ͼ1 .cm-lint-marker-info {content: url('data:image/svg+xml,%3Cpath%20fill%3D%22%23aaf%22%20stroke%3D%22%2377e%22%20stroke-width%3D%226%22%20stroke-linejoin%3D%22round%22%20d%3D%22M5%205L35%205L35%2035L5%2035Z%22%2F%3E');} +.ͼ1 .cm-lint-marker-warning {content: url('data:image/svg+xml,%3Cpath%20fill%3D%22%23fe8%22%20stroke%3D%22%23fd7%22%20stroke-width%3D%226%22%20stroke-linejoin%3D%22round%22%20d%3D%22M20%206L37%2035L3%2035Z%22%2F%3E');} +.ͼ1 .cm-lint-marker-error {content: url('data:image/svg+xml,%3Ccircle%20cx%3D%2220%22%20cy%3D%2220%22%20r%3D%2215%22%20fill%3D%22%23f87%22%20stroke%3D%22%23f43%22%20stroke-width%3D%226%22%2F%3E');} +.ͼ1 .cm-selectionMatch {background-color: #99ff7780;} +.ͼ1 .cm-searchMatch .cm-selectionMatch {background-color: transparent;} +.ͼ1 .cm-tooltip.cm-tooltip-autocomplete > ul > li {overflow-x: hidden; text-overflow: ellipsis; cursor: pointer; padding: 1px 3px; line-height: 1.2;} +.ͼ1 .cm-tooltip.cm-tooltip-autocomplete > ul {font-family: monospace; white-space: nowrap; overflow: hidden auto; max-width: 700px; max-width: min(700px, 95vw); min-width: 250px; max-height: 10em; height: 100%; list-style: none; margin: 0; padding: 0;} +.ͼ2 .cm-tooltip-autocomplete ul li[aria-selected] {background: #17c; color: white;} +.ͼ2 .cm-tooltip-autocomplete-disabled ul li[aria-selected] {background: #777;} +.ͼ3 .cm-tooltip-autocomplete ul li[aria-selected] {background: #347; color: white;} +.ͼ3 .cm-tooltip-autocomplete-disabled ul li[aria-selected] {background: #444;} +.ͼ1 .cm-completionListIncompleteTop:before, .ͼ1 .cm-completionListIncompleteBottom:after {content: "···"; opacity: 0.5; display: block; text-align: center;} +.ͼ1 .cm-tooltip.cm-completionInfo {position: absolute; padding: 3px 9px; width: max-content; max-width: 400px; box-sizing: border-box;} +.ͼ1 .cm-completionInfo.cm-completionInfo-left {right: 100%;} +.ͼ1 .cm-completionInfo.cm-completionInfo-right {left: 100%;} +.ͼ1 .cm-completionInfo.cm-completionInfo-left-narrow {right: 30px;} +.ͼ1 .cm-completionInfo.cm-completionInfo-right-narrow {left: 30px;} +.ͼ1 .cm-completionMatchedText {text-decoration: underline;} +.ͼ1 .cm-completionDetail {margin-left: 0.5em; font-style: italic;} +.ͼ1 .cm-completionIcon {font-size: 90%; width: .8em; display: inline-block; text-align: center; padding-right: .6em; opacity: 0.6; box-sizing: content-box;} +.ͼ1 .cm-completionIcon-function:after, .ͼ1 .cm-completionIcon-method:after {content: 'ƒ';} +.ͼ1 .cm-completionIcon-class:after {content: '○';} +.ͼ1 .cm-completionIcon-interface:after {content: '◌';} +.ͼ1 .cm-completionIcon-variable:after {content: '𝑥';} +.ͼ1 .cm-completionIcon-constant:after {content: '𝐶';} +.ͼ1 .cm-completionIcon-type:after {content: '𝑡';} +.ͼ1 .cm-completionIcon-enum:after {content: '∪';} +.ͼ1 .cm-completionIcon-property:after {content: '□';} +.ͼ1 .cm-completionIcon-keyword:after {content: '🔑︎';} +.ͼ1 .cm-completionIcon-namespace:after {content: '▢';} +.ͼ1 .cm-completionIcon-text:after {content: 'abc'; font-size: 50%; vertical-align: middle;} +.ͼ1 .cm-tooltip {z-index: 100; box-sizing: border-box;} +.ͼ2 .cm-tooltip {border: 1px solid #3e5b9f; background-color: #111828;} +.ͼ2 .cm-tooltip-section:not(:first-child) {border-top: 1px solid #bbb;} +.ͼ3 .cm-tooltip {background-color: #333338; color: white;} +.ͼ1 .cm-tooltip-arrow:before, .ͼ1 .cm-tooltip-arrow:after {content: ''; position: absolute; width: 0; height: 0; border-left: 7px solid transparent; border-right: 7px solid transparent;} +.ͼ1 .cm-tooltip-above .cm-tooltip-arrow:before {border-top: 7px solid #bbb;} +.ͼ1 .cm-tooltip-above .cm-tooltip-arrow:after {border-top: 7px solid #f5f5f5; bottom: 1px;} +.ͼ1 .cm-tooltip-above .cm-tooltip-arrow {bottom: -7px;} +.ͼ1 .cm-tooltip-below .cm-tooltip-arrow:before {border-bottom: 7px solid #bbb;} +.ͼ1 .cm-tooltip-below .cm-tooltip-arrow:after {border-bottom: 7px solid #f5f5f5; top: 1px;} +.ͼ1 .cm-tooltip-below .cm-tooltip-arrow {top: -7px;} +.ͼ1 .cm-tooltip-arrow {height: 7px; width: 14px; position: absolute; z-index: -1; overflow: hidden;} +.ͼ3 .cm-tooltip .cm-tooltip-arrow:before {border-top-color: #333338; border-bottom-color: #333338;} +.ͼ3 .cm-tooltip .cm-tooltip-arrow:after {border-top-color: transparent; border-bottom-color: transparent;} +.ͼ1.cm-focused .cm-matchingBracket {background-color: #328c8252;} +.ͼ1.cm-focused .cm-nonmatchingBracket {background-color: #bb555544;} +.ͼ1 .cm-foldPlaceholder {background-color: #eee; border: 1px solid #ddd; color: #888; border-radius: .2em; margin: 0 1px; padding: 0 1px; cursor: pointer;} +.ͼ1 .cm-foldGutter span {padding: 0 1px; cursor: pointer;} +.ͼp {color: #c678dd;} +.ͼq {color: #e06c75;} +.ͼr {color: #61afef;} +.ͼs {color: #d19a66;} +.ͼt {color: #abb2bf;} +.ͼu {color: #e5c07b;} +.ͼv {color: #56b6c2;} +.ͼw {color: #7d8799;} +.ͼx {font-weight: bold;} +.ͼy {font-style: italic;} +.ͼz {text-decoration: line-through;} +.ͼ10 {color: #7d8799; text-decoration: underline;} +.ͼ11 {font-weight: bold; color: #e06c75;} +.ͼ12 {color: #d19a66;} +.ͼ13 {color: #98c379;} +.ͼ14 {color: #ffffff;} +.ͼo {color: #abb2bf; background-color: #282c34;} +.ͼo .cm-content {caret-color: #528bff;} +.ͼo .cm-cursor, .ͼo .cm-dropCursor {border-left-color: #528bff;} +.ͼo .cm-panels {background-color: #21252b; color: #abb2bf;} +.ͼo .cm-panels.cm-panels-top {border-bottom: 2px solid black;} +.ͼo .cm-panels.cm-panels-bottom {border-top: 2px solid black;} +.ͼo .cm-searchMatch {background-color: #72a1ff59; outline: 1px solid #457dff;} +.ͼo .cm-searchMatch.cm-searchMatch-selected {background-color: #6199ff2f;} +.ͼo .cm-activeLine {background-color: #6699ff0b;} +.ͼo .cm-selectionMatch {background-color: #aafe661a;} +.ͼo.cm-focused .cm-matchingBracket, .ͼo.cm-focused .cm-nonmatchingBracket {background-color: #bad0f847;} +.ͼo .cm-gutters {background-color: #282c34; color: #7d8799; border: none;} +.ͼo .cm-activeLineGutter {background-color: #2c313a;} +.ͼo .cm-foldPlaceholder {background-color: transparent; border: none; color: #ddd;} +.ͼo .cm-tooltip {border: none; background-color: #353a42;} +.ͼo .cm-tooltip .cm-tooltip-arrow:before {border-top-color: transparent; border-bottom-color: transparent;} +.ͼo .cm-tooltip .cm-tooltip-arrow:after {border-top-color: #353a42; border-bottom-color: #353a42;} +.ͼo .cm-tooltip-autocomplete > ul > li[aria-selected] {background-color: #2c313a; color: #abb2bf;} +.ͼ6 {text-decoration: underline;} +.ͼ7 {color: #cbcbcb; text-decoration: underline; font-weight: bold;} +.ͼ8 {font-style: italic;} +.ͼ9 {font-weight: bold;} +.ͼa {text-decoration: line-through;} +.ͼc {color: #569cd5;} +.ͼf, .ͼj, .ͼb, .ͼd, .ͼe, .ͼ6 {color: #ce9178;} +.ͼ6.ͼ5 {color: #9cdcfe;} +.ͼ6.ͼc {color: #9cdcfe;} +.ͼh {color: #30a;} +.ͼi {color: #569bd5;} +.ͼk {color: #256;} +.ͼm {color: #699754;} +.ͼn {color: #f00;} +.ͼ4 .cm-line ::selection {background-color: transparent !important;} +.ͼ4 .cm-line::selection {background-color: transparent !important;} +.ͼ4 .cm-line {caret-color: transparent !important;} \ No newline at end of file diff --git a/go/editor.mjs b/go/editor.mjs index 622d021d..9b68ea13 100644 --- a/go/editor.mjs +++ b/go/editor.mjs @@ -1,16 +1,38 @@ -import { EditorView, lineNumbers, highlightActiveLineGutter, highlightSpecialChars, drawSelection, dropCursor, rectangularSelection, crosshairCursor, highlightActiveLine, keymap } from '@codemirror/view' -import { EditorState, StateField, EditorSelection, StateEffect } from '@codemirror/state' -import { foldGutter, indentOnInput, syntaxHighlighting, defaultHighlightStyle, bracketMatching, foldKeymap, foldAll, unfoldAll, syntaxTree, syntaxTreeAvailable } from '@codemirror/language' -import { history, undo, redo, indentWithTab, indentMore, indentLess, defaultKeymap, historyKeymap, toggleComment} from '@codemirror/commands' -import { openSearchPanel, gotoLine, highlightSelectionMatches, searchKeymap } from '@codemirror/search' -import { closeBrackets, autocompletion, closeBracketsKeymap, completionKeymap } from '@codemirror/autocomplete' -import { linter, lintKeymap, lintGutter } from '@codemirror/lint' -import { html } from "@codemirror/lang-html" -import { css, cssLanguage } from '@codemirror/lang-css' -import { javascript, esLint } from "@codemirror/lang-javascript" -import * as eslint from "eslint-linter-browserify" -import { expandAbbreviation } from '@emmetio/codemirror6-plugin' -import { abbreviationTracker } from '@emmetio/codemirror6-plugin' +import { EditorView, lineNumbers, highlightActiveLineGutter, highlightSpecialChars, drawSelection, dropCursor, rectangularSelection, crosshairCursor, highlightActiveLine, keymap } from '@codemirror/view'; +import { EditorState, StateField, EditorSelection, StateEffect } from '@codemirror/state'; +import { foldGutter, indentOnInput, syntaxHighlighting, defaultHighlightStyle, bracketMatching, foldKeymap, foldAll, unfoldAll, syntaxTree, syntaxTreeAvailable } from '@codemirror/language'; +import { history, undo, redo, indentWithTab, indentMore, indentLess, defaultKeymap, historyKeymap, toggleComment} from '@codemirror/commands'; +import { openSearchPanel, gotoLine, highlightSelectionMatches, searchKeymap } from '@codemirror/search'; +import { closeBrackets, autocompletion, closeBracketsKeymap, completionKeymap } from '@codemirror/autocomplete'; +import { linter, lintKeymap, lintGutter } from '@codemirror/lint'; +import { markdown, markdownLanguage } from '@codemirror/lang-markdown'; +import { html } from "@codemirror/lang-html"; +import { css } from '@codemirror/lang-css'; +import { colorPicker } from '@replit/codemirror-css-color-picker'; +import { javascript, esLint } from "@codemirror/lang-javascript"; +import * as eslint from "eslint-linter-browserify"; +import { expandAbbreviation } from '@emmetio/codemirror6-plugin'; +import { abbreviationTracker } from '@emmetio/codemirror6-plugin'; + +// Retrieve project JSON from localStorage or set default +const savedProject = localStorage.getItem('kodeWeave'); +let defaultProject = { + version: "1.1.50", + settings: { + autoupdate: true, + console: true, + fontSize: 16, + }, + title: "An attractive title", + description: "The most attractive description ever!", + meta: ``, + libraries: ['https://cdnjs.cloudflare.com/ajax/libs/picocss/1.5.7/pico.classless.min.css', 'https://michaelsboost.com/TailwindCSSMod/tailwind-mod.min.css', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css', 'https://michaelsboost.github.io/kodeWeave/go/libraries/tailwind/tailwind.min.js'], + markdown: ``, + html: ``, + css: ``, + javascript: `` +}; +let project = savedProject ? JSON.parse(savedProject) : defaultProject; const config = { // eslint configuration @@ -54,8 +76,39 @@ const basicSetup = [ ]) ]; let activeEditor; +const mdEditor = new EditorView({ + state: EditorState.create({ + doc: project.markdown, + extensions: [ + basicSetup, + EditorView.lineWrapping, + keymap.of([ + indentWithTab, + ...closeBracketsKeymap, + ...defaultKeymap, + ...searchKeymap, + ...historyKeymap + ]), + markdown({ + base: markdownLanguage, + codeLanguages: [], + }), + colorPicker, + abbreviationTracker(), + EditorView.updateListener.of((v) => { + if (autoupdate.checked) { + app.updatePreview(autoupdate.checked); + } + }), + ], + }), + docChanged: true, + parent: document.getElementById('mdEditor'), + allowMultipleSelections: true, +}); const htmlEditor = new EditorView({ state: EditorState.create({ + doc: project.html, extensions: [ basicSetup, EditorView.lineWrapping, @@ -74,21 +127,22 @@ const htmlEditor = new EditorView({ ...lintKeymap ]), html(), + colorPicker, abbreviationTracker(), EditorView.updateListener.of((v) => { if (autoupdate.checked) { - setTimeout(() => { - app.updatePreview(autoupdate.checked); - }, 300); + app.updatePreview(autoupdate.checked); } }), ], }), + docChanged: true, parent: document.getElementById('htmlEditor'), allowMultipleSelections: true, }); const cssEditor = new EditorView({ state: EditorState.create({ + doc: project.css, extensions: [ basicSetup, EditorView.lineWrapping, @@ -107,23 +161,28 @@ const cssEditor = new EditorView({ ...lintKeymap ]), css(), + colorPicker, abbreviationTracker(), EditorView.updateListener.of((v) => { + project.css = cssEditor.state.doc.toString(); + if (autoupdate.checked) { - setTimeout(() => { - app.updatePreview(autoupdate.checked); - }, 300); + const iframe = document.getElementById('preview'); + const idoc = iframe.contentDocument || iframe.contentWindow.document; + idoc.getElementById("kodeWeaveCSSID").innerHTML = project.css; } }), ], }), + docChanged: true, parent: document.getElementById('cssEditor'), allowMultipleSelections: true, }); const jsEditor = new EditorView({ state: EditorState.create({ + doc: project.javascript, extensions: [ - basicSetup, + basicSetup, EditorView.lineWrapping, keymap.of([ indentWithTab, @@ -137,662 +196,192 @@ const jsEditor = new EditorView({ ]), linter(esLint(new eslint.Linter(), config)), javascript(), + colorPicker, + abbreviationTracker(), EditorView.updateListener.of((v) => { if (autoupdate.checked) { - setTimeout(() => { - app.updatePreview(autoupdate.checked); - }, 300); + app.updatePreview(autoupdate.checked); } }), ], }), + docChanged: true, parent: document.getElementById('jsEditor'), allowMultipleSelections: true, }); -activeEditor = htmlEditor; -let project; -let sortable; - -const app = { - // This is used to grab the active page and remember all contents used within that page's index - activePage: 0, - - // ajax function to get source of a file - getFile: (url, callback) => { - var xhr = new XMLHttpRequest() - xhr.open("GET", url) - xhr.send() - - xhr.onreadystatechange = (data) => { - if (xhr.readyState !== 4) { - return - } - - if (xhr.status === 200) { - callback(xhr.responseText) - } else { - console.warn("request_error") - } - } - }, - // Function to update settings - updateSettings: () => { - let settings = project.settings; - settings.theme = theme.checked; - settings.fontSize = fz.value; - settings.autoupdate = document.getElementById('autoupdate').checked; - settings.console = toggleconsole.checked; - }, +// Set doc values for each editor +mdEditor.state.update({ + changes: { from: 0, to: mdEditor.state.doc.length, insert: project.markdown } +}); - // Update localStorage - updateStorage: () => { - project.settings.autoupdate = autoupdate.checked; - project.settings.console = toggleconsole.checked; - project.settings.fontSize = fz.value; - project.settings.theme = theme.checked; - project.settings.scratchpad = scratchpad.value; - - project.pages[app.activePage].html = htmlEditor.state.doc.toString(); - project.pages[app.activePage].css = cssEditor.state.doc.toString(); - project.pages[app.activePage].javascript = jsEditor.state.doc.toString(); - - localStorage.setItem('kodeWeave', JSON.stringify(project)); - }, +htmlEditor.state.update({ + changes: { from: 0, to: htmlEditor.state.doc.length, insert: project.html } +}); - // Funtion to create button list of total pages - createPageButtonList: () => { - const pagesTab = document.querySelector('[data-pagecontent]'); - pagesTab.innerHTML = ''; +cssEditor.state.update({ + changes: { from: 0, to: cssEditor.state.doc.length, insert: project.css } +}); - for (let i = 0; i < project.pages.length; i++) { - let val = project.pages[i].name; +jsEditor.state.update({ + changes: { from: 0, to: jsEditor.state.doc.length, insert: project.javascript } +}); +activeEditor = htmlEditor; +let sortable; - let pageContainer = document.createElement('div'); - pageContainer.className = 'h-full pb-28'; +const app = { + appName: "kodeWeave", + appVersion: "1.1.50", + appUrl: "https://github.com/michaelsboost/kodeWeave/tree/main", + appLicense: "https://github.com/michaelsboost/kodeWeave/blob/main/LICENSE", - let pageName = document.createElement('div'); - pageName.classList.add('name'); - if (i === app.activePage) { - pageName.classList.add('text-blue-500', 'activepage'); - } else { - pageName.classList.add('text-current', 'bg-transparent', 'border-current'); + // Function to handle storage and display of library/framework + fetchSuggestions: searchText => { + fetch( + `https://api.cdnjs.com/libraries?search=${searchText}&fields=filename,description,version` + ) + .then(response => { + if (!response.ok) { + throw new Error("Network response was not ok"); } - pageName.classList.add('mb-2'); - pageName.textContent = val; - pageName.addEventListener('click', () => app.activatePage(i)); - - let article = document.createElement('article'); - article.classList.add('relative', 'h-full', 'm-0', 'p-0'); - - let previewElm = document.createElement('div'); - previewElm.id = `previewElm${i}`; - previewElm.classList.add('rounded-md', 'w-full', 'h-full'); - - let clickOverlay = document.createElement('div'); - clickOverlay.classList.add('absolute', 'inset-0'); - clickOverlay.addEventListener('click', () => app.activatePage(i)); - - let section = document.createElement('section'); - section.classList.add('mt-2'); - - let buttonGrid = document.createElement('div'); - - let eraseButton = document.createElement('button'); - eraseButton.classList.add('inline-block', 'text-current', 'bg-transparent', 'border-current'); - eraseButton.innerHTML = ''; - eraseButton.addEventListener('click', () => app.clearPage(i)); - - let cloneButton = document.createElement('button'); - cloneButton.classList.add('inline-block', 'text-current', 'bg-transparent', 'border-current'); - cloneButton.innerHTML = ''; - cloneButton.addEventListener('click', () => app.clonePage(i)); - - if (i === 0) { - buttonGrid.classList.add('grid', 'grid-cols-2', 'gap-1'); - buttonGrid.appendChild(cloneButton); - buttonGrid.appendChild(eraseButton); - } else { - buttonGrid.classList.add('grid', 'grid-cols-4', 'gap-1'); - - let renameButton = document.createElement('button'); - renameButton.classList.add('inline-block', 'text-current', 'bg-transparent', 'border-current'); - renameButton.innerHTML = ''; - renameButton.addEventListener('click', () => app.renamePage(i)); - - let deleteButton = document.createElement('button'); - deleteButton.classList.add('inline-block', 'text-current', 'bg-transparent', 'border-current'); - deleteButton.innerHTML = ''; - deleteButton.addEventListener('click', () => app.deletePage(i)); - - buttonGrid.appendChild(renameButton); - buttonGrid.appendChild(cloneButton); - buttonGrid.appendChild(eraseButton); - buttonGrid.appendChild(deleteButton); + return response.json(); + }) + .then(data => { + if (data && data.results && data.results.length > 0) { + const libraries = data.results.map(result => result); + app.displaySuggestions(libraries); } - - section.appendChild(buttonGrid); - article.appendChild(previewElm); - article.appendChild(clickOverlay); - article.appendChild(section); - - pageContainer.appendChild(pageName); - pageContainer.appendChild(article); - - pagesTab.appendChild(pageContainer); - - app.renderPagePreviews(i); - } - app.updateStorage(); - }, - - // Funtion to add a page - addPage: () => { - // first detect if user can clone a page - if (!projectTitle.value || !description.value) { - alert('Error: Website is missing the title and/or description meta information!'); - return false - } - - // value for the new page name - let val = prompt("What's the page's file name?").toLowerCase(); - - // detect if page name already exists - for (let i in project.pages) { - if (project.pages[i].name === val) { - alert('Operation aborted: Page name already exists!'); - return false - } - } - - // push page info to object - let tempObj = { - "name": val, - "title": `${projectTitle.value}`, - "description": `${description.value}`, - "scratchpad": "", - "libraries": [], - "html": "", - "css": "", - "javascript": "" - }; - project.pages.push(tempObj); - - // refresh pages list - app.createPageButtonList(); - }, - - // Funtion to rename a page - renamePage: index => { - // value for page name - let val = prompt("What's the page's file name?").toLowerCase(); - - // detect if page name already exists - for (let i in project.pages) { - if (project.pages[i].name === val) { - alert('Operation aborted: Page name already exists!'); - return false - } - } - - // remember old name - project.pages[index].name; - - // renames the object - project.pages[index].name = val; - - // apply the new name to the page button - undefined.textContent = val; - - // refresh pages list - app.createPageButtonList(); - }, - - // Funtion to clone a page - clonePage: i => { - // Clone the object - let originalPage = project.pages[i]; - let clonedPage = JSON.parse(JSON.stringify(originalPage)); - - // Apply the new name to the cloned object - clonedPage.name = `${originalPage.name}_clone${project.pages.length}`; - - // Push the cloned object to the pages object array - project.pages.push(clonedPage); - - // Refresh pages list - app.createPageButtonList(); - }, - - // Funtion to empty a page - clearPage: i => { - // Clear the object - let originalPage = project.pages[i]; - originalPage.libraries = []; - originalPage.html = ""; - originalPage.css = ""; - originalPage.javascript = ""; - - let resetEditor = editor => { - // Get the current state of the editor - const currentState = editor.state; - - // Create a new state with the updated content - editor.dispatch({ - changes: { - from: 0, - to: editor.state.doc.toString().length, - insert: "", - }, }) - } - - // detect if currently active page - if (i === app.activePage) { - resetEditor(htmlEditor) - resetEditor(cssEditor) - resetEditor(jsEditor) - } - - // Refresh pages list - app.createPageButtonList(); - }, - - // Funtion to delete a page - deletePage: i => { - // Remember page name for history stack - project.pages[i].name; - - // delete page from object - project.pages.splice(i, 1); - - // refresh pages list - app.createPageButtonList(); - - // if user deletes the active page, make the index the active page - if (i === app.activePage) { - const buttons = document.querySelectorAll("[data-pagecontent] button"); - const newActivePageButton = buttons[0]; - - app.activatePage(0, newActivePageButton); - } - }, - - // Funtion to make page active - activatePage: i => { - app.activePage = i; - const projectTitle = document.getElementById('projectTitle'); - - // Check if project has pages and the index is within bounds - if (project.pages && project.pages.length > i && project.pages[i]) { - const currentPage = project.pages[i]; - - // Update input values - css.value = project.settings.css - projectTitle.value = currentPage.title || ''; - description.value = currentPage.description || ''; - scratchpad.value = currentPage.scratchpad || ''; - - // Create a new state with the updated content - htmlEditor.dispatch({ - changes: { - from: 0, - to: htmlEditor.state.doc.toString().length, - insert: currentPage.html, - }, - }); - cssEditor.dispatch({ - changes: { - from: 0, - to: cssEditor.state.doc.toString().length, - insert: currentPage.css, - }, - }); - jsEditor.dispatch({ - changes: { - from: 0, - to: jsEditor.state.doc.toString().length, - insert: currentPage.javascript, - }, + .catch(error => { + console.error("Error fetching data:", error); }); - - // Remove old active page color indicator - let activePageBtn = document.querySelector("[data-pagecontent] .activepage"); - if (activePageBtn) { - activePageBtn.classList.remove("text-blue-500", "activepage"); - } - - // Add new active page color indicator - activePageBtn = document.querySelectorAll("[data-pagecontent] .name")[i]; - activePageBtn.classList.add("text-blue-500", "activepage"); - - // Update preview to show the active page - app.displayLibrariesArray(); - app.updatePreview(autoupdate.checked); - } else { - console.error('Invalid page data at index', i, 'in pages array:', project.pages); - console.log('Current project state:', project); - } - }, - - // update iframe preview function - updatePreview: (runManually = false) => { - app.updateSettings(); - app.updateStorage(); - const previewElm = document.getElementById('previewElm'); - const elm = document.querySelector('html[data-theme]'); - - const theme = project.settings.theme ? 'dark' : 'light'; - elm.setAttribute('data-theme', theme); - const css = project.pages[app.activePage].css; - - const consoleStyle = project.settings.console ? `.wrapper_yOR7u {width: 95%!important; border-radius: 15px 15px 0 0!important; z-index: 99999999;} .btn_yOR7u { background: inherit; padding: 0 0.5rem; margin: inherit; margin-right: 10px; border: inherit; color: #fff!important; } .nav_yOR7u {padding-bottom: 14px!important;} .line_yOR7u {background: inherit!important;}` : ''; - - const generateHtmlCode = (theme, showConsole, consoleStyle) => { - let libraryTags = ''; - const libraries = project.pages[app.activePage].libraries; - libraries.forEach(library => { - if (library.endsWith('.js')) { - libraryTags += ` - `; - } else if (library.endsWith('.css')) { - libraryTags += ` - `; - } else { - // Assuming it's a Google font - libraryTags += ` - `; - } - }); - return ` - - - ${project.pages[app.activePage].title} - - - - ${libraryTags} - - - - ${project.pages[app.activePage].html} - - ${showConsole} - -`; - }; - - const showConsole = project.settings.console ? `\n` : ''; - const htmlCode = generateHtmlCode(theme, showConsole, consoleStyle); - - previewElm.innerHTML = ''; - const frame = document.createElement('iframe'); - frame.setAttribute('id', 'preview'); - frame.setAttribute('title', project.pages[app.activePage].title); - frame.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-downloads allow-forms allow-modals allow-pointer-lock allow-popups'); - previewElm.appendChild(frame); - const previewFrame = document.getElementById('preview'); - const preview = previewFrame.contentDocument || previewFrame.contentWindow.document; - - preview.open(); - preview.write(htmlCode); - preview.close(); - }, - renderPagePreviews: initPage => { - const elm = document.querySelector('html[data-theme]'); - const theme = project.settings.theme ? 'dark' : 'light'; - elm.setAttribute('data-theme', theme); - const css = project.pages[initPage].css; - - const generateHtmlCode = theme => { - let libraryTags = ''; - const libraries = project.pages[initPage].libraries; - libraries.forEach(library => { - if (library.endsWith('.js')) { - libraryTags += ` - `; - } else if (library.endsWith('.css')) { - libraryTags += ` - `; - } else { - // Assuming it's a Google font - libraryTags += ` - `; - } - }); - return ` - - - ${project.pages[initPage].title} - - - - ${libraryTags} - - - - ${project.pages[initPage].html} - -`; - }; - const htmlCode = generateHtmlCode(theme); - - const outputPage = (pageid, pageframe) => { - const container = document.getElementById(`${pageid}`); - container.innerHTML = ''; // Clear existing content - const frame = document.createElement('iframe'); - frame.setAttribute('id', pageframe); - frame.setAttribute('title', project.pages[initPage].title); - frame.className = "transform scale-50"; // Apply Tailwind classes for transform - frame.style.width = "200%"; - frame.style.height = "200%"; - frame.style.transformOrigin = "top left"; // Set transform origin to center - frame.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-downloads allow-forms allow-modals allow-pointer-lock allow-popups'); - container.appendChild(frame); - const previewFrame = document.getElementById(`${pageframe}`); - const previewDoc = previewFrame.contentDocument || previewFrame.contentWindow.document; - previewDoc.open(); - previewDoc.write(htmlCode); - previewDoc.close(); - }; - - outputPage(`previewElm${initPage}`, `preview${initPage}`); }, - - // share weave - shareWeave: () => { - let cssLibrary = ""; - let jsLibrary = ""; - - const libraries = project.pages[app.activePage].libraries; + displaySuggestions: suggestions => { + const suggestionsList = document.getElementById("suggestions"); + suggestionsList.innerHTML = ""; // Clear previous suggestions - libraries.forEach(library => { - if (library.endsWith('.js')) { - jsLibrary += `${library};`; - } else if (library.endsWith('.css')) { - cssLibrary += `${library};`; - } else { - // Assuming it's a Google font, treat it as CSS - cssLibrary += `${library};`; - } + suggestions.forEach(result => { + const listItem = document.createElement("li"); + listItem.className = "list-none"; + listItem.innerHTML = `
+ ${result.name} + ${result.version} +
+
${result.description}

`; + listItem.onclick = () => { + // Add the clicked suggestion to the libraries array + const url = result.latest; // Assuming 'latest' holds the URL + project.libraries.push(url); + // Clear the suggestions list + suggestionsList.innerHTML = ""; + // Display the libraries display + app.displayLibrariesArray(); + searchBox.value = ""; + app.updatePreview(autoupdate.checked); + }; + suggestionsList.appendChild(listItem); }); + }, + displayLibrariesArray: () => { + const librariesArray = project.libraries; + let sortLibrariesContainer = document.getElementById("sortLibraries"); + sortLibrariesContainer.innerHTML = ""; + const embedArray = (result, index) => { + const newNav = document.createElement('nav'); + newNav.className = "flex justify-between py-2"; + newNav.setAttribute('data-index', index); - let data = { - title: projectTitle.value, - description: description.value, - html: ` - -${htmlEditor.state.doc.toString()}`, - css: `/* Shared from kodeWeave: https://michaelsboost.com/kodeWeave/ */ -${cssEditor.state.doc.toString()}`, - js: `// Shared from kodeWeave: https://michaelsboost.com/kodeWeave/ - -${jsEditor.state.doc.toString()}`, - css_external: cssLibrary, - js_external: jsLibrary, - editors: '000', // Set HTML and JS editors open, CSS editor closed - layout: 'left' // Set the layout to left - }; - - // Remove the trailing semicolon - if (cssLibrary !== "") { - cssLibrary = cssLibrary.slice(0, -1); - } - if (jsLibrary !== "") { - jsLibrary = jsLibrary.slice(0, -1); - } - - let JSONstring = JSON.stringify(data).replace(/"/g, """).replace(/'/g, "&apos"); + const sortButton = document.createElement('button'); + sortButton.className = 'p-3'; + sortButton.innerHTML = ''; + sortButton.setAttribute('data-sort', index); - let form = - '
' + - '' + - '' + - '
'; + const newInput = document.createElement("input"); + newInput.type = "text"; + newInput.placeholder = + "https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.15.2/Sortable.min.js"; + newInput.setAttribute("data", "library"); + newInput.className = "w-full p-3 pr-0 rounded-md rounded-r-none bg-gray-800"; + newInput.value = result; + newInput.onkeyup = () => { + // Update the value of the librariesArray at the corresponding index + librariesArray[index] = newInput.value.trim(); + app.updatePreview(autoupdate.checked); + }; - // Append click then remove - document.body.innerHTML += form; - document.querySelector('form').submit(); - document.querySelector('form').remove(); -}, + const deleteButton = document.createElement("button"); + deleteButton.className = + // "delete-button p-3 bg-red-400 rounded-md rounded-l-none"; + "delete-button p-3 bg-gray-800 rounded-md rounded-l-none"; + deleteButton.innerHTML = ''; + deleteButton.onclick = () => { + // Remove the library from the array by its index + project.libraries.splice(index, 1); + // Re-render the libraries array + app.displayLibrariesArray(); + app.updatePreview(autoupdate.checked); + }; - // export project json - exportProjectFile: () => { - let blob = new Blob([JSON.stringify(project)], { - type: "application/json" - }); - saveAs(blob, `${project.pages[app.activePage].name.toString().toLowerCase().replace(/ /g,"")}-kodeWeave.json`); - }, - - // export zip file - exportZip: () => { - const theme = project.settings.theme ? 'dark' : 'light'; + newNav.appendChild(sortButton); + newNav.appendChild(newInput); + newNav.appendChild(deleteButton); + sortLibrariesContainer.appendChild(newNav); + }; - let zip = new JSZip(); + // Embed each library into a new input field and delete button + librariesArray.forEach((input, index) => { + embedArray(librariesArray[index], index); + }); - let readmeCode = `${project.pages[0].title} -=================== - -${project.pages[0].description} - -Version -------------- - -0.0.1 - -License -------------- - -MIT - -This app was created and exported with [kodeWeave](https://michaelsboost.github.io/kodeWeave/)`; - - let licenseStr = `The MIT License (MIT) -Copyright (c) ${new Date().getFullYear()} John Doe - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE.`; + // Initialize SortableJS if it hasn't been initialized yet + if (!sortable) { + sortable = new Sortable(sortLibrariesContainer, { + handle: '[data-sort]', // Selector for the handle element + animation: 150, // Animation duration in milliseconds + onEnd: (event) => { + // Update the libraries array after sorting + const startIndex = event.oldIndex; + const endIndex = event.newIndex; + const movedLibrary = librariesArray.splice(startIndex, 1)[0]; + librariesArray.splice(endIndex, 0, movedLibrary); + app.updatePreview(autoupdate.checked); + app.createPageButtonList(); + } + }); + } - zip.file(`README.md`, readmeCode); - zip.file(`LICENSE.md`, licenseStr); - - project.pages.forEach(page => { - const css = page.css ? `` : ''; + // Check if the last input field is empty, and append an additional empty input field if needed + if ( + librariesArray.length === 0 || + librariesArray[librariesArray.length - 1].trim() !== "" + ) { + embedArray("", librariesArray.length); + } + }, - let libraryTags = ''; - page.libraries.forEach(library => { - if (library.endsWith('.js')) { - libraryTags += `\n`; - } else if (library.endsWith('.css')) { - libraryTags += `\n`; - } else { - // Assuming it's a Google font - libraryTags += `\n`; - } - }); - - zip.file(`${page.name}/js/${page.name}.js`, `${page.javascript}`); - zip.file(`${page.name}/css/${page.name}.css`, `${page.css}`); - zip.file(`${page.name}/${page.name}.html`, ` - - - ${page.title} - - - - - - - - - - - - - - - - - - ${libraryTags} - ${css} - - - ${page.html} - - - -`); + // Ajax function to download over http + getFile: (url, callback) => { + var xhr = new XMLHttpRequest(); + xhr.open("GET", url); + xhr.send(); - // Add libraries for the current page - /* - // Fetch and add external libraries - for (const library of page.libraries) { - const fileType = library.endsWith('.js') ? 'js' : 'css'; - app.getFile(library, content => { - zip.folder(`${page.name}/${fileType}_external`).file(library.split('/').pop(), content); - }); + xhr.onreadystatechange = data => { + if (xhr.readyState !== 4) { + return; } - */ - }); - // save kodeWeave project file in export - zip.file('project-kodeWeave.json', JSON.stringify(project)); - - let content = zip.generate({type:"blob"}); - saveAs(content, 'project.zip'); + if (xhr.status === 200) { + callback(xhr.responseText); + } else { + console.warn("request_error"); + } + }; }, - - // zooming and panning function + + // zooming and panning function initZoomPan: () => { // variables - document.querySelector('[data-device]'); - let canvas = document.querySelector('[data-canvas]'); + let canvas = document.getElementById('previewElm'); let canvasH = parseFloat(canvas.clientHeight); let canvasW = parseFloat(canvas.clientWidth / 2); @@ -803,7 +392,7 @@ SOFTWARE.`; }); let centerCanvas = () => { - let canvas = document.querySelector('[data-canvas]'); + let canvas = document.getElementById('previewElm'); let canvasH = parseFloat(canvas.clientHeight); let canvasW = parseFloat(canvas.clientWidth / 2); canvasW = parseFloat(canvas.clientWidth); @@ -868,10 +457,6 @@ SOFTWARE.`; zoomRatio // initial zoom ); instance.moveTo(initialXPos, initialYPos); - - // display size - viewx.value = parseInt(canvas.style.width); - viewy.value = parseInt(canvas.style.height); }; centerCanvas(); @@ -881,13 +466,13 @@ SOFTWARE.`; if (zoomIcon.getAttribute('data-zoom') === 'true') { canvas.selection = false; instance.pause(); - zoomIcon.innerHTML = ''; + zoomIcon.innerHTML = ''; zoomIcon.setAttribute('data-zoom', false); fill.classList.add('hidden'); } else { canvas.selection = true; instance.resume(); - zoomIcon.innerHTML = ''; + zoomIcon.innerHTML = ''; zoomIcon.setAttribute('data-zoom', true); fill.classList.remove('hidden'); } @@ -903,21 +488,16 @@ SOFTWARE.`; centerCanvas(); }; + // display size + let displaySize = () => { + viewx.value = parseInt(canvas.style.width); + viewy.value = parseInt(canvas.style.height); + }; + // reset canvas dimentions and center it let resetCanvas = (w, h) => { - canvasW = w; - canvasH = h; - - if (canvasW > canvasH) { - // landscape - canvas.style.width = canvasW + 'px'; - canvas.style.height = canvasH + 'px'; - centerCanvas(); - return false - } - - canvas.style.width = canvasH + 'px'; - canvas.style.height = canvasW + 'px'; + canvas.style.width = w + 'px'; + canvas.style.height = h + 'px'; centerCanvas(); }; @@ -925,347 +505,376 @@ SOFTWARE.`; // dimensions of Galaxy S8+ used mobilep.onclick = () => { resetCanvas(360, 740); - rotateview(); + displaySize(); }; mobilel.onclick = () => { - resetCanvas(360, 740); + resetCanvas(740, 360); + displaySize(); }; // reset canvas dimensions and center it - // dimensions of iPad Mini used + // dimensions of iPad Pro used tabletp.onclick = () => { - resetCanvas(1024, 768); - rotateview(); + resetCanvas(1024, 1366); + displaySize(); }; tabletl.onclick = () => { - resetCanvas(1024, 768); + resetCanvas(1366, 1024); + displaySize(); }; // reset canvas dimensions and center it - // 2012 macbook pro dimensions used + // 2015 15-inch retina macbook pro dimensions used desktopsize.onclick = () => { - resetCanvas(1440, 834); + resetCanvas(1920, 1200); + displaySize(); }; - // manually reset canvas dimensions and center it - viewx.onkeyup = () => { - resetCanvas(viewx.value, viewy.value); - }; - viewy.onkeyup = () => { - resetCanvas(viewx.value, viewy.value); - }; - }, - - // Function to handle storage and display of library/framework - fetchSuggestions: searchText => { - fetch(`https://api.cdnjs.com/libraries?search=${searchText}&fields=filename,description,version`) - .then(response => { - if (!response.ok) { - throw new Error('Network response was not ok'); - } - return response.json(); - }) - .then(data => { - if (data && data.results && data.results.length > 0) { - const libraries = data.results.map(result => result); - app.displaySuggestions(libraries); - } - }) - .catch(error => { - console.error('Error fetching data:', error); - }); - }, - displaySuggestions: suggestions => { - const suggestionsList = document.getElementById('suggestions'); - suggestionsList.innerHTML = ''; // Clear previous suggestions - - suggestions.forEach(result => { - const listItem = document.createElement('li'); - listItem.className = 'list-none'; - listItem.innerHTML = `
- ${result.name} - ${result.version} -
-
${result.description}

`; - listItem.onclick = () => { - // Add the clicked suggestion to the libraries array - const url = result.latest; // Assuming 'latest' holds the URL - project.pages[app.activePage].libraries.push(url); - // Clear the suggestions list - suggestionsList.innerHTML = ''; - // Display the libraries display - app.displayLibrariesArray(); - app.updatePreview(autoupdate.checked); - app.createPageButtonList(); - }; - suggestionsList.appendChild(listItem); - }); - }, - displayLibrariesArray: () => { - const librariesArray = project.pages[app.activePage].libraries; - let sortLibrariesContainer = document.getElementById('sortLibraries'); - sortLibrariesContainer.innerHTML = ''; - const embedArray = (result, index) => { - const newNav = document.createElement('nav'); - newNav.setAttribute('data-index', index); - - const sortButton = document.createElement('button'); - sortButton.className = 'w-auto border-0 bg-transparent text-current py-2'; - sortButton.innerHTML = ''; - sortButton.setAttribute('data-sort', index); + // manually reset canvas dimensions and center it + viewx.onkeyup = () => { + resetCanvas(viewx.value, viewy.value); + }; + viewy.onkeyup = () => { + resetCanvas(viewx.value, viewy.value); + }; + }, - const newInput = document.createElement('input'); - newInput.type = 'text'; - newInput.placeholder = 'https://website.com/index.css/.js'; - newInput.setAttribute('data', 'library') - newInput.className = 'rounded-r-none py-2'; - newInput.value = result; - newInput.onkeyup = () => { - // Update the value of the librariesArray at the corresponding index - librariesArray[index] = newInput.value.trim(); - app.updatePreview(autoupdate.checked); - app.createPageButtonList(); + // toggle side nav + toggleSideNav: e => { + const hideLists = () => { + document.querySelectorAll('#sidenav ul').forEach(list => { + list.classList.add('hidden'); + }); + }; + + const openSideNav = targetId => { + hideLists(); + document.getElementById(targetId).classList.remove('hidden'); + }; + + // Only shows main side navigation + document.querySelectorAll(`[data-openSide=${e}]`).forEach(btn => { + btn.onclick = () => { + openSideNav(e); }; - - const deleteButton = document.createElement('button'); - deleteButton.className = 'delete-button w-auto border-0 bg-red-400 rounded-l-none py-2'; - deleteButton.innerHTML = ''; - deleteButton.onclick = () => { - // Remove the library from the array by its index - project.pages[app.activePage].libraries.splice(index, 1); - // Re-render the libraries array - app.createPageButtonList(); - app.displayLibrariesArray(); - app.updatePreview(autoupdate.checked); + }); + + // Open sub menu in side navigation + document.querySelectorAll(`[data-closeSide=${e}]`).forEach(btn => { + btn.onclick = () => { + hideLists(); + mainsidenav.classList.remove('hidden'); // Assuming mainsidenav is defined somewhere + }; + }); + }, + + // load and save settings + loadSettings: () => { + projectTitle.value = project.title; + projectDesc.value = project.description; + projectMeta.value = project.meta; + fz.value = project.settings.fontSize; + toggleconsole.checked = (project.settings.console) ? true : false; + }, + saveSettings: () => { + const handleKeyup = (element, property) => { + element.onkeyup = () => { + project[property] = element.value; + localStorage.setItem('kodeWeave', JSON.stringify(project)); }; + }; - newNav.appendChild(sortButton); - newNav.appendChild(newInput); - newNav.appendChild(deleteButton); - sortLibrariesContainer.appendChild(newNav); + handleKeyup(projectTitle, "title"); + handleKeyup(projectDesc, "description"); + handleKeyup(projectMeta, "meta"); + + // Update project values immediately + project.title = projectTitle.value; + project.description = projectDesc.value; + project.meta = projectMeta.value; + fz.onkeyup = () => { + project.settings.fontSize = fz.value; + localStorage.setItem('kodeWeave', JSON.stringify(project)); }; +}, + + // Function to update previews + updatePreview: (runManually = false) => { + app.updateStorage(); + const generateHtmlCode = () => { + const tailwindStyle = + ".wrapper_yOR7u {left: 0!important; width: 100%!important; border-radius: 15px 15px 0 0!important; z-index: 99999999;} .btn_yOR7u { cursor: pointer; background: inherit; padding: 0 0.5rem; margin: inherit; margin-right: 0px; border: inherit; color: #fff!important; } .nav_yOR7u {padding-bottom: 14px!important;} .line_yOR7u {background: inherit!important;}"; + const consoleStyle = ``; + const addConsoleCSS = project.settings.console ? consoleStyle : ""; + const showConsole = project.settings.console + ? `` + : ""; - // Embed each library into a new input field and delete button - librariesArray.forEach((input, index) => { - embedArray(librariesArray[index], index); - }); + let libraryTags = ''; + const libraries = project.libraries; + libraries.forEach(library => { + if (library.endsWith('.js')) { + libraryTags += ` + `; + } else if (library.endsWith('.css')) { + libraryTags += ` + `; + } else { + // Assuming it's a Google font + libraryTags += ` + `; + } + }); + + // render html + return ` + + + ${project.title} + + + + ${libraryTags} + ${addConsoleCSS} + ${showConsole} + + + + ${project.html} - // Initialize SortableJS if it hasn't been initialized yet - if (!sortable) { - sortable = new Sortable(sortLibrariesContainer, { - handle: '[data-sort]', // Selector for the handle element - animation: 150, // Animation duration in milliseconds - onEnd: (event) => { - // Update the libraries array after sorting - const startIndex = event.oldIndex; - const endIndex = event.newIndex; - const movedLibrary = librariesArray.splice(startIndex, 1)[0]; - librariesArray.splice(endIndex, 0, movedLibrary); - app.updatePreview(autoupdate.checked); - app.createPageButtonList(); - } - }); - } + + +`; + }; - // Check if the last input field is empty, and append an additional empty input field if needed - if (librariesArray.length === 0 || librariesArray[librariesArray.length - 1].trim() !== '') { - embedArray('', librariesArray.length); - } - app.updatePreview(autoupdate.checked); - app.createPageButtonList(); + // Clear existing content in the preview element + previewElm.innerHTML = ``; + + // Get the content document of the iframe + const previewFrame = document.getElementById("preview"); + const previewDoc = + previewFrame.contentDocument || previewFrame.contentWindow.document; + + // Open, write HTML code, and close the content document + previewDoc.open(); + previewDoc.write(generateHtmlCode()); + previewDoc.close(); }, - // initalize application function - init: () => { - if (!localStorage.getItem('kodeWeave')) { - // project json - project = { - "version": "1.1.43", - "settings": { - "theme": false, - "fontSize": 16, - "autoupdate": true, - "console": true, - "scratchpad": "", - }, - "pages": [{ - "name": "index", - "title": "An attractive title", - "description": "The most attractive description ever!", - "libraries": ['https://cdnjs.cloudflare.com/ajax/libs/picocss/1.5.7/pico.classless.min.css', 'https://michaelsboost.com/TailwindCSSMod/tailwind-mod.min.css', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css', 'https://michaelsboost.github.io/kodeWeave/go/libraries/tailwind/tailwind.min.js'], - "html": "", - "css": "", - "javascript": "" - }] - }; - } else { - project = JSON.parse(localStorage.getItem('kodeWeave')) - - // Make first page as active page - app.activePage = 0; - - // Update settings - autoupdate.checked = project.settings.autoupdate; - const runParent = run.parentElement; - autoupdate.checked ? runParent.classList.add('hidden') : runParent.classList.remove('hidden'); - toggleconsole.checked = project.settings.console; - fz.value = project.settings.fontSize; - theme.checked = project.settings.theme; - scratchpad.value = project.settings.scratchpad; - let cmEditor = document.querySelectorAll('.cm-editor'); - cmEditor.forEach((child) => { - child.style.fontSize = `${fz.value}px`; - }); - - // Load code into appropriate editors - htmlEditor.dispatch({ - changes: { - from: 0, - to: htmlEditor.state.doc.toString().length, - insert: project.pages[0].html, - }, - }); - cssEditor.dispatch({ - changes: { - from: 0, - to: cssEditor.state.doc.toString().length, - insert: project.pages[0].css, - }, - }); - jsEditor.dispatch({ + // Update localStorage + updateStorage: () => { + project.settings.autoupdate = autoupdate.checked; + project.settings.console = toggleconsole.checked; + project.settings.fontSize = fz.value; + + project.markdown = mdEditor.state.doc.toString(); + project.html = htmlEditor.state.doc.toString(); + project.css = cssEditor.state.doc.toString(); + project.javascript = jsEditor.state.doc.toString(); + + localStorage.setItem('kodeWeave', JSON.stringify(project)); + }, + + newProject: () => { + projectTitle.value = ""; + projectDesc.value = ""; + projectMeta.value = ""; + project.libraries = []; + app.displayLibrariesArray(); + + // Load code into appropriate editors + function clearEditor(editor) { + editor.dispatch({ changes: { from: 0, - to: jsEditor.state.doc.toString().length, - insert: project.pages[0].javascript, + to: editor.state.doc.toString().length, + insert: "", }, }); - - // render previews - app.createPageButtonList(); - app.activatePage(0); - app.displayLibrariesArray(); - app.updatePreview(autoupdate.checked); } - const sortLibrariesContainer = document.getElementById('sortLibraries'); - const searchBox = document.getElementById('searchBox'); - const suggestionsList = document.getElementById('suggestions'); - searchBox.onkeyup = () => { - const searchText = searchBox.value.trim(); - suggestionsList.innerHTML = ""; - if (searchText.length <= 0) { - suggestionsList.innerHTML = ""; - return false; - } else { - app.fetchSuggestions(searchText); - } - }; - addanother.onclick = () => app.displayLibrariesArray() - logit.onclick = () => console.log(JSON.stringify(project.pages[app.activePage])) - - // init zooming and panning - app.initZoomPan(); - - // display libraries array - app.displayLibrariesArray(); - - // Define an array of objects containing element IDs, corresponding project properties, and event types - const elementProperties = [ - { id: 'autoupdate', property: 'settings.autoupdate', event: 'change' }, - { id: 'toggleconsole', property: 'settings.console', event: 'change' }, - { id: 'fa', property: 'settings.fontawesome', event: 'change' }, - { id: 'fz', property: 'settings.fontSize', events: ['keyup', 'change'] }, - { id: 'theme', property: 'settings.theme', event: 'change' }, - { id: 'css', property: 'settings.css', event: 'change' } - ]; - - // Function to handle saving project data on keyup or change event - const handleProjectDataChange = (element, property) => { - let value; - if (element.type === 'checkbox') { - value = element.checked; + clearEditor(mdEditor); + clearEditor(htmlEditor); + clearEditor(cssEditor); + clearEditor(jsEditor); + + app.updatePreview(autoupdate.checked); + }, + + // export project json + exportProjectFile: () => { + let blob = new Blob([JSON.stringify(project, null, 2)], { + type: "application/json" + }); + saveAs(blob, `${project.title.toString().toLowerCase().replace(/ /g,"")}-kodeWeave.json`); + }, + + // Exports zip file + exportZip: () => { + let zip = new JSZip(); + let licenseStr = `The MIT License (MIT) +Copyright (c) ${new Date().getFullYear()} John Doe + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.`; + + zip.file("README.md", project.markdown); + zip.file("LICENSE.md", licenseStr); + + // Iterate over each library + let libraryTags = ''; + let cssBundle = '/* imports */\n'; + let jsBundleFiles = ""; + project.libraries.forEach(library => { + if (library.endsWith('.js')) { + libraryTags += `\n`; + jsBundleFiles += `kWExportJSFiles.importJS("${library}");\n `; + } else if (library.endsWith('.css')) { + libraryTags += `\n`; + cssBundle += `@import url('${library}');\n`; } else { - value = element.value; - } - // Update the project data - const nestedProperties = property.split('.'); - let obj = project; - for (const prop of nestedProperties.slice(0, -1)) { - obj = obj[prop]; - if (obj === undefined) break; // Stop if any nested property is undefined - } - obj[nestedProperties.slice(-1)[0]] = value; - }; - - // Loop through the array and attach event handlers to the elements - elementProperties.forEach(({ id, property, event, events }) => { - const element = document.getElementById(id); - if (element) { - const handler = () => { - handleProjectDataChange(element, property); - if (element === autoupdate) { - run.parentElement.classList.toggle('hidden', autoupdate.checked); - } - if (element === fz) { - let cmEditor = document.querySelectorAll('.cm-editor'); - cmEditor.forEach((child) => { - child.style.fontSize = `${fz.value}px` - }); - } - app.updatePreview(autoupdate.checked); - }; - if (events) { - events.forEach(ev => element.addEventListener(ev, handler)); - } else { - element.addEventListener(event, handler); - } + // Assuming it's a Google font + libraryTags += `\n`; + cssBundle += `@import url('${library}');\n`; } }); - - // Define an array of objects containing element IDs, corresponding project properties, and event types - const elementProperties2 = [ - { id: 'projectTitle', property: 'title', events: ['keyup', 'change'] }, - { id: 'description', property: 'description', events: ['keyup', 'change'] }, - { id: 'scratchpad', property: 'scratchpad', events: ['keyup', 'change'] } - ]; - - // Function to handle saving project data - const savePageData = (element, property) => { - // Update the project data - const currentPage = project.pages[app.activePage]; - currentPage[property] = element.value; - app.updatePreview(autoupdate.checked); + + // add project css after libraries have been added in a single css file + cssBundle += project.css; + + zip.file("css/index.css", project.css); + zip.file("css/bundle.css", cssBundle); + zip.file("js/index.js", project.javascript); + zip.file("js/bundle.js", `const kWExportJSFiles = { + importJS: url => { + let script = document.createElement("script"); + script.src = url; + script.setAttribute("defer", ""); + document.head.appendChild(script); + }, + + init: () => { + ${jsBundleFiles} + setTimeout(() => kWExportJSFiles.importJS("js/index.js"), 100); + } +}; + +kWExportJSFiles.init();`); + zip.file("index.html", ` + + + ${project.title} + + + + + + + + + + + + + + + + + + + + ${project.html} + + + +`); + + // finally export the project as zip file + let content = zip.generate({ type: "blob" }); + saveAs(content, `${project.title.toString().toLowerCase().replace(/ /g,"")}-kodeWeave.zip`); + }, + + // share weave + shareWeave: () => { + const data = { + title: project.title, + description: project.description, + html: project.html, + css: project.css, + js: project.javascript, + css_external: project.libraries.filter(lib => lib.endsWith('.css')).join(';'), + js_external: project.libraries.filter(lib => lib.endsWith('.js')).join(';'), + editors: '000', + layout: 'left' }; + + // Stringify the JSON object and escape quotes + const JSONstring = JSON.stringify(data) + .replace(/"/g, """) + .replace(/'/g, "'"); + + // Create form element + const form = ` +
+ + +
`; + + // Append form to the document body and submit + document.body.insertAdjacentHTML('beforeend', form); + document.querySelector('form').submit(); + }, + + // Initiate function + init: () => { + // Place app name and version + document.getElementById("appName").textContent = app.appName; + document.getElementById("appVersion").textContent = app.appVersion; + document.getElementById("appUrl").href = app.appUrl; + document.getElementById("appLicense").href = app.appLicense; + + // load project meta data + app.loadSettings(); - // Loop through the array and attach event listeners to the elements for each event type - elementProperties2.forEach(({ id, property, events }) => { - const element = document.getElementById(id); - if (element) { - events.forEach(event => { - element.addEventListener(event, () => { - savePageData(element, property); - app.updatePreview(autoupdate.checked); - }); - }); - } - }); - - // function to add a page - addPage.onclick = app.addPage; - - // Function to init a new project - initNewProject.onclick = () => { - localStorage.clear(); - location.reload(true); - }; - + // save project meta data + app.saveSettings(); + + // init toggle settings for side nav + app.toggleSideNav('settings'); + + // toggle menu + menu.onchange = () => { + document.querySelector('label[for=menu]').classList.toggle('text-blue-500'); + sidenav.classList.toggle('hidden'); + }; + // Create a function to dynamically create and append the navbar const createNavbar = container => { // Create the main nav element const navbar = document.createElement('nav'); - navbar.className = 'absolute bottom-10 inset-x-0 px-4 text-center inline-block overflow-auto'; + // navbar.className = 'absolute bottom-10 inset-x-0 px-4 text-center inline-block overflow-auto'; const navbar2 = document.createElement('nav'); - navbar2.className = 'absolute bottom-0 inset-x-0 px-4 text-center inline-block overflow-auto'; + // navbar2.className = 'absolute bottom-0 inset-x-0 px-4 text-center inline-block overflow-auto'; // Create the ul element const ul = document.createElement('ul'); @@ -1397,103 +1006,154 @@ SOFTWARE.`; // Function to cut the selected text const cutSelection = editor => { - const { state, dispatch } = editor; - const { selection } = state; - const selectedText = state.sliceDoc(selection.main.from, selection.main.to); - navigator.clipboard.writeText(selectedText); - dispatch(state.update({ - changes: { from: selection.main.from, to: selection.main.to, insert: '' } - })); + const { state, dispatch } = editor; + const { selection } = state; + const selectedText = state.sliceDoc(selection.main.from, selection.main.to); + navigator.clipboard.writeText(selectedText); + dispatch(state.update({ + changes: { from: selection.main.from, to: selection.main.to, insert: '' } + })); }; // Function to copy the selected text const copySelection = editor => { - const { state } = editor; - const { selection } = state; - const selectedText = state.sliceDoc(selection.main.from, selection.main.to); - navigator.clipboard.writeText(selectedText); + const { state } = editor; + const { selection } = state; + const selectedText = state.sliceDoc(selection.main.from, selection.main.to); + navigator.clipboard.writeText(selectedText); }; // Function to paste text at the cursor position const pasteText = async editor => { - const { state, dispatch } = editor; - try { - const text = await navigator.clipboard.readText(); - if (text) { - const { selection } = state; - dispatch(state.update({ changes: { from: selection.main.from, to: selection.main.to, insert: text } })); - } else { - console.log('Clipboard is empty or does not contain text.'); - } - } catch (error) { - console.error('Failed to paste text:', error); + const { state, dispatch } = editor; + try { + const text = await navigator.clipboard.readText(); + if (text) { + const { selection } = state; + dispatch(state.update({ changes: { from: selection.main.from, to: selection.main.to, insert: text } })); + } else { + console.log('Clipboard is empty or does not contain text.'); } + } catch (error) { + console.error('Failed to paste text:', error); + } }; // Function to select all text in the active editor const selectAll = editor => { - const { state, dispatch } = editor; - const { doc } = state; - const selection = { anchor: 0, head: doc.length }; - dispatch(state.update({ selection })); + const { state, dispatch } = editor; + const { doc } = state; + const selection = { anchor: 0, head: doc.length }; + dispatch(state.update({ selection })); }; - - // init tabs - const tabButtons = document.querySelectorAll('[data-toggletab]'); - const tabContent = document.querySelectorAll('[data-tabcontent]'); - tabButtons.forEach(button => { - button.addEventListener('click', function() { - // Check if the clicked button already has the text-blue-500 class - const isAlreadyActive = this.classList.contains('text-blue-500'); - - // Remove text-blue-500 from all buttons - tabButtons.forEach((btn) => btn.classList.remove('text-blue-500')); - - // Hide all tab contents - tabContent.forEach((tab) => tab.classList.add('hidden')); - - if (!isAlreadyActive) { - // If the clicked button wasn't active, add text-blue-500 and show corresponding tab - this.classList.add('text-blue-500'); - const tabName = this.getAttribute('data-toggletab'); - const selectedTab = document.querySelector(`[data-tabcontent="${tabName}"]`); - selectedTab.classList.remove('hidden'); - - // Check if the selected tab is the 'pages' tab - if (tabName === 'pages') { - // push demo to the display tab - app.createPageButtonList(); + + // init tabs + const tabButtons = document.querySelectorAll("[data-toggletab]"); + const tabContent = document.querySelectorAll("[data-tabcontent]"); + + tabButtons.forEach(button => { + button.addEventListener("click", function () { + // Check if the clicked button already has the text-blue-500 class + const isAlreadyActive = this.classList.contains("text-blue-500"); + + // Remove text-blue-500 from all buttons + tabButtons.forEach(btn => btn.classList.remove("text-blue-500", "border-b", "border-blue-500")); + + // Hide all tab contents + tabContent.forEach(tab => tab.classList.add("hidden")); + + if (!isAlreadyActive) { + // If the clicked button wasn't active, add text-blue-500 and show corresponding tab + this.classList.add("text-blue-500", "border-b", "border-blue-500"); + const tabName = this.getAttribute("data-toggletab"); + const selectedTab = document.querySelector( + `[data-tabcontent="${tabName}"]` + ); + selectedTab.classList.remove("hidden"); + + if (tabName === 'markdown') { + document.querySelector('[data-editorHTMLNavbar]').innerHTML = ''; + document.querySelector('[data-editorCSSNavbar]').innerHTML = ''; + document.querySelector('[data-editorJSNavbar]').innerHTML = ''; + activeEditor = mdEditor; + createNavbar("editorMDNavbar"); } - // remember the active editor if (tabName === 'html') { - document.querySelector('[data-editorJSNavbar]').innerHTML = ''; + document.querySelector('[data-editorMDNavbar]').innerHTML = ''; document.querySelector('[data-editorCSSNavbar]').innerHTML = ''; + document.querySelector('[data-editorJSNavbar]').innerHTML = ''; activeEditor = htmlEditor; createNavbar("editorHTMLNavbar"); } if (tabName === 'css') { + document.querySelector('[data-editorMDNavbar]').innerHTML = ''; document.querySelector('[data-editorHTMLNavbar]').innerHTML = ''; document.querySelector('[data-editorJSNavbar]').innerHTML = ''; activeEditor = cssEditor; createNavbar("editorCSSNavbar"); } - if (tabName === 'js') { + if (tabName === 'javascript') { + document.querySelector('[data-editorMDNavbar]').innerHTML = ''; document.querySelector('[data-editorHTMLNavbar]').innerHTML = ''; document.querySelector('[data-editorCSSNavbar]').innerHTML = ''; activeEditor = jsEditor; createNavbar("editorJSNavbar"); } - } else { - // If the clicked button was already active, show the random tab - const randomTab = document.querySelector('[data-tabcontent="menu"]'); - randomTab.classList.remove('hidden'); - } - }); - }); + } else { + // If the clicked button was already active, hide it's content + const tabName = this.getAttribute("data-toggletab"); + const selectedTab = document.querySelector( + `[data-toggletab="${tabName}"]`); + const selectedTabContent = document.querySelector( + `[data-tabcontent="${tabName}"]`); + selectedTab.classList.remove("text-blue-500", "border-b", "border-blue-500"); + selectedTabContent.classList.add("hidden"); + } + }); + }); + + // init zooming and panning + app.initZoomPan(); - // init preview via onclick - run.onclick = () => app.updatePreview(autoupdate.checked); + // toggle auto update + autoupdate.onchange = () => { + run.classList.toggle('hidden', autoupdate.checked); + app.updatePreview(autoupdate.checked); + }; + // toggle console + toggleconsole.onchange = () => { + project.settings.console = toggleconsole.checked; + app.updatePreview(autoupdate.checked); + }; + + // displays and handles libraries array + app.displayLibrariesArray(); + const sortLibrariesContainer = document.getElementById("sortLibraries"); + const searchBox = document.getElementById("searchBox"); + const suggestionsList = document.getElementById("suggestions"); + const searchFunc = () => { + const searchText = searchBox.value.trim(); + suggestionsList.innerHTML = ""; + if (!searchBox.value) { + suggestionsList.innerHTML = ""; + return false; + } + + if (searchText.length <= 0) { + suggestionsList.innerHTML = ""; + return false; + } else { + app.fetchSuggestions(searchText); + } + }; + searchBox.onkeyup = () => searchFunc(); + searchBox.onchange = () => searchFunc(); + addanother.onclick = () => app.displayLibrariesArray(); + + // init new project + newProj.onclick = () => app.newProject(); + // function to load json file document.getElementById('importProject').onchange = () => { let reader = new FileReader(); @@ -1501,68 +1161,59 @@ SOFTWARE.`; reader.onload = e => { // grab file project = JSON.parse(e.target.result); - - // Make first page as acrive page - app.activePage = 0; + if (app.appVersion.localeCompare(project.version) > 0) { + alert("Version must be 1.1.50 or greater!"); + return false; + } // Update settings - autoupdate.checked = (project.settings.autoupdate) ? true : false; + autoupdate.checked = (project.settings.autoupdate) ? false : false; toggleconsole.checked = (project.settings.console) ? true : false; - fz.value = project.settings.fontSize; - theme.checked = (project.settings.theme) ? true : false; - scratchpad.value = project.settings.scratchpad; + document.getElementById("fz").value = project.settings.fontSize; + document.getElementById("projectTitle").value = project.title; + document.getElementById("projectDesc").value = project.description; + document.getElementById("projectMeta").value = project.meta; let cmEditor = document.querySelectorAll('.cm-editor'); cmEditor.forEach((child) => { child.style.fontSize = `${fz.value}px` }); // Load code into appropriate editors - htmlEditor.dispatch({ - changes: { - from: 0, - to: htmlEditor.state.doc.toString().length, - insert: project.pages[0].html, - }, - }); - cssEditor.dispatch({ - changes: { - from: 0, - to: cssEditor.state.doc.toString().length, - insert: project.pages[0].css, - }, - }); - jsEditor.dispatch({ - changes: { - from: 0, - to: jsEditor.state.doc.toString().length, - insert: project.pages[0].javascript, - }, - }); + function dispatchChanges(editor, content) { + editor.dispatch({ + changes: { + from: 0, + to: editor.state.doc.toString().length, + insert: content, + }, + }); + } + dispatchChanges(mdEditor, project.markdown); + dispatchChanges(htmlEditor, project.html); + dispatchChanges(cssEditor, project.css); + dispatchChanges(jsEditor, project.javascript); // render previews - app.createPageButtonList(); - app.activatePage(0); app.displayLibrariesArray(); app.updatePreview(autoupdate.checked); } reader.readAsText(document.getElementById('importProject').files[0]); }; - // init preview - setTimeout(app.updatePreview, 300); - - // export project file - exportProjectFile.onclick = () => app.exportProjectFile(); - - // export zip file - exportZip.onclick = () => app.exportZip(); - } + // export project file + exportProj.onclick = () => app.exportProjectFile(); + + // export project as zip file + exportZip.onclick = () => app.exportZip(); + + // function to share weave + shareWeave.onclick = () => app.shareWeave(); + } }; // check if FileReader API is available if (!window.FileReader) { - alert('File API & FileReader API not supported!'); + alert("File API & FileReader API not supported!"); } -// initialize application app.init(); \ No newline at end of file diff --git a/go/favicon.ico b/go/favicon.ico deleted file mode 100644 index ec989d7e..00000000 Binary files a/go/favicon.ico and /dev/null differ diff --git a/go/imgs/author.jpg b/go/imgs/author.jpg new file mode 100644 index 00000000..0db1d1d6 Binary files /dev/null and b/go/imgs/author.jpg differ diff --git a/go/imgs/bg.svg b/go/imgs/bg.svg deleted file mode 100644 index 43b5fe6e..00000000 --- a/go/imgs/bg.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - diff --git a/go/imgs/header.png b/go/imgs/header.png new file mode 100644 index 00000000..ee068763 Binary files /dev/null and b/go/imgs/header.png differ diff --git a/go/imgs/header.svg b/go/imgs/header.svg new file mode 100644 index 00000000..d8c87673 --- /dev/null +++ b/go/imgs/header.svg @@ -0,0 +1,62 @@ + + + +odeWeave diff --git a/go/imgs/logo.png b/go/imgs/logo.png index 2ee5a206..83ce7b7f 100644 Binary files a/go/imgs/logo.png and b/go/imgs/logo.png differ diff --git a/go/imgs/screenshots/emmet.png b/go/imgs/screenshots/emmet.png new file mode 100644 index 00000000..4cdaedf2 Binary files /dev/null and b/go/imgs/screenshots/emmet.png differ diff --git a/go/imgs/screenshots/kodeweave.png b/go/imgs/screenshots/kodeweave.png new file mode 100644 index 00000000..0b8d709e Binary files /dev/null and b/go/imgs/screenshots/kodeweave.png differ diff --git a/go/imgs/screenshots/lintandconsole.png b/go/imgs/screenshots/lintandconsole.png new file mode 100644 index 00000000..d8044219 Binary files /dev/null and b/go/imgs/screenshots/lintandconsole.png differ diff --git a/imgs/screenshots/pages.png b/go/imgs/screenshots/pages.png similarity index 100% rename from imgs/screenshots/pages.png rename to go/imgs/screenshots/pages.png diff --git a/go/imgs/screenshots/settings.png b/go/imgs/screenshots/settings.png new file mode 100644 index 00000000..a289ba9c Binary files /dev/null and b/go/imgs/screenshots/settings.png differ diff --git a/go/index.html b/go/index.html index 0d549d2a..831c5b6a 100644 --- a/go/index.html +++ b/go/index.html @@ -1,5 +1,5 @@ - + kodeWeave: Your on the go code playground! @@ -23,281 +23,377 @@ - + -
+
-
- - -
-
-
+
+
+
+
+ +
+
+
-
- -
- - -
-
- -
+ +
+
+
-
-
-
-
- -