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.
- package/CLAUDE.md +200 -0
- package/DESIGN.md +288 -0
- package/PROFESSOR-INSTRUCTIONS.txt +131 -0
- package/README.md +250 -0
- package/dist/canvas-api.d.ts +24 -0
- package/dist/canvas-api.d.ts.map +1 -0
- package/dist/canvas-api.js +146 -0
- package/dist/canvas-api.js.map +1 -0
- package/dist/config.d.ts +5 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +22 -0
- package/dist/config.js.map +1 -0
- package/dist/design-engine.d.ts +5 -0
- package/dist/design-engine.d.ts.map +1 -0
- package/dist/design-engine.js +23 -0
- package/dist/design-engine.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +567 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/accessibility.d.ts +7 -0
- package/dist/tools/accessibility.d.ts.map +1 -0
- package/dist/tools/accessibility.js +149 -0
- package/dist/tools/accessibility.js.map +1 -0
- package/dist/tools/contrast.d.ts +2 -0
- package/dist/tools/contrast.d.ts.map +1 -0
- package/dist/tools/contrast.js +7 -0
- package/dist/tools/contrast.js.map +1 -0
- package/dist/tools/critique.d.ts +23 -0
- package/dist/tools/critique.d.ts.map +1 -0
- package/dist/tools/critique.js +194 -0
- package/dist/tools/critique.js.map +1 -0
- package/dist/tools/generate.d.ts +18 -0
- package/dist/tools/generate.d.ts.map +1 -0
- package/dist/tools/generate.js +101 -0
- package/dist/tools/generate.js.map +1 -0
- package/dist/tools/gotchas.d.ts +7 -0
- package/dist/tools/gotchas.d.ts.map +1 -0
- package/dist/tools/gotchas.js +61 -0
- package/dist/tools/gotchas.js.map +1 -0
- package/dist/tools/ingest.d.ts +44 -0
- package/dist/tools/ingest.d.ts.map +1 -0
- package/dist/tools/ingest.js +211 -0
- package/dist/tools/ingest.js.map +1 -0
- package/dist/tools/list-courses.d.ts +16 -0
- package/dist/tools/list-courses.d.ts.map +1 -0
- package/dist/tools/list-courses.js +79 -0
- package/dist/tools/list-courses.js.map +1 -0
- package/dist/tools/page-io.d.ts +19 -0
- package/dist/tools/page-io.d.ts.map +1 -0
- package/dist/tools/page-io.js +77 -0
- package/dist/tools/page-io.js.map +1 -0
- package/dist/tools/panopto.d.ts +36 -0
- package/dist/tools/panopto.d.ts.map +1 -0
- package/dist/tools/panopto.js +188 -0
- package/dist/tools/panopto.js.map +1 -0
- package/dist/tools/personas.d.ts +21 -0
- package/dist/tools/personas.d.ts.map +1 -0
- package/dist/tools/personas.js +464 -0
- package/dist/tools/personas.js.map +1 -0
- package/dist/tools/philosophy.d.ts +21 -0
- package/dist/tools/philosophy.d.ts.map +1 -0
- package/dist/tools/philosophy.js +137 -0
- package/dist/tools/philosophy.js.map +1 -0
- package/dist/tools/publish.d.ts +31 -0
- package/dist/tools/publish.d.ts.map +1 -0
- package/dist/tools/publish.js +198 -0
- package/dist/tools/publish.js.map +1 -0
- package/dist/tools/redesign.d.ts +18 -0
- package/dist/tools/redesign.d.ts.map +1 -0
- package/dist/tools/redesign.js +68 -0
- package/dist/tools/redesign.js.map +1 -0
- package/dist/tools/update-kb.d.ts +10 -0
- package/dist/tools/update-kb.d.ts.map +1 -0
- package/dist/tools/update-kb.js +93 -0
- package/dist/tools/update-kb.js.map +1 -0
- package/dist/tools/validate.d.ts +10 -0
- package/dist/tools/validate.d.ts.map +1 -0
- package/dist/tools/validate.js +76 -0
- package/dist/tools/validate.js.map +1 -0
- package/dist/types.d.ts +65 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/wizard.d.ts +3 -0
- package/dist/wizard.d.ts.map +1 -0
- package/dist/wizard.js +229 -0
- package/dist/wizard.js.map +1 -0
- package/docs/canvas-design-kb/00-meta/Changelog.md +42 -0
- package/docs/canvas-design-kb/00-meta/Contributing.md +58 -0
- package/docs/canvas-design-kb/00-meta/KB-Overview.md +51 -0
- package/docs/canvas-design-kb/01-canvas-rce/CSS-Inline-Strategy.md +166 -0
- package/docs/canvas-design-kb/01-canvas-rce/Canvas-Built-In-CSS-Classes.md +243 -0
- package/docs/canvas-design-kb/01-canvas-rce/Canvas-Page-Types.md +59 -0
- package/docs/canvas-design-kb/01-canvas-rce/HTML-Allowlist.md +204 -0
- package/docs/canvas-design-kb/01-canvas-rce/RCE-Limitations-and-Workarounds.md +151 -0
- package/docs/canvas-design-kb/01-canvas-rce/RCE-Overview.md +92 -0
- package/docs/canvas-design-kb/02-design-md/DESIGN-MD-Canvas-Template.md +323 -0
- package/docs/canvas-design-kb/02-design-md/DESIGN-MD-File-Structure.md +245 -0
- package/docs/canvas-design-kb/02-design-md/DESIGN-MD-Overview.md +120 -0
- package/docs/canvas-design-kb/02-design-md/DESIGN-MD-Toolchain.md +234 -0
- package/docs/canvas-design-kb/03-design-systems/Color-and-Typography.md +146 -0
- package/docs/canvas-design-kb/03-design-systems/Component-Library.md +299 -0
- package/docs/canvas-design-kb/03-design-systems/Design-System-Principles.md +99 -0
- package/docs/canvas-design-kb/04-tools/Canvas-Theme-Editor.md +40 -0
- package/docs/canvas-design-kb/04-tools/Other-Canvas-Design-Tools.md +47 -0
- package/docs/canvas-design-kb/05-patterns/Course-Home-Page.md +224 -0
- package/docs/canvas-design-kb/06-accessibility/Accessibility-Overview.md +128 -0
- package/docs/canvas-design-kb/07-resources/Inspiration-and-Showcases.md +121 -0
- package/docs/canvas-design-kb/07-resources/Official-Canvas-Links.md +54 -0
- package/docs/canvas-design-kb/README.md +58 -0
- package/docs/feature-roadmap.md +123 -0
- package/docs/installation.md +340 -0
- package/package.json +45 -0
- package/scripts/deploy-public.ps1 +68 -0
- package/src/kb/design-principles.md +45 -0
- package/src/templates/ignite-assignment-page.html +203 -0
- 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"}
|
package/dist/types.d.ts
ADDED
|
@@ -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"}
|