@newsails/veil-studio 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +181 -0
  2. package/bin/veil-studio.js +142 -0
  3. package/nuxt-app/.output/public/200.html +13 -0
  4. package/nuxt-app/.output/public/404.html +13 -0
  5. package/nuxt-app/.output/public/_nuxt/builds/latest.json +1 -0
  6. package/nuxt-app/.output/public/_nuxt/builds/meta/6b28df26-54af-4fad-a1f0-38808960d9fe.json +1 -0
  7. package/nuxt-app/.output/public/_nuxt/entry.BrrOeBSX.js +120 -0
  8. package/nuxt-app/.output/public/_nuxt/entry.CYnp7zY5.css +1 -0
  9. package/nuxt-app/.output/public/_nuxt/error-404.BbdzCaXe.js +1 -0
  10. package/nuxt-app/.output/public/_nuxt/error-404.JekaaCis.css +1 -0
  11. package/nuxt-app/.output/public/_nuxt/error-500.CNP9nqm1.css +1 -0
  12. package/nuxt-app/.output/public/_nuxt/error-500.DbOlBIIY.js +1 -0
  13. package/nuxt-app/.output/public/_nuxt/index.BEoXSIOu.css +1 -0
  14. package/nuxt-app/.output/public/_nuxt/index.CNms2yAq.js +1 -0
  15. package/nuxt-app/.output/public/_nuxt/materialdesignicons-webfont.B7mPwVP_.ttf +0 -0
  16. package/nuxt-app/.output/public/_nuxt/materialdesignicons-webfont.CSr8KVlo.eot +0 -0
  17. package/nuxt-app/.output/public/_nuxt/materialdesignicons-webfont.Dp5v-WZN.woff2 +0 -0
  18. package/nuxt-app/.output/public/_nuxt/materialdesignicons-webfont.PXm3-2wK.woff +0 -0
  19. package/nuxt-app/.output/public/_nuxt/vue.-sixQ7xP.BlWffD__.js +1 -0
  20. package/nuxt-app/.output/public/index.html +13 -0
  21. package/package.json +37 -0
  22. package/server/index.js +184 -0
  23. package/server/routes/files.js +80 -0
  24. package/server/socket.js +507 -0
  25. package/server/utils/board-state.js +357 -0
  26. package/server/utils/config.js +51 -0
  27. package/server/utils/db.js +484 -0
  28. package/server/utils/element-instances.js +104 -0
  29. package/server/utils/element-registry.js +127 -0
  30. package/server/utils/elements/agent-instance.js +62 -0
  31. package/server/utils/elements/agent.js +93 -0
  32. package/server/utils/elements/annotation.js +30 -0
  33. package/server/utils/elements/approval-gate.js +49 -0
  34. package/server/utils/elements/assumption.js +32 -0
  35. package/server/utils/elements/blocker.js +36 -0
  36. package/server/utils/elements/chat-room.js +47 -0
  37. package/server/utils/elements/chat-view.js +33 -0
  38. package/server/utils/elements/code-block.js +39 -0
  39. package/server/utils/elements/collapsible-group.js +37 -0
  40. package/server/utils/elements/comparison-table.js +38 -0
  41. package/server/utils/elements/constraint.js +32 -0
  42. package/server/utils/elements/decision.js +39 -0
  43. package/server/utils/elements/diff-patch.js +38 -0
  44. package/server/utils/elements/divider.js +30 -0
  45. package/server/utils/elements/document-draft.js +35 -0
  46. package/server/utils/elements/fact-claim.js +36 -0
  47. package/server/utils/elements/feedback-request.js +43 -0
  48. package/server/utils/elements/file-reference.js +31 -0
  49. package/server/utils/elements/filter.js +48 -0
  50. package/server/utils/elements/generator.js +51 -0
  51. package/server/utils/elements/goal.js +36 -0
  52. package/server/utils/elements/html.js +35 -0
  53. package/server/utils/elements/idea.js +32 -0
  54. package/server/utils/elements/image-local.js +21 -0
  55. package/server/utils/elements/image.js +34 -0
  56. package/server/utils/elements/json-object.js +47 -0
  57. package/server/utils/elements/label-tag.js +30 -0
  58. package/server/utils/elements/markdown.js +37 -0
  59. package/server/utils/elements/merger.js +36 -0
  60. package/server/utils/elements/message.js +40 -0
  61. package/server/utils/elements/milestone.js +44 -0
  62. package/server/utils/elements/notification.js +34 -0
  63. package/server/utils/elements/outline.js +35 -0
  64. package/server/utils/elements/primitive.js +36 -0
  65. package/server/utils/elements/pro-con-list.js +39 -0
  66. package/server/utils/elements/processor.js +54 -0
  67. package/server/utils/elements/project.js +40 -0
  68. package/server/utils/elements/question.js +42 -0
  69. package/server/utils/elements/queue.js +85 -0
  70. package/server/utils/elements/research-note.js +41 -0
  71. package/server/utils/elements/section.js +35 -0
  72. package/server/utils/elements/source-collection.js +45 -0
  73. package/server/utils/elements/splitter.js +42 -0
  74. package/server/utils/elements/status-update.js +38 -0
  75. package/server/utils/elements/task.js +72 -0
  76. package/server/utils/elements/template.js +46 -0
  77. package/server/utils/elements/test-case.js +42 -0
  78. package/server/utils/elements/text.js +29 -0
  79. package/server/utils/elements/todo-list.js +57 -0
  80. package/server/utils/elements/url-card.js +37 -0
  81. package/server/utils/elements/web-search-query.js +46 -0
  82. package/server/utils/elements/web-snapshot.js +37 -0
  83. package/server/utils/file-utils.js +88 -0
  84. package/server/utils/session-watcher.js +108 -0
  85. package/server/utils/socket-io.js +14 -0
  86. package/server/utils/veil-client.js +185 -0
  87. package/server/utils/veil-ws.js +207 -0
