Ver Código Fonte

initial editor with readonly files and persisting to localStorage

Lucas Stadler 10 anos atrás
pai
commit
5b7694826c
1 arquivos alterados com 152 adições e 0 exclusões
  1. 152 0
      glsl/files.js

+ 152 - 0
glsl/files.js

@ -0,0 +1,152 @@
1
document.body.innerHTML = "";
2
3
var files = {};
4
files.prefix = "/papill0n.org/shaders/";
5
files.current = "spec.txt";
6
7
files.builtin = {"spec.txt": `# Spec
8
9
- selecting a name from the list loads that file
10
- built-in files are accessible read-only
11
    (copy/paste if you want to change them)
12
- typing a new name and pressing enter (?) creates a new file
13
- typing an existing name and pressing enter loads that file
14
15
File store:
16
17
- \`.get(name: string) -> content or false if not existing\`
18
- \`create(name: string, content: string)\`, throws an error if
19
    a program with that name already exists (or if it is an
20
    internal file)
21
- \`save(name: string, content: string)\`, throws an error if
22
    there is no program with that name (?)
23
- \`remove(name: string)\`, for completeness
24
25
Some ideas:
26
27
- only store custom files in localStorage (obvious in retrospect)
28
- have a special temporary file that's saved very often (every
29
    keypress?), to prevent losing stuff.  reopen that file if
30
    it is present, delete/empty it on save.
31
- an 'unsaved changes' indicator, to make people save their stuff
32
33
Later:
34
35
- "forking" would be nice:
36
    - open existing code
37
    - say "fork" (or something more understandable)
38
    - create a new file with the content from the current one
39
    - edit away!
40
    - what about new names, though?
41
`,
42
             "default.frag": `void main() {
43
  gl_FragColor = gl_FragCoord;
44
}`,
45
             "includes/stdlib.frag": `float sdSphere(vec3 pos) {
46
  return length(pos) - 1.0;
47
}
48
49
float sdSphere(vec3 pos, float size) {
50
  return length(pos) - size;
51
}`,
52
             "includes/sphere-tracing.frag": `float trace(vec3 origin, vec3 direction) {
53
  // Uh, ... almost?
54
  return 0.0;
55
}`,
56
             "includes/ray-marching.frag": `// Warning: This is likely to be slower than sphere tracing!
57
float trace(vec3 origin, vec3 direction) {
58
  return 0.0;
59
}`};
60
61
files.exists = function(name) {
62
  return name in files.builtin || (files.prefix + name) in localStorage;
63
}
64
65
files.open = function(name) {
66
  files.current = name;
67
  if (name in files.builtin) {
68
    return {"name": name, "content": files.builtin[name], "readonly": true};
69
  } else {
70
    return {"name": name, "content": localStorage[files.prefix + name], "readonly": false};
71
  }
72
}
73
74
files.create = function(name, content) {
75
  if (name.trim() == "") { throw new Error("name can't be empty"); }
76
  if (name in files.builtin) { throw new Error("can't use internal file names"); }
77
  if (files.exists(name)) { throw new Error("already exists"); }
78
  
79
  files.current = name;
80
  
81
  var file = { "name": name, "content": content, readonly: false };
82
  localStorage["/papill0n.org/shaders/" + name] = content;
83
  return file;
84
}
85
86
files.save = function(name, content) {
87
  localStorage[files.prefix + name] = content;
88
}
89
90
function setFile(file, nameEl, contentEl) {
91
  nameEl.value = file.name;
92
  contentEl.value = file.content;
93
  contentEl.readOnly = file.readonly;
94
}
95
96
var nameEl = document.createElement("input");
97
nameEl.value = files.current;
98
nameEl.maxlength = 20;
99
nameEl.placeholder = "Name of the shader";
100
nameEl.setAttribute('list', 'files');
101
102
nameEl.onselect = function(ev) {
103
  setFile(files.open(nameEl.value), nameEl, editorEl);
104
  editorEl.focus();
105
}
106
107
nameEl.onkeyup = function(ev) {
108
  if (ev.keyCode == 13 && nameEl.value != files.current) { // Enter
109
    var file = nameEl.value;
110
    if (files.exists(file)) {
111
      setFile(files.open(file), nameEl, editorEl);
112
    } else {
113
      setFile(files.create(file, ""), nameEl, editorEl);
114
      var fileEl = document.createElement("option");
115
      fileEl.textContent = file;
116
      filesEl.appendChild(fileEl);
117
    }
118
    editorEl.focus();
119
  }
120
}
121
122
function addFileOption(name) {
123
  var fileEl = document.createElement("option");
124
  fileEl.textContent = name;
125
  filesEl.appendChild(fileEl);
126
}
127
128
var filesEl = document.createElement("datalist");
129
filesEl.id = "files";
130
Object.keys(files.builtin).forEach(addFileOption);
131
132
Object.keys(localStorage).forEach(function(file) {
133
  if (file.startsWith(files.prefix)) {
134
    addFileOption(file.substr(files.prefix.length));
135
  }
136
});
137
138
var editorEl = document.createElement("textarea");
139
editorEl.style  = "width: 70ex; height: 40em";
140
editorEl.onkeydown = function(ev) {
141
  if (ev.ctrlKey && ev.keyCode == 83) { // Ctrl-s
142
    ev.preventDefault();
143
    
144
    files.save(files.current, editorEl.value);
145
  }
146
}
147
148
setFile(files.open(files.current), nameEl, editorEl);
149
150
document.body.appendChild(filesEl);
151
document.body.appendChild(nameEl);
152
document.body.appendChild(editorEl);