canvas-design-mcp 0.9.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 (118) hide show
  1. package/CLAUDE.md +200 -0
  2. package/DESIGN.md +288 -0
  3. package/PROFESSOR-INSTRUCTIONS.txt +131 -0
  4. package/README.md +250 -0
  5. package/dist/canvas-api.d.ts +24 -0
  6. package/dist/canvas-api.d.ts.map +1 -0
  7. package/dist/canvas-api.js +146 -0
  8. package/dist/canvas-api.js.map +1 -0
  9. package/dist/config.d.ts +5 -0
  10. package/dist/config.d.ts.map +1 -0
  11. package/dist/config.js +22 -0
  12. package/dist/config.js.map +1 -0
  13. package/dist/design-engine.d.ts +5 -0
  14. package/dist/design-engine.d.ts.map +1 -0
  15. package/dist/design-engine.js +23 -0
  16. package/dist/design-engine.js.map +1 -0
  17. package/dist/index.d.ts +3 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +567 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/tools/accessibility.d.ts +7 -0
  22. package/dist/tools/accessibility.d.ts.map +1 -0
  23. package/dist/tools/accessibility.js +149 -0
  24. package/dist/tools/accessibility.js.map +1 -0
  25. package/dist/tools/contrast.d.ts +2 -0
  26. package/dist/tools/contrast.d.ts.map +1 -0
  27. package/dist/tools/contrast.js +7 -0
  28. package/dist/tools/contrast.js.map +1 -0
  29. package/dist/tools/critique.d.ts +23 -0
  30. package/dist/tools/critique.d.ts.map +1 -0
  31. package/dist/tools/critique.js +194 -0
  32. package/dist/tools/critique.js.map +1 -0
  33. package/dist/tools/generate.d.ts +18 -0
  34. package/dist/tools/generate.d.ts.map +1 -0
  35. package/dist/tools/generate.js +101 -0
  36. package/dist/tools/generate.js.map +1 -0
  37. package/dist/tools/gotchas.d.ts +7 -0
  38. package/dist/tools/gotchas.d.ts.map +1 -0
  39. package/dist/tools/gotchas.js +61 -0
  40. package/dist/tools/gotchas.js.map +1 -0
  41. package/dist/tools/ingest.d.ts +44 -0
  42. package/dist/tools/ingest.d.ts.map +1 -0
  43. package/dist/tools/ingest.js +211 -0
  44. package/dist/tools/ingest.js.map +1 -0
  45. package/dist/tools/list-courses.d.ts +16 -0
  46. package/dist/tools/list-courses.d.ts.map +1 -0
  47. package/dist/tools/list-courses.js +79 -0
  48. package/dist/tools/list-courses.js.map +1 -0
  49. package/dist/tools/page-io.d.ts +19 -0
  50. package/dist/tools/page-io.d.ts.map +1 -0
  51. package/dist/tools/page-io.js +77 -0
  52. package/dist/tools/page-io.js.map +1 -0
  53. package/dist/tools/panopto.d.ts +36 -0
  54. package/dist/tools/panopto.d.ts.map +1 -0
  55. package/dist/tools/panopto.js +188 -0
  56. package/dist/tools/panopto.js.map +1 -0
  57. package/dist/tools/personas.d.ts +21 -0
  58. package/dist/tools/personas.d.ts.map +1 -0
  59. package/dist/tools/personas.js +464 -0
  60. package/dist/tools/personas.js.map +1 -0
  61. package/dist/tools/philosophy.d.ts +21 -0
  62. package/dist/tools/philosophy.d.ts.map +1 -0
  63. package/dist/tools/philosophy.js +137 -0
  64. package/dist/tools/philosophy.js.map +1 -0
  65. package/dist/tools/publish.d.ts +31 -0
  66. package/dist/tools/publish.d.ts.map +1 -0
  67. package/dist/tools/publish.js +198 -0
  68. package/dist/tools/publish.js.map +1 -0
  69. package/dist/tools/redesign.d.ts +18 -0
  70. package/dist/tools/redesign.d.ts.map +1 -0
  71. package/dist/tools/redesign.js +68 -0
  72. package/dist/tools/redesign.js.map +1 -0
  73. package/dist/tools/update-kb.d.ts +10 -0
  74. package/dist/tools/update-kb.d.ts.map +1 -0
  75. package/dist/tools/update-kb.js +93 -0
  76. package/dist/tools/update-kb.js.map +1 -0
  77. package/dist/tools/validate.d.ts +10 -0
  78. package/dist/tools/validate.d.ts.map +1 -0
  79. package/dist/tools/validate.js +76 -0
  80. package/dist/tools/validate.js.map +1 -0
  81. package/dist/types.d.ts +65 -0
  82. package/dist/types.d.ts.map +1 -0
  83. package/dist/types.js +2 -0
  84. package/dist/types.js.map +1 -0
  85. package/dist/wizard.d.ts +3 -0
  86. package/dist/wizard.d.ts.map +1 -0
  87. package/dist/wizard.js +229 -0
  88. package/dist/wizard.js.map +1 -0
  89. package/docs/canvas-design-kb/00-meta/Changelog.md +42 -0
  90. package/docs/canvas-design-kb/00-meta/Contributing.md +58 -0
  91. package/docs/canvas-design-kb/00-meta/KB-Overview.md +51 -0
  92. package/docs/canvas-design-kb/01-canvas-rce/CSS-Inline-Strategy.md +166 -0
  93. package/docs/canvas-design-kb/01-canvas-rce/Canvas-Built-In-CSS-Classes.md +243 -0
  94. package/docs/canvas-design-kb/01-canvas-rce/Canvas-Page-Types.md +59 -0
  95. package/docs/canvas-design-kb/01-canvas-rce/HTML-Allowlist.md +204 -0
  96. package/docs/canvas-design-kb/01-canvas-rce/RCE-Limitations-and-Workarounds.md +151 -0
  97. package/docs/canvas-design-kb/01-canvas-rce/RCE-Overview.md +92 -0
  98. package/docs/canvas-design-kb/02-design-md/DESIGN-MD-Canvas-Template.md +323 -0
  99. package/docs/canvas-design-kb/02-design-md/DESIGN-MD-File-Structure.md +245 -0
  100. package/docs/canvas-design-kb/02-design-md/DESIGN-MD-Overview.md +120 -0
  101. package/docs/canvas-design-kb/02-design-md/DESIGN-MD-Toolchain.md +234 -0
  102. package/docs/canvas-design-kb/03-design-systems/Color-and-Typography.md +146 -0
  103. package/docs/canvas-design-kb/03-design-systems/Component-Library.md +299 -0
  104. package/docs/canvas-design-kb/03-design-systems/Design-System-Principles.md +99 -0
  105. package/docs/canvas-design-kb/04-tools/Canvas-Theme-Editor.md +40 -0
  106. package/docs/canvas-design-kb/04-tools/Other-Canvas-Design-Tools.md +47 -0
  107. package/docs/canvas-design-kb/05-patterns/Course-Home-Page.md +224 -0
  108. package/docs/canvas-design-kb/06-accessibility/Accessibility-Overview.md +128 -0
  109. package/docs/canvas-design-kb/07-resources/Inspiration-and-Showcases.md +121 -0
  110. package/docs/canvas-design-kb/07-resources/Official-Canvas-Links.md +54 -0
  111. package/docs/canvas-design-kb/README.md +58 -0
  112. package/docs/feature-roadmap.md +123 -0
  113. package/docs/installation.md +340 -0
  114. package/package.json +45 -0
  115. package/scripts/deploy-public.ps1 +68 -0
  116. package/src/kb/design-principles.md +45 -0
  117. package/src/templates/ignite-assignment-page.html +203 -0
  118. package/src/templates/two-column-dashboard.html +33 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"philosophy.js","sourceRoot":"","sources":["../../src/tools/philosophy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { CanvasPage, CollisionAction, InstitutionConfig, ToolError } from '../types.js';
2
+ import { type AccessibilityWarning } from './accessibility.js';
3
+ export interface PublishToCanvasInput {
4
+ courseId?: number;
5
+ html: string;
6
+ pageTitle: string;
7
+ forcePublish?: boolean;
8
+ skipFerpaCheck?: boolean;
9
+ collisionAction?: CollisionAction;
10
+ relatedPageTitle?: string;
11
+ }
12
+ export interface PublishSuccess {
13
+ url: string;
14
+ action: 'created' | 'updated';
15
+ pageTitle: string;
16
+ tip: string;
17
+ accessibilityWarnings?: AccessibilityWarning[];
18
+ }
19
+ export interface PublishApi {
20
+ listPages(courseId: number): Promise<CanvasPage[]>;
21
+ createPage(courseId: number, title: string, html: string): Promise<CanvasPage>;
22
+ updatePage(courseId: number, pageUrl: string, html: string): Promise<CanvasPage>;
23
+ }
24
+ export interface FerpaFinding {
25
+ reason: string;
26
+ line: number;
27
+ }
28
+ export declare function scanFerpa(html: string, _professorEmail?: string): FerpaFinding | undefined;
29
+ export declare function titleSimilarity(leftTitle: string, rightTitle: string): number;
30
+ export declare function publishToCanvas(input: PublishToCanvasInput, config: InstitutionConfig, api: PublishApi): Promise<PublishSuccess | ToolError>;
31
+ //# sourceMappingURL=publish.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../../src/tools/publish.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAG7F,OAAO,EAAsB,KAAK,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAInF,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,SAAS,GAAG,SAAS,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,qBAAqB,CAAC,EAAE,oBAAoB,EAAE,CAAC;CAChD;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IACnD,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/E,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CAClF;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAWD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAkB1F;AA2CD,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAQ7E;AAsED,wBAAsB,eAAe,CACnC,KAAK,EAAE,oBAAoB,EAC3B,MAAM,EAAE,iBAAiB,EACzB,GAAG,EAAE,UAAU,GACd,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CA6ErC"}
@@ -0,0 +1,198 @@
1
+ import { CanvasApiError } from '../canvas-api.js';
2
+ import { ferpaGotcha, titleCollisionGotcha, tokenScopeGotcha, versionControlTip } from './gotchas.js';
3
+ import { validateCanvasHtml } from './validate.js';
4
+ import { auditAccessibility } from './accessibility.js';
5
+ const COLLISION_THRESHOLD = 0.8;
6
+ function lineForOffset(text, offset) {
7
+ return text.slice(0, offset).split(/\r?\n/).length;
8
+ }
9
+ export function scanFerpa(html, _professorEmail) {
10
+ const patterns = [
11
+ { reason: 'possible BSU student ID', pattern: /\bB\d{8}\b/i },
12
+ { reason: 'possible 9-digit student ID', pattern: /\b\d{9}\b/ },
13
+ {
14
+ reason: 'possible grade disclosure',
15
+ pattern: /\b[A-Z][a-z]+(?:\s+[A-Z][a-z]+)+[^<\n]{0,50}(?:received|scored|earned|grade(?:d)?|score(?:d)?)\b[^<\n]{0,30}\b(?:[ABCDF][+-]?|\d{1,3}%|\d{1,3}\s*\/\s*\d{1,3})(?=\s|<|$)/i,
16
+ },
17
+ ];
18
+ for (const item of patterns) {
19
+ const match = item.pattern.exec(html);
20
+ if (match?.index !== undefined) {
21
+ return { reason: item.reason, line: lineForOffset(html, match.index) };
22
+ }
23
+ }
24
+ return undefined;
25
+ }
26
+ function normalizeTitle(title) {
27
+ return title.toLowerCase().replace(/[^a-z0-9]+/g, ' ').trim();
28
+ }
29
+ function titleTokens(title) {
30
+ return new Set(normalizeTitle(title).split(/\s+/).filter(Boolean));
31
+ }
32
+ function levenshtein(left, right) {
33
+ const matrix = Array.from({ length: left.length + 1 }, () => new Array(right.length + 1).fill(0));
34
+ for (let i = 0; i <= left.length; i += 1)
35
+ matrix[i][0] = i;
36
+ for (let j = 0; j <= right.length; j += 1)
37
+ matrix[0][j] = j;
38
+ for (let i = 1; i <= left.length; i += 1) {
39
+ for (let j = 1; j <= right.length; j += 1) {
40
+ const cost = left[i - 1] === right[j - 1] ? 0 : 1;
41
+ matrix[i][j] = Math.min(matrix[i - 1][j] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j - 1] + cost);
42
+ }
43
+ }
44
+ return matrix[left.length][right.length];
45
+ }
46
+ function overlapScore(leftTokens, rightTokens) {
47
+ if (leftTokens.size === 0 && rightTokens.size === 0)
48
+ return 1;
49
+ if (leftTokens.size === 0 || rightTokens.size === 0)
50
+ return 0;
51
+ let intersection = 0;
52
+ for (const token of leftTokens) {
53
+ if (rightTokens.has(token))
54
+ intersection += 1;
55
+ }
56
+ const smallerSetSize = Math.min(leftTokens.size, rightTokens.size);
57
+ return intersection / smallerSetSize;
58
+ }
59
+ export function titleSimilarity(leftTitle, rightTitle) {
60
+ const left = normalizeTitle(leftTitle);
61
+ const right = normalizeTitle(rightTitle);
62
+ const longest = Math.max(left.length, right.length);
63
+ const editScore = longest === 0 ? 1 : 1 - levenshtein(left, right) / longest;
64
+ const tokenContainmentScore = overlapScore(titleTokens(leftTitle), titleTokens(rightTitle));
65
+ return Math.max(editScore, tokenContainmentScore);
66
+ }
67
+ function bestCollision(pages, pageTitle) {
68
+ const best = pages
69
+ .map(page => ({ page, score: titleSimilarity(page.title, pageTitle) }))
70
+ .sort((left, right) => right.score - left.score)[0];
71
+ return best && best.score >= COLLISION_THRESHOLD ? best : undefined;
72
+ }
73
+ function canvasPageUrl(page) {
74
+ return page.html_url ?? page.url;
75
+ }
76
+ function validateBeforePublish(html) {
77
+ const result = validateCanvasHtml(html);
78
+ if (result.valid)
79
+ return undefined;
80
+ return {
81
+ error: `Canvas HTML validation failed with ${result.violations.length} issue(s). Fix them or pass forcePublish: true after review.`,
82
+ code: 'VALIDATION_FAILED',
83
+ details: { violations: result.violations },
84
+ };
85
+ }
86
+ function missingTokenError(config) {
87
+ return {
88
+ error: [
89
+ 'No Canvas API token configured, so Canvas Design Studio cannot publish directly to Canvas yet.',
90
+ 'You can still generate Canvas-safe HTML and paste it into Canvas manually.',
91
+ `To enable direct publishing later, create a Canvas API token at ${config.canvasUrl.replace(/\/+$/, '')}/profile/settings and run setup_institution.`,
92
+ ].join(' '),
93
+ code: 'MISSING_API_TOKEN',
94
+ };
95
+ }
96
+ function collisionError(collision, pageTitle) {
97
+ return {
98
+ error: titleCollisionGotcha(collision.page.title, pageTitle, collision.score),
99
+ code: 'TITLE_COLLISION',
100
+ details: {
101
+ existingPage: collision.page,
102
+ newTitle: pageTitle,
103
+ score: collision.score,
104
+ options: [
105
+ 'Rerun with collisionAction: "update"',
106
+ 'Rerun with collisionAction: "create"',
107
+ 'Rerun with collisionAction: "related" and relatedPageTitle',
108
+ 'Rerun with collisionAction: "cancel"',
109
+ ],
110
+ },
111
+ };
112
+ }
113
+ function apiError(err, config) {
114
+ if (err.code === 'CANVAS_FORBIDDEN') {
115
+ return {
116
+ error: tokenScopeGotcha(config.canvasUrl),
117
+ code: err.code,
118
+ details: { status: err.status, canvas: err.details },
119
+ };
120
+ }
121
+ return {
122
+ error: err.message,
123
+ code: err.code,
124
+ details: { status: err.status, canvas: err.details },
125
+ };
126
+ }
127
+ export async function publishToCanvas(input, config, api) {
128
+ if (!config.apiToken?.trim())
129
+ return missingTokenError(config);
130
+ if (typeof input.courseId !== 'number') {
131
+ return {
132
+ error: 'Course ID is required. Run list_canvas_courses first, then pass the chosen courseId to publish_to_canvas.',
133
+ code: 'COURSE_ID_REQUIRED',
134
+ };
135
+ }
136
+ if (!input.skipFerpaCheck) {
137
+ const finding = scanFerpa(input.html, config.professorEmail);
138
+ if (finding) {
139
+ return {
140
+ error: ferpaGotcha(finding.reason, finding.line),
141
+ code: 'FERPA_REVIEW_REQUIRED',
142
+ details: { ...finding },
143
+ };
144
+ }
145
+ }
146
+ if (!input.forcePublish) {
147
+ const validationError = validateBeforePublish(input.html);
148
+ if (validationError)
149
+ return validationError;
150
+ }
151
+ const a11yWarnings = auditAccessibility(input.html);
152
+ try {
153
+ const pages = await api.listPages(input.courseId);
154
+ const collision = bestCollision(pages, input.pageTitle);
155
+ if (collision && !input.collisionAction)
156
+ return collisionError(collision, input.pageTitle);
157
+ if (input.collisionAction === 'cancel') {
158
+ return { error: 'Publishing cancelled. No Canvas page was changed.', code: 'PUBLISH_CANCELLED' };
159
+ }
160
+ if (input.collisionAction === 'update') {
161
+ if (!collision) {
162
+ return { error: 'No matching existing page was found to update.', code: 'NO_COLLISION_TO_UPDATE' };
163
+ }
164
+ const updated = await api.updatePage(input.courseId, collision.page.url, input.html);
165
+ return {
166
+ url: canvasPageUrl(updated),
167
+ action: 'updated',
168
+ pageTitle: updated.title,
169
+ tip: versionControlTip(),
170
+ ...(a11yWarnings.length > 0 && { accessibilityWarnings: a11yWarnings }),
171
+ };
172
+ }
173
+ const title = input.collisionAction === 'related' ? input.relatedPageTitle?.trim() : input.pageTitle;
174
+ if (input.collisionAction === 'related' && !title) {
175
+ return {
176
+ error: 'relatedPageTitle is required when collisionAction is "related".',
177
+ code: 'RELATED_TITLE_REQUIRED',
178
+ };
179
+ }
180
+ const created = await api.createPage(input.courseId, title ?? input.pageTitle, input.html);
181
+ return {
182
+ url: canvasPageUrl(created),
183
+ action: 'created',
184
+ pageTitle: created.title,
185
+ tip: versionControlTip(),
186
+ ...(a11yWarnings.length > 0 && { accessibilityWarnings: a11yWarnings }),
187
+ };
188
+ }
189
+ catch (err) {
190
+ if (err instanceof CanvasApiError)
191
+ return apiError(err, config);
192
+ return {
193
+ error: err instanceof Error ? err.message : String(err),
194
+ code: 'PUBLISH_FAILED',
195
+ };
196
+ }
197
+ }
198
+ //# sourceMappingURL=publish.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publish.js","sourceRoot":"","sources":["../../src/tools/publish.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { type AccessibilityWarning } from './accessibility.js';
2
+ import type { CritiqueFinding } from './critique.js';
3
+ export interface RedesignInput {
4
+ html: string;
5
+ findings: CritiqueFinding[];
6
+ mode?: 'quick' | 'comprehensive';
7
+ pageType?: string;
8
+ primaryGoal?: string;
9
+ }
10
+ export interface RedesignResult {
11
+ html: string;
12
+ appliedFixes: string[];
13
+ skippedFindings: string[];
14
+ accessibilityWarnings?: AccessibilityWarning[];
15
+ kbContext?: string;
16
+ }
17
+ export declare function redesignCanvasPage(input: RedesignInput): RedesignResult;
18
+ //# sourceMappingURL=redesign.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redesign.d.ts","sourceRoot":"","sources":["../../src/tools/redesign.ts"],"names":[],"mappings":"AAGA,OAAO,EAAsB,KAAK,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AACnF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,IAAI,CAAC,EAAE,OAAO,GAAG,eAAe,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,qBAAqB,CAAC,EAAE,oBAAoB,EAAE,CAAC;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA8BD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,aAAa,GAAG,cAAc,CAwCvE"}
@@ -0,0 +1,68 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { fileURLToPath } from 'node:url';
3
+ import { dirname, join } from 'node:path';
4
+ import { auditAccessibility } from './accessibility.js';
5
+ function fixFontFloor(html) {
6
+ let fixed = false;
7
+ const result = html.replace(/font-size:\s*(\d+(?:\.\d+)?)px/gi, (match, size) => {
8
+ if (parseFloat(size) < 13) {
9
+ fixed = true;
10
+ return 'font-size:13px';
11
+ }
12
+ return match;
13
+ });
14
+ return { html: result, fixed };
15
+ }
16
+ function fixHeroUrl(html) {
17
+ if (!html.includes('HERO_IMAGE_URL'))
18
+ return { html, fixed: false };
19
+ const comment = '<!-- Replace HERO_IMAGE_URL with your hosted image URL (1200×400px) -->';
20
+ const result = html.replace(/(<img[^>]*src="HERO_IMAGE_URL"[^>]*>)/i, `${comment}$1`);
21
+ return { html: result, fixed: true };
22
+ }
23
+ function loadKb() {
24
+ const __dirname = dirname(fileURLToPath(import.meta.url));
25
+ try {
26
+ return readFileSync(join(__dirname, '../../src/kb/design-principles.md'), 'utf-8');
27
+ }
28
+ catch {
29
+ return '';
30
+ }
31
+ }
32
+ export function redesignCanvasPage(input) {
33
+ const { findings, mode = 'quick' } = input;
34
+ let { html } = input;
35
+ const appliedFixes = [];
36
+ const skippedFindings = [];
37
+ const fontResult = fixFontFloor(html);
38
+ if (fontResult.fixed) {
39
+ html = fontResult.html;
40
+ appliedFixes.push('Bumped all font sizes below 13px to 13px');
41
+ }
42
+ const heroResult = fixHeroUrl(html);
43
+ if (heroResult.fixed) {
44
+ html = heroResult.html;
45
+ appliedFixes.push('Added comment to replace HERO_IMAGE_URL with hosted image URL');
46
+ }
47
+ for (const finding of findings) {
48
+ const isHeroFinding = finding.area === 'completeness' && finding.issue.includes('placeholder');
49
+ const isFontFinding = finding.area === 'typography';
50
+ if ((isHeroFinding && heroResult.fixed) || (isFontFinding && fontResult.fixed))
51
+ continue;
52
+ skippedFindings.push(finding.suggestion);
53
+ }
54
+ const a11y = auditAccessibility(html);
55
+ const result = {
56
+ html,
57
+ appliedFixes,
58
+ skippedFindings,
59
+ ...(a11y.length > 0 && { accessibilityWarnings: a11y }),
60
+ };
61
+ if (mode === 'comprehensive') {
62
+ const kb = loadKb();
63
+ if (kb)
64
+ result.kbContext = kb;
65
+ }
66
+ return result;
67
+ }
68
+ //# sourceMappingURL=redesign.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redesign.js","sourceRoot":"","sources":["../../src/tools/redesign.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export interface KbUpdateResult {
2
+ updated: boolean;
3
+ changes: string[];
4
+ lastChecked: string;
5
+ cssPropsCount: number;
6
+ htmlTagsCount: number;
7
+ parseWarning?: string;
8
+ }
9
+ export declare function updateCanvasKb(force?: boolean): Promise<KbUpdateResult>;
10
+ //# sourceMappingURL=update-kb.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-kb.d.ts","sourceRoot":"","sources":["../../src/tools/update-kb.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AA0CD,wBAAsB,cAAc,CAAC,KAAK,UAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,CAuE3E"}
@@ -0,0 +1,93 @@
1
+ import { homedir } from 'os';
2
+ import { join } from 'path';
3
+ import { mkdirSync, readFileSync, writeFileSync, existsSync } from 'fs';
4
+ const KB_DIR = join(homedir(), '.canvas-design-mcp', 'kb');
5
+ const CANVAS_SANITIZE_URL = 'https://raw.githubusercontent.com/instructure/canvas-lms/master/gems/canvas_sanitize/lib/canvas_sanitize/canvas_sanitize.rb';
6
+ function parseRubyPercentW(source, varPattern) {
7
+ const m = source.match(varPattern);
8
+ if (!m)
9
+ return [];
10
+ return m[1].trim().split(/\s+/).filter(Boolean);
11
+ }
12
+ function parseCanvasSanitize(source) {
13
+ // Canvas uses %w[...] arrays for allowed elements and CSS properties
14
+ const cssProps = parseRubyPercentW(source, /ALLOWED_CSS_PROPERTIES\s*=\s*%w\[([\s\S]*?)\]/);
15
+ const htmlTagsA = parseRubyPercentW(source, /ALLOWED_ELEMENTS\s*=\s*%w\[([\s\S]*?)\]/);
16
+ // Fallback: some versions use ALLOWED_TAGS
17
+ const htmlTagsB = htmlTagsA.length === 0
18
+ ? parseRubyPercentW(source, /ALLOWED_TAGS\s*=\s*%w\[([\s\S]*?)\]/)
19
+ : htmlTagsA;
20
+ const parseWarning = cssProps.length === 0 && htmlTagsB.length === 0
21
+ ? 'Could not parse allowlist arrays — Canvas source format may have changed. KB not updated.'
22
+ : undefined;
23
+ return { cssProps, htmlTags: htmlTagsB, parseWarning };
24
+ }
25
+ function diff(prev, next) {
26
+ return {
27
+ added: next.filter(x => !prev.includes(x)),
28
+ removed: prev.filter(x => !next.includes(x)),
29
+ };
30
+ }
31
+ export async function updateCanvasKb(force = false) {
32
+ mkdirSync(KB_DIR, { recursive: true });
33
+ const metaPath = join(KB_DIR, 'meta.json');
34
+ const allowlistPath = join(KB_DIR, 'allowlist.json');
35
+ const meta = existsSync(metaPath)
36
+ ? JSON.parse(readFileSync(metaPath, 'utf-8'))
37
+ : { lastChecked: '' };
38
+ if (!force && meta.lastChecked) {
39
+ const ageMs = Date.now() - new Date(meta.lastChecked).getTime();
40
+ if (ageMs < 24 * 60 * 60 * 1000) {
41
+ const prev = existsSync(allowlistPath)
42
+ ? JSON.parse(readFileSync(allowlistPath, 'utf-8'))
43
+ : { cssProps: [], htmlTags: [], fetchedAt: '' };
44
+ return {
45
+ updated: false,
46
+ changes: [],
47
+ lastChecked: meta.lastChecked,
48
+ cssPropsCount: prev.cssProps.length,
49
+ htmlTagsCount: prev.htmlTags.length,
50
+ };
51
+ }
52
+ }
53
+ const response = await fetch(CANVAS_SANITIZE_URL, {
54
+ headers: { 'User-Agent': 'canvas-design-mcp/0.1.0' },
55
+ });
56
+ if (!response.ok) {
57
+ throw new Error(`Failed to fetch Canvas sanitizer source: HTTP ${response.status}`);
58
+ }
59
+ const source = await response.text();
60
+ const { cssProps, htmlTags, parseWarning } = parseCanvasSanitize(source);
61
+ if (parseWarning) {
62
+ return {
63
+ updated: false,
64
+ changes: [],
65
+ lastChecked: meta.lastChecked || 'never',
66
+ cssPropsCount: 0,
67
+ htmlTagsCount: 0,
68
+ parseWarning,
69
+ };
70
+ }
71
+ const prev = existsSync(allowlistPath)
72
+ ? JSON.parse(readFileSync(allowlistPath, 'utf-8'))
73
+ : { cssProps: [], htmlTags: [], fetchedAt: '' };
74
+ const cssDiff = diff(prev.cssProps, cssProps);
75
+ const tagDiff = diff(prev.htmlTags, htmlTags);
76
+ const changes = [
77
+ ...cssDiff.added.map(p => `+ CSS: ${p}`),
78
+ ...cssDiff.removed.map(p => `- CSS: ${p}`),
79
+ ...tagDiff.added.map(t => `+ tag: <${t}>`),
80
+ ...tagDiff.removed.map(t => `- tag: <${t}>`),
81
+ ];
82
+ const now = new Date().toISOString();
83
+ writeFileSync(allowlistPath, JSON.stringify({ cssProps, htmlTags, fetchedAt: now }, null, 2));
84
+ writeFileSync(metaPath, JSON.stringify({ lastChecked: now }, null, 2));
85
+ return {
86
+ updated: true,
87
+ changes,
88
+ lastChecked: now,
89
+ cssPropsCount: cssProps.length,
90
+ htmlTagsCount: htmlTags.length,
91
+ };
92
+ }
93
+ //# sourceMappingURL=update-kb.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-kb.js","sourceRoot":"","sources":["../../src/tools/update-kb.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export interface ValidationViolation {
2
+ rule: string;
3
+ context: string;
4
+ }
5
+ export interface ValidationResult {
6
+ valid: boolean;
7
+ violations: ValidationViolation[];
8
+ }
9
+ export declare function validateCanvasHtml(html: string): ValidationResult;
10
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/tools/validate.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,mBAAmB,EAAE,CAAC;CACnC;AAuDD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAyBjE"}
@@ -0,0 +1,76 @@
1
+ const RULES = [
2
+ {
3
+ name: 'no-style-block',
4
+ pattern: /<style[\s>]/i,
5
+ message: 'No <style> blocks — all CSS must be inline style="" attributes',
6
+ },
7
+ {
8
+ name: 'no-script-tag',
9
+ pattern: /<script[\s>]/i,
10
+ message: 'No <script> tags — JavaScript is not allowed in Canvas RCE',
11
+ },
12
+ {
13
+ name: 'no-box-shadow',
14
+ pattern: /box-shadow\s*:/i,
15
+ message: 'No box-shadow — stripped by Canvas sanitizer',
16
+ },
17
+ {
18
+ name: 'no-gap-property',
19
+ pattern: /(?:^|;|\s)gap\s*:/i,
20
+ message: 'No gap property in flex/grid — use margin on children instead',
21
+ },
22
+ {
23
+ name: 'no-opacity-property',
24
+ pattern: /(?:^|[;"\s])opacity\s*:/i,
25
+ message: 'No opacity property — use rgba() color values instead',
26
+ },
27
+ {
28
+ name: 'no-filter',
29
+ pattern: /(?:^|;|\s)filter\s*:/i,
30
+ message: 'No filter property — stripped by Canvas sanitizer',
31
+ },
32
+ {
33
+ name: 'no-transform',
34
+ pattern: /(?<![a-z-])transform\s*:/i,
35
+ message: 'No transform property — stripped by Canvas sanitizer (text-transform is allowed)',
36
+ },
37
+ {
38
+ name: 'no-transition',
39
+ pattern: /(?:^|;|\s)transition\s*:/i,
40
+ message: 'No transition property — stripped by Canvas sanitizer',
41
+ },
42
+ {
43
+ name: 'no-animation',
44
+ pattern: /(?:^|;|\s)animation\s*:/i,
45
+ message: 'No animation property — stripped by Canvas sanitizer',
46
+ },
47
+ {
48
+ name: 'no-h1',
49
+ pattern: /<h1[\s>]/i,
50
+ message: 'No <h1> tags — Canvas reserves H1 for the page title',
51
+ },
52
+ ];
53
+ export function validateCanvasHtml(html) {
54
+ const violations = [];
55
+ const stripped = html.replace(/<!--[\s\S]*?-->/g, '');
56
+ for (const rule of RULES) {
57
+ const match = stripped.match(rule.pattern);
58
+ if (match) {
59
+ violations.push({
60
+ rule: rule.message,
61
+ context: match[0].trim(),
62
+ });
63
+ }
64
+ }
65
+ const imgTags = stripped.match(/<img[^>]*>/gi) ?? [];
66
+ for (const img of imgTags) {
67
+ if (!/alt\s*=/i.test(img)) {
68
+ violations.push({
69
+ rule: 'All <img> tags must have an alt="" attribute',
70
+ context: img.substring(0, 60) + '...',
71
+ });
72
+ }
73
+ }
74
+ return { valid: violations.length === 0, violations };
75
+ }
76
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/tools/validate.ts"],"names":[],"mappings":"AAUA,MAAM,KAAK,GAA8D;IACvE;QACE,IAAI,EAAE,gBAAgB;QACtB,OAAO,EAAE,cAAc;QACvB,OAAO,EAAE,gEAAgE;KAC1E;IACD;QACE,IAAI,EAAE,eAAe;QACrB,OAAO,EAAE,eAAe;QACxB,OAAO,EAAE,4DAA4D;KACtE;IACD;QACE,IAAI,EAAE,eAAe;QACrB,OAAO,EAAE,iBAAiB;QAC1B,OAAO,EAAE,8CAA8C;KACxD;IACD;QACE,IAAI,EAAE,iBAAiB;QACvB,OAAO,EAAE,oBAAoB;QAC7B,OAAO,EAAE,+DAA+D;KACzE;IACD;QACE,IAAI,EAAE,qBAAqB;QAC3B,OAAO,EAAE,0BAA0B;QACnC,OAAO,EAAE,uDAAuD;KACjE;IACD;QACE,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,uBAAuB;QAChC,OAAO,EAAE,mDAAmD;KAC7D;IACD;QACE,IAAI,EAAE,cAAc;QACpB,OAAO,EAAE,2BAA2B;QACpC,OAAO,EAAE,kFAAkF;KAC5F;IACD;QACE,IAAI,EAAE,eAAe;QACrB,OAAO,EAAE,2BAA2B;QACpC,OAAO,EAAE,uDAAuD;KACjE;IACD;QACE,IAAI,EAAE,cAAc;QACpB,OAAO,EAAE,0BAA0B;QACnC,OAAO,EAAE,sDAAsD;KAChE;IACD;QACE,IAAI,EAAE,OAAO;QACb,OAAO,EAAE,WAAW;QACpB,OAAO,EAAE,sDAAsD;KAChE;CACF,CAAC;AAEF,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,MAAM,UAAU,GAA0B,EAAE,CAAC;IAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;IAEtD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC3C,IAAI,KAAK,EAAE,CAAC;YACV,UAAU,CAAC,IAAI,CAAC;gBACd,IAAI,EAAE,IAAI,CAAC,OAAO;gBAClB,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;aACzB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;IACrD,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,UAAU,CAAC,IAAI,CAAC;gBACd,IAAI,EAAE,8CAA8C;gBACpD,OAAO,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK;aACtC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;AACxD,CAAC"}
@@ -0,0 +1,65 @@
1
+ export interface InstitutionColors {
2
+ primary: string;
3
+ primaryDark: string;
4
+ primaryLight: string;
5
+ secondary: string;
6
+ }
7
+ export interface PanoptoConfig {
8
+ domain: string;
9
+ iframeWhitelisted: boolean | null;
10
+ clientId?: string;
11
+ clientSecret?: string;
12
+ }
13
+ export interface InstitutionConfig {
14
+ institution: string;
15
+ colors: InstitutionColors;
16
+ canvasUrl: string;
17
+ apiToken?: string;
18
+ professorEmail?: string;
19
+ favoriteCourses?: number[];
20
+ kbTipShown?: boolean;
21
+ panopto?: PanoptoConfig;
22
+ }
23
+ export interface CanvasEnrollment {
24
+ type?: string;
25
+ role?: string;
26
+ role_id?: number;
27
+ user_id?: number;
28
+ enrollment_state?: string;
29
+ }
30
+ export interface CanvasCourse {
31
+ id: number;
32
+ name: string;
33
+ course_code?: string;
34
+ nickname?: string;
35
+ friendly_name?: string;
36
+ workflow_state?: string;
37
+ start_at?: string | null;
38
+ end_at?: string | null;
39
+ enrollments?: CanvasEnrollment[];
40
+ total_students?: number;
41
+ teachers?: Array<{
42
+ id?: number;
43
+ display_name?: string;
44
+ name?: string;
45
+ }>;
46
+ term?: {
47
+ id?: number;
48
+ name?: string;
49
+ };
50
+ }
51
+ export interface CanvasPage {
52
+ title: string;
53
+ url: string;
54
+ html_url?: string;
55
+ published?: boolean;
56
+ updated_at?: string;
57
+ }
58
+ export interface ToolError {
59
+ error: string;
60
+ code: string;
61
+ details?: Record<string, unknown>;
62
+ }
63
+ export type SemesterFilter = 'current' | 'future' | 'past' | 'all';
64
+ export type CollisionAction = 'update' | 'create' | 'related' | 'cancel';
65
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,EAAE,OAAO,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,WAAW,CAAC,EAAE,gBAAgB,EAAE,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxE,IAAI,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACvC;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,CAAC;AACnE,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC"}