@@ -0,0 +1,36 @@
1
+ 'use strict'
2
+
3
+ module.exports = {
4
+ type: 'fact-claim',
5
+ name: 'Fact / Claim',
6
+ description: 'A verifiable fact or claim with confidence rating.',
7
+ icon: 'check-circle',
8
+ defaultWidth: 200,
9
+ defaultHeight: 80,
10
+ expansionMode: 'inline',
11
+ tags: ['research', 'thinking'],
12
+ isBuiltIn: true,
13
+ hiddenFromPalette: true,
14
+
15
+ createInstance(context) {
16
+ const { element } = context
17
+ return {
18
+ getViewData() {
19
+ return {
20
+ claim: element.data.claim || '',
21
+ confidence: element.data.confidence ?? 0.5,
22
+ sourceIds: element.data.sourceIds || [],
23
+ }
24
+ },
25
+ getPorts() {
26
+ return [{ key: 'claim-out', direction: 'output', dataType: 'text', label: 'Claim Out' }]
27
+ },
28
+ actions: {
29
+ update(data = {}) {
30
+ const { updateElement } = require('../board-state.js')
31
+ updateElement(element.id, { data: { ...element.data, ...data } })
32
+ },
33
+ },
34
+ }
35
+ },
36
+ }
@@ -0,0 +1,43 @@
1
+ 'use strict'
2
+
3
+ module.exports = {
4
+ type: 'feedback-request',
5
+ name: 'Feedback Request',
6
+ description: 'Request feedback or input from an agent or user.',
7
+ icon: 'message-circle-question',
8
+ defaultWidth: 220,
9
+ defaultHeight: 90,
10
+ expansionMode: 'inline',
11
+ tags: ['comm'],
12
+ isBuiltIn: true,
13
+ hiddenFromPalette: true,
14
+
15
+ createInstance(context) {
16
+ const { element } = context
17
+ return {
18
+ getViewData() {
19
+ return {
20
+ question: element.data.question || '',
21
+ status: element.data.status || 'waiting',
22
+ response: element.data.response || null,
23
+ }
24
+ },
25
+ getPorts() {
26
+ return [
27
+ { key: 'question-in', direction: 'input', dataType: 'text', label: 'Question In' },
28
+ { key: 'response-out', direction: 'output', dataType: 'text', label: 'Response Out' },
29
+ ]
30
+ },
31
+ actions: {
32
+ respond({ response } = {}) {
33
+ const { updateElement } = require('../board-state.js')
34
+ updateElement(element.id, { data: { ...element.data, status: 'answered', response } })
35
+ },
36
+ update(data = {}) {
37
+ const { updateElement } = require('../board-state.js')
38
+ updateElement(element.id, { data: { ...element.data, ...data } })
39
+ },
40
+ },
41
+ }
42
+ },
43
+ }
@@ -0,0 +1,31 @@
1
+ 'use strict'
2
+
3
+ module.exports = {
4
+ type: 'file-reference',
5
+ name: 'File Reference',
6
+ description: 'Reference a file in the project directory.',
7
+ icon: 'file',
8
+ defaultWidth: 180,
9
+ defaultHeight: 70,
10
+ expansionMode: 'popup',
11
+ tags: ['data', 'file'],
12
+ isBuiltIn: true,
13
+
14
+ createInstance(context) {
15
+ const { element } = context
16
+ return {
17
+ getViewData() {
18
+ return { filePath: element.data.filePath || null }
19
+ },
20
+ getPorts() {
21
+ return [{ key: 'file-out', direction: 'output', dataType: 'file', label: 'File Out' }]
22
+ },
23
+ actions: {
24
+ update({ filePath } = {}) {
25
+ const { updateElement } = require('../board-state.js')
26
+ updateElement(element.id, { data: { ...element.data, filePath } })
27
+ },
28
+ },
29
+ }
30
+ },
31
+ }
@@ -0,0 +1,48 @@
1
+ 'use strict'
2
+
3
+ module.exports = {
4
+ type: 'filter',
5
+ name: 'Filter',
6
+ description: 'Pass or reject input based on a JS expression.',
7
+ icon: 'filter',
8
+ defaultWidth: 200,
9
+ defaultHeight: 80,
10
+ expansionMode: 'inline',
11
+ tags: ['flow'],
12
+ isBuiltIn: true,
13
+ hiddenFromPalette: true,
14
+
15
+ createInstance(context) {
16
+ const { element, log } = context
17
+ return {
18
+ getViewData() {
19
+ return { expression: element.data.expression || '' }
20
+ },
21
+ getPorts() {
22
+ return [
23
+ { key: 'input-in', direction: 'input', dataType: 'any', label: 'Input In' },
24
+ { key: 'out-passed', direction: 'output', dataType: 'any', label: 'Passed' },
25
+ { key: 'out-rejected', direction: 'output', dataType: 'any', label: 'Rejected' },
26
+ ]
27
+ },
28
+ actions: {
29
+ evaluate({ input } = {}) {
30
+ const expr = element.data.expression
31
+ if (!expr) return { passed: true, rejected: false }
32
+ try {
33
+ // eslint-disable-next-line no-new-func
34
+ const result = new Function('input', `return (${expr})`)(input)
35
+ return { passed: !!result, rejected: !result }
36
+ } catch (err) {
37
+ log(`Filter expression error: ${err.message}`)
38
+ return { passed: false, rejected: true }
39
+ }
40
+ },
41
+ update(data = {}) {
42
+ const { updateElement } = require('../board-state.js')
43
+ updateElement(element.id, { data: { ...element.data, ...data } })
44
+ },
45
+ },
46
+ }
47
+ },
48
+ }
@@ -0,0 +1,51 @@
1
+ 'use strict'
2
+
3
+ module.exports = {
4
+ type: 'generator',
5
+ name: 'Generator',
6
+ description: 'Generate content by running an agent task with a prompt template.',
7
+ icon: 'cpu',
8
+ defaultWidth: 220,
9
+ defaultHeight: 130,
10
+ expansionMode: 'popup',
11
+ tags: ['flow'],
12
+ isBuiltIn: true,
13
+ hiddenFromPalette: true,
14
+
15
+ createInstance(context) {
16
+ const { element, veil, log } = context
17
+ return {
18
+ getViewData() {
19
+ return {
20
+ agentName: element.data.agentName || null,
21
+ promptTemplate: element.data.promptTemplate || '',
22
+ inputType: element.data.inputType || 'text',
23
+ outputType: element.data.outputType || 'text',
24
+ spawnOutput: element.data.spawnOutput || false,
25
+ }
26
+ },
27
+ getPorts() {
28
+ return [
29
+ { key: 'input-in', direction: 'input', dataType: 'any', label: 'Input In' },
30
+ { key: 'output-out', direction: 'output', dataType: 'any', label: 'Output Out' },
31
+ ]
32
+ },
33
+ actions: {
34
+ async run({ inputs } = {}) {
35
+ const agentName = element.data.agentName
36
+ if (!agentName) throw new Error('No agent name set')
37
+ const prompt = (element.data.promptTemplate || '').replace(/\{\{(\w+)\}\}/g, (_, k) => inputs?.[k] ?? `{{${k}}}`)
38
+ log(`Generating with agent "${agentName}"`)
39
+ const task = await veil.post('/tasks', { agent: agentName, prompt })
40
+ const { updateElement, updateElementState } = require('../board-state.js')
41
+ updateElement(element.id, { data: { ...element.data, lastTaskId: task.id } })
42
+ updateElementState(element.id, 'running')
43
+ },
44
+ update(data = {}) {
45
+ const { updateElement } = require('../board-state.js')
46
+ updateElement(element.id, { data: { ...element.data, ...data } })
47
+ },
48
+ },
49
+ }
50
+ },
51
+ }
@@ -0,0 +1,36 @@
1
+ 'use strict'
2
+
3
+ module.exports = {
4
+ type: 'goal',
5
+ name: 'Goal',
6
+ description: 'A goal with success criteria.',
7
+ icon: 'target',
8
+ defaultWidth: 200,
9
+ defaultHeight: 90,
10
+ expansionMode: 'inline',
11
+ tags: ['thinking', 'pm'],
12
+ isBuiltIn: true,
13
+ hiddenFromPalette: true,
14
+
15
+ createInstance(context) {
16
+ const { element } = context
17
+ return {
18
+ getViewData() {
19
+ return {
20
+ title: element.data.title || '',
21
+ description: element.data.description || '',
22
+ criteria: element.data.criteria || [],
23
+ }
24
+ },
25
+ getPorts() {
26
+ return [{ key: 'goal-out', direction: 'output', dataType: 'text', label: 'Goal Out' }]
27
+ },
28
+ actions: {
29
+ update(data = {}) {
30
+ const { updateElement } = require('../board-state.js')
31
+ updateElement(element.id, { data: { ...element.data, ...data } })
32
+ },
33
+ },
34
+ }
35
+ },
36
+ }
@@ -0,0 +1,35 @@
1
+ 'use strict'
2
+
3
+ module.exports = {
4
+ type: 'html',
5
+ name: 'HTML Preview',
6
+ description: 'Write HTML in edit mode, preview it live via a sandboxed iframe.',
7
+ icon: 'code-tags',
8
+ defaultWidth: 320,
9
+ defaultHeight: 240,
10
+ tags: ['data', 'dev'],
11
+ isBuiltIn: true,
12
+
13
+ createInstance(context) {
14
+ const { element } = context
15
+ return {
16
+ getViewData() {
17
+ return {
18
+ html: element.data.html || '',
19
+ }
20
+ },
21
+ getPorts() {
22
+ return [
23
+ { key: 'html-in', direction: 'input', dataType: 'text', label: 'HTML In' },
24
+ { key: 'html-out', direction: 'output', dataType: 'text', label: 'HTML Out' },
25
+ ]
26
+ },
27
+ actions: {
28
+ update(data = {}) {
29
+ const { updateElement } = require('../board-state.js')
30
+ updateElement(element.id, { data: { ...element.data, ...data } })
31
+ },
32
+ },
33
+ }
34
+ },
35
+ }
@@ -0,0 +1,32 @@
1
+ 'use strict'
2
+
3
+ module.exports = {
4
+ type: 'idea',
5
+ name: 'Idea',
6
+ description: 'Capture a single idea or insight.',
7
+ icon: 'lightbulb',
8
+ defaultWidth: 170,
9
+ defaultHeight: 80,
10
+ expansionMode: 'inline',
11
+ tags: ['thinking'],
12
+ isBuiltIn: true,
13
+ hiddenFromPalette: true,
14
+
15
+ createInstance(context) {
16
+ const { element } = context
17
+ return {
18
+ getViewData() {
19
+ return { text: element.data.text || '', category: element.data.category || null }
20
+ },
21
+ getPorts() {
22
+ return [{ key: 'idea-out', direction: 'output', dataType: 'text', label: 'Idea Out' }]
23
+ },
24
+ actions: {
25
+ update(data = {}) {
26
+ const { updateElement } = require('../board-state.js')
27
+ updateElement(element.id, { data: { ...element.data, ...data } })
28
+ },
29
+ },
30
+ }
31
+ },
32
+ }
@@ -0,0 +1,21 @@
1
+ 'use strict'
2
+
3
+ module.exports = {
4
+ type: 'image-local',
5
+ name: 'Image (Local)',
6
+ description: 'Display a local file image by its path on disk.',
7
+ icon: '🖼',
8
+ defaultWidth: 200,
9
+ defaultHeight: 160,
10
+ expansionMode: 'inline',
11
+ tags: ['data', 'media'],
12
+ isBuiltIn: true,
13
+
14
+ createInstance(_context) {
15
+ return {
16
+ getViewData() { return {} },
17
+ getPorts() { return [] },
18
+ actions: {},
19
+ }
20
+ },
21
+ }
@@ -0,0 +1,34 @@
1
+ 'use strict'
2
+
3
+ module.exports = {
4
+ type: 'image',
5
+ name: 'Image',
6
+ description: 'Display an image from the project directory.',
7
+ icon: 'image',
8
+ defaultWidth: 220,
9
+ defaultHeight: 160,
10
+ expansionMode: 'popup',
11
+ tags: ['data', 'media'],
12
+ isBuiltIn: true,
13
+
14
+ createInstance(context) {
15
+ const { element } = context
16
+ return {
17
+ getViewData() {
18
+ return { filePath: element.data.filePath || null, alt: element.data.alt || '' }
19
+ },
20
+ getPorts() {
21
+ return [{ key: 'image-out', direction: 'output', dataType: 'file', label: 'Image Out' }]
22
+ },
23
+ actions: {
24
+ update({ filePath, alt } = {}) {
25
+ const { updateElement } = require('../board-state.js')
26
+ const data = { ...element.data }
27
+ if (filePath !== undefined) data.filePath = filePath
28
+ if (alt !== undefined) data.alt = alt
29
+ updateElement(element.id, { data })
30
+ },
31
+ },
32
+ }
33
+ },
34
+ }
@@ -0,0 +1,47 @@
1
+ 'use strict'
2
+
3
+ module.exports = {
4
+ type: 'json-object',
5
+ name: 'JSON Object',
6
+ description: 'Store and display a JSON object with dynamic output ports per top-level key.',
7
+ icon: 'braces',
8
+ defaultWidth: 220,
9
+ defaultHeight: 110,
10
+ expansionMode: 'popup',
11
+ tags: ['data'],
12
+ isBuiltIn: true,
13
+
14
+ createInstance(context) {
15
+ const { element } = context
16
+ return {
17
+ getViewData() {
18
+ return {
19
+ content: element.data.content || {},
20
+ title: element.data.title || '',
21
+ }
22
+ },
23
+ getPorts() {
24
+ const content = element.data.content || {}
25
+ const keyPorts = Object.keys(content).map(key => ({
26
+ key: `out-${key}`,
27
+ direction: 'output',
28
+ dataType: 'any',
29
+ label: key,
30
+ }))
31
+ return [
32
+ { key: 'full-out', direction: 'output', dataType: 'json', label: 'Full Object' },
33
+ ...keyPorts,
34
+ ]
35
+ },
36
+ actions: {
37
+ update({ content, title } = {}) {
38
+ const { updateElement } = require('../board-state.js')
39
+ const data = { ...element.data }
40
+ if (content !== undefined) data.content = content
41
+ if (title !== undefined) data.title = title
42
+ updateElement(element.id, { data })
43
+ },
44
+ },
45
+ }
46
+ },
47
+ }
@@ -0,0 +1,30 @@
1
+ 'use strict'
2
+
3
+ module.exports = {
4
+ type: 'label-tag',
5
+ name: 'Label / Tag',
6
+ description: 'A visual label or tag for organizing elements.',
7
+ icon: 'tag',
8
+ defaultWidth: 120,
9
+ defaultHeight: 36,
10
+ expansionMode: 'inline',
11
+ tags: ['org'],
12
+ isBuiltIn: true,
13
+ hiddenFromPalette: true,
14
+
15
+ createInstance(context) {
16
+ const { element } = context
17
+ return {
18
+ getViewData() {
19
+ return { text: element.data.text || '', color: element.data.color || '#3b82f6' }
20
+ },
21
+ getPorts() { return [] },
22
+ actions: {
23
+ update(data = {}) {
24
+ const { updateElement } = require('../board-state.js')
25
+ updateElement(element.id, { data: { ...element.data, ...data } })
26
+ },
27
+ },
28
+ }
29
+ },
30
+ }
@@ -0,0 +1,37 @@
1
+ 'use strict'
2
+
3
+ module.exports = {
4
+ type: 'markdown',
5
+ name: 'Markdown',
6
+ description: 'A markdown note with rendered preview and edit/read toggle.',
7
+ icon: 'file-text',
8
+ defaultWidth: 220,
9
+ defaultHeight: 140,
10
+ expansionMode: 'inline',
11
+ tags: ['data', 'content'],
12
+ isBuiltIn: true,
13
+
14
+ createInstance(context) {
15
+ const { element } = context
16
+ return {
17
+ getViewData() {
18
+ return {
19
+ content: element.data.content || '',
20
+ title: element.data.title || '',
21
+ }
22
+ },
23
+ getPorts() {
24
+ return [{ key: 'text-out', direction: 'output', dataType: 'text', label: 'Text Out' }]
25
+ },
26
+ actions: {
27
+ update({ content, title } = {}) {
28
+ const { updateElement } = require('../board-state.js')
29
+ const data = { ...element.data }
30
+ if (content !== undefined) data.content = content
31
+ if (title !== undefined) data.title = title
32
+ updateElement(element.id, { data })
33
+ },
34
+ },
35
+ }
36
+ },
37
+ }
@@ -0,0 +1,36 @@
1
+ 'use strict'
2
+
3
+ module.exports = {
4
+ type: 'merger',
5
+ name: 'Merger',
6
+ description: 'Combine multiple inputs into a single output.',
7
+ icon: 'merge',
8
+ defaultWidth: 200,
9
+ defaultHeight: 90,
10
+ expansionMode: 'inline',
11
+ tags: ['flow'],
12
+ isBuiltIn: true,
13
+ hiddenFromPalette: true,
14
+
15
+ createInstance(context) {
16
+ const { element } = context
17
+ return {
18
+ getViewData() {
19
+ return { strategy: element.data.strategy || 'concat' }
20
+ },
21
+ getPorts() {
22
+ return [
23
+ { key: 'in-a', direction: 'input', dataType: 'any', label: 'Input A' },
24
+ { key: 'in-b', direction: 'input', dataType: 'any', label: 'Input B' },
25
+ { key: 'merged-out', direction: 'output', dataType: 'any', label: 'Merged Out' },
26
+ ]
27
+ },
28
+ actions: {
29
+ update(data = {}) {
30
+ const { updateElement } = require('../board-state.js')
31
+ updateElement(element.id, { data: { ...element.data, ...data } })
32
+ },
33
+ },
34
+ }
35
+ },
36
+ }
@@ -0,0 +1,40 @@
1
+ 'use strict'
2
+
3
+ module.exports = {
4
+ type: 'message',
5
+ name: 'Message',
6
+ description: 'A single message with sender and content.',
7
+ icon: 'message-square',
8
+ defaultWidth: 200,
9
+ defaultHeight: 80,
10
+ expansionMode: 'inline',
11
+ tags: ['comm'],
12
+ isBuiltIn: true,
13
+ hiddenFromPalette: true,
14
+
15
+ createInstance(context) {
16
+ const { element } = context
17
+ return {
18
+ getViewData() {
19
+ return {
20
+ from: element.data.from || '',
21
+ content: element.data.content || '',
22
+ timestamp: element.data.timestamp || null,
23
+ to: element.data.to || null,
24
+ }
25
+ },
26
+ getPorts() {
27
+ return [
28
+ { key: 'msg-in', direction: 'input', dataType: 'text', label: 'Message In' },
29
+ { key: 'msg-out', direction: 'output', dataType: 'text', label: 'Message Out' },
30
+ ]
31
+ },
32
+ actions: {
33
+ update(data = {}) {
34
+ const { updateElement } = require('../board-state.js')
35
+ updateElement(element.id, { data: { ...element.data, ...data } })
36
+ },
37
+ },
38
+ }
39
+ },
40
+ }
@@ -0,0 +1,44 @@
1
+ 'use strict'
2
+
3
+ module.exports = {
4
+ type: 'milestone',
5
+ name: 'Milestone',
6
+ description: 'A project milestone with deadline.',
7
+ icon: 'flag',
8
+ defaultWidth: 220,
9
+ defaultHeight: 90,
10
+ expansionMode: 'inline',
11
+ tags: ['pm'],
12
+ isBuiltIn: true,
13
+ hiddenFromPalette: true,
14
+
15
+ createInstance(context) {
16
+ const { element } = context
17
+ return {
18
+ getViewData() {
19
+ return {
20
+ title: element.data.title || '',
21
+ deadline: element.data.deadline || null,
22
+ completed: element.data.completed || false,
23
+ taskIds: element.data.taskIds || [],
24
+ }
25
+ },
26
+ getPorts() {
27
+ return [
28
+ { key: 'tasks-in', direction: 'input', dataType: 'any', label: 'Tasks In' },
29
+ { key: 'status-out', direction: 'output', dataType: 'text', label: 'Status Out' },
30
+ ]
31
+ },
32
+ actions: {
33
+ update(data = {}) {
34
+ const { updateElement } = require('../board-state.js')
35
+ updateElement(element.id, { data: { ...element.data, ...data } })
36
+ },
37
+ complete(_params) {
38
+ const { updateElement } = require('../board-state.js')
39
+ updateElement(element.id, { data: { ...element.data, completed: true } })
40
+ },
41
+ },
42
+ }
43
+ },
44
+ }
@@ -0,0 +1,34 @@
1
+ 'use strict'
2
+
3
+ module.exports = {
4
+ type: 'notification',
5
+ name: 'Notification',
6
+ description: 'A styled notification card with color, icon, title, and content.',
7
+ icon: 'bell',
8
+ defaultWidth: 220,
9
+ defaultHeight: 100,
10
+ expansionMode: 'inline',
11
+ tags: ['data'],
12
+ isBuiltIn: true,
13
+
14
+ createInstance(context) {
15
+ const { element } = context
16
+ return {
17
+ getViewData() {
18
+ return {
19
+ color: element.data.color || '#60a5fa',
20
+ icon: element.data.icon || '',
21
+ title: element.data.title || '',
22
+ content: element.data.content || '',
23
+ }
24
+ },
25
+ getPorts() { return [] },
26
+ actions: {
27
+ update(data = {}) {
28
+ const { updateElement } = require('../board-state.js')
29
+ updateElement(element.id, { data: { ...element.data, ...data } })
30
+ },
31
+ },
32
+ }
33
+ },
34
+ }
@@ -0,0 +1,35 @@
1
+ 'use strict'
2
+
3
+ module.exports = {
4
+ type: 'outline',
5
+ name: 'Outline',
6
+ description: 'A hierarchical outline with nested nodes.',
7
+ icon: 'list-tree',
8
+ defaultWidth: 220,
9
+ defaultHeight: 140,
10
+ expansionMode: 'popup',
11
+ tags: ['docs'],
12
+ isBuiltIn: true,
13
+ hiddenFromPalette: true,
14
+
15
+ createInstance(context) {
16
+ const { element } = context
17
+ return {
18
+ getViewData() {
19
+ return { title: element.data.title || '', nodes: element.data.nodes || [] }
20
+ },
21
+ getPorts() {
22
+ return [
23
+ { key: 'items-in', direction: 'input', dataType: 'any', label: 'Items In' },
24
+ { key: 'structure-out', direction: 'output', dataType: 'json', label: 'Structure Out' },
25
+ ]
26
+ },
27
+ actions: {
28
+ update(data = {}) {
29
+ const { updateElement } = require('../board-state.js')
30
+ updateElement(element.id, { data: { ...element.data, ...data } })
31
+ },
32
+ },
33
+ }
34
+ },
35
+ }