@@ -19,8 +19,11 @@ import (
19
19
"oss.terrastruct.com/d2/d2lsp"
20
20
"oss.terrastruct.com/d2/d2oracle"
21
21
"oss.terrastruct.com/d2/d2parser"
22
+ "oss.terrastruct.com/d2/d2renderers/d2animate"
22
23
"oss.terrastruct.com/d2/d2renderers/d2fonts"
23
24
"oss.terrastruct.com/d2/d2renderers/d2svg"
25
+ "oss.terrastruct.com/d2/d2renderers/d2svg/appendix"
26
+ "oss.terrastruct.com/d2/d2target"
24
27
"oss.terrastruct.com/d2/lib/log"
25
28
"oss.terrastruct.com/d2/lib/memfs"
26
29
"oss.terrastruct.com/d2/lib/textmeasure"
@@ -170,47 +173,61 @@ func Compile(args []js.Value) (interface{}, error) {
170
173
return nil , & WASMError {Message : "missing 'index' file in input fs" , Code : 400 }
171
174
}
172
175
173
- fs , err := memfs .New (input .FS )
176
+ compileOpts := & d2lib.CompileOptions {
177
+ UTF16Pos : true ,
178
+ }
179
+
180
+ compileOpts .LayoutResolver = func (engine string ) (d2graph.LayoutGraph , error ) {
181
+ switch engine {
182
+ case "dagre" :
183
+ return d2dagrelayout .DefaultLayout , nil
184
+ case "elk" :
185
+ return d2elklayout .DefaultLayout , nil
186
+ default :
187
+ return nil , & WASMError {Message : fmt .Sprintf ("layout option '%s' not recognized" , engine ), Code : 400 }
188
+ }
189
+ }
190
+
191
+ var err error
192
+ compileOpts .FS , err = memfs .New (input .FS )
174
193
if err != nil {
175
194
return nil , & WASMError {Message : fmt .Sprintf ("invalid fs input: %s" , err .Error ()), Code : 400 }
176
195
}
177
196
178
- ruler , err : = textmeasure .NewRuler ()
197
+ compileOpts . Ruler , err = textmeasure .NewRuler ()
179
198
if err != nil {
180
199
return nil , & WASMError {Message : fmt .Sprintf ("text ruler cannot be initialized: %s" , err .Error ()), Code : 500 }
181
200
}
182
- ctx := log .WithDefault (context .Background ())
183
- layoutFunc := d2dagrelayout .DefaultLayout
201
+
184
202
if input .Opts != nil && input .Opts .Layout != nil {
185
- switch * input .Opts .Layout {
186
- case "dagre" :
187
- layoutFunc = d2dagrelayout .DefaultLayout
188
- case "elk" :
189
- layoutFunc = d2elklayout .DefaultLayout
190
- default :
191
- return nil , & WASMError {Message : fmt .Sprintf ("layout option '%s' not recognized" , * input .Opts .Layout ), Code : 400 }
192
- }
193
- }
194
- layoutResolver := func (engine string ) (d2graph.LayoutGraph , error ) {
195
- return layoutFunc , nil
203
+ compileOpts .Layout = input .Opts .Layout
196
204
}
197
205
198
206
renderOpts := & d2svg.RenderOpts {}
199
- var fontFamily * d2fonts.FontFamily
200
- if input .Opts != nil && input .Opts .Sketch != nil && * input .Opts .Sketch {
201
- fontFamily = go2 .Pointer (d2fonts .HandDrawn )
207
+ if input .Opts != nil && input .Opts .Sketch != nil {
202
208
renderOpts .Sketch = input .Opts .Sketch
209
+ if * input .Opts .Sketch {
210
+ compileOpts .FontFamily = go2 .Pointer (d2fonts .HandDrawn )
211
+ }
212
+ }
213
+ if input .Opts != nil && input .Opts .Pad != nil {
214
+ renderOpts .Pad = input .Opts .Pad
215
+ }
216
+ if input .Opts != nil && input .Opts .Center != nil {
217
+ renderOpts .Center = input .Opts .Center
203
218
}
204
219
if input .Opts != nil && input .Opts .ThemeID != nil {
205
220
renderOpts .ThemeID = input .Opts .ThemeID
206
221
}
207
- diagram , g , err := d2lib .Compile (ctx , input .FS ["index" ], & d2lib.CompileOptions {
208
- UTF16Pos : true ,
209
- FS : fs ,
210
- Ruler : ruler ,
211
- LayoutResolver : layoutResolver ,
212
- FontFamily : fontFamily ,
213
- }, renderOpts )
222
+ if input .Opts != nil && input .Opts .DarkThemeID != nil {
223
+ renderOpts .DarkThemeID = input .Opts .DarkThemeID
224
+ }
225
+ if input .Opts != nil && input .Opts .Scale != nil {
226
+ renderOpts .Scale = input .Opts .Scale
227
+ }
228
+
229
+ ctx := log .WithDefault (context .Background ())
230
+ diagram , g , err := d2lib .Compile (ctx , input .FS ["index" ], compileOpts , renderOpts )
214
231
if err != nil {
215
232
if pe , ok := err .(* d2parser.ParseError ); ok {
216
233
errs , _ := json .Marshal (pe .Errors )
@@ -225,6 +242,19 @@ func Compile(args []js.Value) (interface{}, error) {
225
242
FS : input .FS ,
226
243
Diagram : * diagram ,
227
244
Graph : * g ,
245
+ RenderOptions : RenderOptions {
246
+ ThemeID : renderOpts .ThemeID ,
247
+ DarkThemeID : renderOpts .DarkThemeID ,
248
+ Sketch : renderOpts .Sketch ,
249
+ Pad : renderOpts .Pad ,
250
+ Center : renderOpts .Center ,
251
+ Scale : renderOpts .Scale ,
252
+ ForceAppendix : input .Opts .ForceAppendix ,
253
+ Target : input .Opts .Target ,
254
+ AnimateInterval : input .Opts .AnimateInterval ,
255
+ Salt : input .Opts .Salt ,
256
+ NoXMLTag : input .Opts .NoXMLTag ,
257
+ },
228
258
}, nil
229
259
}
230
260
@@ -241,21 +271,159 @@ func Render(args []js.Value) (interface{}, error) {
241
271
return nil , & WASMError {Message : "missing 'diagram' field in input JSON" , Code : 400 }
242
272
}
243
273
274
+ animateInterval := 0
275
+ if input .Opts != nil && input .Opts .AnimateInterval != nil && * input .Opts .AnimateInterval > 0 {
276
+ animateInterval = int (* input .Opts .AnimateInterval )
277
+ }
278
+
279
+ var boardPath []string
280
+ noChildren := true
281
+
282
+ if input .Opts .Target != nil {
283
+ switch * input .Opts .Target {
284
+ case "*" :
285
+ noChildren = false
286
+ case "" :
287
+ default :
288
+ target := * input .Opts .Target
289
+ if strings .HasSuffix (target , ".*" ) {
290
+ target = target [:len (target )- 2 ]
291
+ noChildren = false
292
+ }
293
+ key , err := d2parser .ParseKey (target )
294
+ if err != nil {
295
+ return nil , & WASMError {Message : fmt .Sprintf ("target '%s' not recognized" , target ), Code : 400 }
296
+ }
297
+ boardPath = key .StringIDA ()
298
+ }
299
+ if ! noChildren && animateInterval <= 0 {
300
+ return nil , & WASMError {Message : fmt .Sprintf ("target '%s' only supported for animated SVGs" , * input .Opts .Target ), Code : 500 }
301
+ }
302
+ }
303
+
304
+ diagram := input .Diagram .GetBoard (boardPath )
305
+ if diagram == nil {
306
+ return nil , & WASMError {Message : fmt .Sprintf ("render target '%s' not found" , strings .Join (boardPath , "." )), Code : 400 }
307
+ }
308
+ if noChildren {
309
+ diagram .Layers = nil
310
+ diagram .Scenarios = nil
311
+ diagram .Steps = nil
312
+ }
313
+
244
314
renderOpts := & d2svg.RenderOpts {}
315
+
316
+ if input .Opts != nil && input .Opts .Salt != nil {
317
+ renderOpts .Salt = input .Opts .Salt
318
+ }
319
+
320
+ if animateInterval > 0 {
321
+ masterID , err := diagram .HashID (renderOpts .Salt )
322
+ if err != nil {
323
+ return nil , & WASMError {Message : fmt .Sprintf ("cannot process animate interval: %s" , err .Error ()), Code : 500 }
324
+ }
325
+ renderOpts .MasterID = masterID
326
+ }
327
+
328
+ ruler , err := textmeasure .NewRuler ()
329
+ if err != nil {
330
+ return nil , & WASMError {Message : fmt .Sprintf ("text ruler cannot be initialized: %s" , err .Error ()), Code : 500 }
331
+ }
332
+
245
333
if input .Opts != nil && input .Opts .Sketch != nil {
246
334
renderOpts .Sketch = input .Opts .Sketch
247
335
}
336
+ if input .Opts != nil && input .Opts .Pad != nil {
337
+ renderOpts .Pad = input .Opts .Pad
338
+ }
339
+ if input .Opts != nil && input .Opts .Center != nil {
340
+ renderOpts .Center = input .Opts .Center
341
+ }
248
342
if input .Opts != nil && input .Opts .ThemeID != nil {
249
343
renderOpts .ThemeID = input .Opts .ThemeID
250
344
}
251
- out , err := d2svg .Render (input .Diagram , renderOpts )
345
+ if input .Opts != nil && input .Opts .DarkThemeID != nil {
346
+ renderOpts .DarkThemeID = input .Opts .DarkThemeID
347
+ }
348
+ if input .Opts != nil && input .Opts .Scale != nil {
349
+ renderOpts .Scale = input .Opts .Scale
350
+ }
351
+ if input .Opts != nil && input .Opts .NoXMLTag != nil {
352
+ renderOpts .NoXMLTag = input .Opts .NoXMLTag
353
+ }
354
+
355
+ forceAppendix := input .Opts != nil && input .Opts .ForceAppendix != nil && * input .Opts .ForceAppendix
356
+
357
+ var boards [][]byte
358
+ if noChildren {
359
+ var board []byte
360
+ board , err = renderSingleBoard (renderOpts , forceAppendix , ruler , diagram )
361
+ boards = [][]byte {board }
362
+ } else {
363
+ boards , err = renderBoards (renderOpts , forceAppendix , ruler , diagram )
364
+ }
252
365
if err != nil {
253
366
return nil , & WASMError {Message : fmt .Sprintf ("render failed: %s" , err .Error ()), Code : 500 }
254
367
}
255
368
369
+ var out []byte
370
+ if len (boards ) > 0 {
371
+ out = boards [0 ]
372
+ if animateInterval > 0 {
373
+ out , err = d2animate .Wrap (diagram , boards , * renderOpts , animateInterval )
374
+ if err != nil {
375
+ return nil , & WASMError {Message : fmt .Sprintf ("animation failed: %s" , err .Error ()), Code : 500 }
376
+ }
377
+ }
378
+ }
379
+ return out , nil
380
+ }
381
+
382
+ func renderSingleBoard (opts * d2svg.RenderOpts , forceAppendix bool , ruler * textmeasure.Ruler , diagram * d2target.Diagram ) ([]byte , error ) {
383
+ out , err := d2svg .Render (diagram , opts )
384
+ if err != nil {
385
+ return nil , & WASMError {Message : fmt .Sprintf ("render failed: %s" , err .Error ()), Code : 500 }
386
+ }
387
+ if forceAppendix {
388
+ out = appendix .Append (diagram , opts , ruler , out )
389
+ }
256
390
return out , nil
257
391
}
258
392
393
+ func renderBoards (opts * d2svg.RenderOpts , forceAppendix bool , ruler * textmeasure.Ruler , diagram * d2target.Diagram ) ([][]byte , error ) {
394
+ var boards [][]byte
395
+ for _ , dl := range diagram .Layers {
396
+ childrenBoards , err := renderBoards (opts , forceAppendix , ruler , dl )
397
+ if err != nil {
398
+ return nil , err
399
+ }
400
+ boards = append (boards , childrenBoards ... )
401
+ }
402
+ for _ , dl := range diagram .Scenarios {
403
+ childrenBoards , err := renderBoards (opts , forceAppendix , ruler , dl )
404
+ if err != nil {
405
+ return nil , err
406
+ }
407
+ boards = append (boards , childrenBoards ... )
408
+ }
409
+ for _ , dl := range diagram .Steps {
410
+ childrenBoards , err := renderBoards (opts , forceAppendix , ruler , dl )
411
+ if err != nil {
412
+ return nil , err
413
+ }
414
+ boards = append (boards , childrenBoards ... )
415
+ }
416
+
417
+ if ! diagram .IsFolderOnly {
418
+ out , err := renderSingleBoard (opts , forceAppendix , ruler , diagram )
419
+ if err != nil {
420
+ return boards , err
421
+ }
422
+ boards = append ([][]byte {out }, boards ... )
423
+ }
424
+ return boards , nil
425
+ }
426
+
259
427
func GetBoardAtPosition (args []js.Value ) (interface {}, error ) {
260
428
if len (args ) < 3 {
261
429
return nil , & WASMError {Message : "missing required arguments" , Code : 400 }
0 commit comments