diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 00000000..d67ef8a3 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,110 @@ +## Project Context + +Message Header Analyzer (MHA) - Outlook add-in for analyzing email headers. See README.md for full feature list and usage. + +**Development Workflow:** +- User runs `npm run dev-server` for local development - don't ask to build unless there are errors +- Webpack handles compilation and hot reload automatically +- Test in Outlook Desktop with Debug tasks when needed + +## Development Principles + +- **Zero tolerance for errors AND warnings**: ALL errors and warnings from ANY source (build, TypeScript, ESLint, tests) must be fixed - warnings are errors, don't introduce problems +- **Stop on unexpected errors**: Present options instead of chasing fixes wildly +- **Check in frequently**: Commit often for incremental testing +- **Minimal dependencies**: Discuss before adding new packages +- **No legacy code**: Remove old code completely when updating +- **No process comments**: Don't add "Phase 1", "TODO", or development process comments +- **Keep README current**: When adding features/commands/options, update README.md in the same change +- **Don't create markdown files**: Never create new .md files (README, TODO, etc.) without being asked +- **Let user commit**: Don't auto-commit changes - present what's done and let user commit when ready +- **Complete all planned work**: When outlining a multi-step plan, implement ALL parts - don't stop partway and declare "good enough". Only the user decides when work is complete. +- **Don't duplicate functionality**: Reuse existing code when possible - if unsure about options, ask first +- **Fix everything you find**: When user says "fix X", fix ALL instances of X everywhere. If you find related issues while fixing, fix those too. Don't say "this is for later" - either fix it now or explicitly offer to add it to TODO.md. The user will tell you if something should be deferred. +- **No deferring without permission**: Never skip fixing something because it seems hard or time-consuming. If you think something should be deferred, explicitly ask: "Should I add this to TODO.md or fix it now?" + +## Code Standards + +### TypeScript +- **Pure TypeScript**: No JavaScript files - everything must be .ts +- **Pure ES Modules**: `"type": "module"` in package.json, no CommonJS +- Strict mode with all safety flags enabled +- No implicit `any` +- **Never use `unknown` type** - use proper types or type assertions +- No unsafe operations +- Explicit return types required +- Use type assertions (`as Type`) when unavoidable + +### HTML/CSS +- **No inline code or styles**: Keep JavaScript and CSS in separate files +- Use external TypeScript modules compiled to ES modules +- Use external CSS files linked in HTML + +### ESLint +- Max complexity: 15 (only ignore for legitimately complex functions) +- Explicit function return types required +- No `any` types +- K&R brace style +- **No trailing whitespace**: ESLint will fail on any trailing spaces or extra blank lines - never add them +- Fix issues properly - **never disable rules to bypass problems** + +### Error Handling +- Let errors propagate naturally with typed errors +- Use try/catch only when required by dependencies +- Provide meaningful error messages + +## Common Patterns + +### Type Assertions (when unavoidable) +```typescript +const data = JSON.parse(jsonString) as SomeType[]; +const element = document.getElementById('id') as HTMLElement; +``` + +### Complexity Exceptions (use sparingly) +```typescript +// eslint-disable-next-line complexity +async function legitimatelyComplexFunction() { } +``` + +## Anti-Patterns to Avoid + +❌ Disabling ESLint rules to bypass problems +❌ Using `any` or `unknown` without type assertions +❌ Adding TODO/Phase comments in code +❌ Keeping deprecated/legacy code +❌ Guessing at fixes - stop and ask instead + +## Testing Requirements + +- All tests must pass before committing +- Test failures are NEVER expected - if tests fail, fix the code or fix the tests +- Run `npm test` after any code changes +- Run `npm run lint` to verify no ESLint errors + +### TDD Approach for Bug Fixes + +When fixing bugs identified in code reviews or reported issues: + +1. **Write failing test first**: Create a test that demonstrates the bug - it MUST FAIL with the current buggy code +2. **Apply the fix**: Implement the code changes to address the bug +3. **Verify test passes**: Run the test again - it should now PASS with the fixed code +4. **If test still fails**: Reconsider whether the fix is incorrect OR the test is incorrect +5. **If test was incorrect**: Fix the test, then retest with OLD buggy code to verify it fails there, then retest with NEW fixed code to verify it passes + +This approach ensures: +- The bug actually exists and is reproducible +- The fix actually solves the problem +- We have regression protection going forward + +## Workflow for Changes + +**Development:** +- User runs `npm run dev-server` - don't ask to start it +- Webpack dev server handles compilation and hot reload +- Make code changes and test in browser/Outlook + +**Testing:** +- `npm test` - Run tests +- `npm run lint` - ESLint check +- `npm run lint:fix` - Auto-fix ESLint issues diff --git a/package-lock.json b/package-lock.json index b8839c34..148270fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1230,37 +1230,6 @@ "node": ">=14.17.0" } }, - "node_modules/@emnapi/core": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.6.0.tgz", - "integrity": "sha512-zq/ay+9fNIJJtJiZxdTnXS20PllcYMX3OE23ESc4HK/bdYu3cOWYVhsOhVnXALfU/uqJIxn5NBPd9z4v+SfoSg==", - "dev": true, - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.1.0", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.6.0.tgz", - "integrity": "sha512-obtUmAHTMjll499P+D9A3axeJFlhdjOWdKUNs/U6QIGT7V5RjcUW1xToAzjvmgTSQhDbYn/NwfTRoJcQ2rNBxA==", - "dev": true, - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", - "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", - "dev": true, - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.0", "dev": true, @@ -3642,18 +3611,6 @@ "node": ">=4" } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", - "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", - "dev": true, - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.10.0" - } - }, "node_modules/@nevware21/ts-async": { "version": "0.5.4", "license": "MIT", @@ -3768,16 +3725,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", - "dev": true, - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@types/babel__core": { "version": "7.20.5", "dev": true, @@ -4396,230 +4343,6 @@ "dev": true, "license": "ISC" }, - "node_modules/@unrs/resolver-binding-android-arm-eabi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", - "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-android-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", - "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", - "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", - "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", - "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", - "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", - "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", - "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", - "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", - "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", - "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", - "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", - "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", - "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", - "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", - "cpu": [ - "wasm32" - ], - "dev": true, - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.11" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", - "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", - "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@unrs/resolver-binding-win32-x64-msvc": { "version": "1.11.1", "cpu": [ @@ -8176,21 +7899,6 @@ "dev": true, "license": "ISC" }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "dev": true, @@ -15286,19 +14994,6 @@ "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, - "node_modules/unrs-resolver/node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", - "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, "node_modules/update-browserslist-db": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", diff --git a/src/Content/classicDesktopFrame.css b/src/Content/classicDesktopFrame.css index cbc497d6..424f3e55 100644 --- a/src/Content/classicDesktopFrame.css +++ b/src/Content/classicDesktopFrame.css @@ -1,5 +1,6 @@ @import url("themeColors.css"); @import url("Office.css"); +@import url("rulesCommon.css"); body { padding: 0; @@ -256,7 +257,7 @@ button[aria-expanded="false"] .collapsibleSwitch::after { color: #E44D4D; /* A nice red */ } -/* High contrast support for error styling - modern and legacy combined */ +/* High contrast support for error styling */ @media (prefers-contrast: high), (forced-colors: active), screen and (-ms-high-contrast: active) { .negativeCell { color: CanvasText !important; @@ -339,3 +340,68 @@ button[aria-expanded="false"] .collapsibleSwitch::after { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4); } } + +/* Diagnostics section - matches classic table style */ +#diagnosticsContent { + border: 1px solid black; + background-color: #FFFFFF; + margin-bottom: 20px; +} + +.diagnostic-group { + border-bottom: 1px solid #CDE6F7; + padding: 8px 12px; +} + +.diagnostic-group:last-child { + border-bottom: none; +} + +/* Classic-specific overrides for violation card header */ +.violation-card-header { + padding: 4px 0; + font-weight: bold; +} + +.diagnostic-violations { + margin-top: 8px; + padding-left: 20px; +} + +/* Classic-specific overrides for violation card */ +.violation-card { + padding: 6px 0; + margin-bottom: 6px; + padding-left: 8px; +} + +/* Classic-specific overrides for violation details */ +.violation-details { + margin-top: 4px; + padding-left: 20px; + padding-bottom: 0px; +} + +.violation-rule { + padding-bottom: 0px; +} + +.violation-parent-message { + padding-top: 4px; +} + +/* Classic-specific badge styling - smaller and more subtle */ +#summary .severity-badge, +#forefrontAntiSpamReport .severity-badge, +#antiSpamReport .severity-badge, +#otherHeaders .severity-badge { + font-size: 9px; + padding: 1px 4px; + margin-left: 6px; + vertical-align: middle; +} + +/* Ensure summary table values work well with inline violations */ +.summaryList td { + line-height: 1.4; +} diff --git a/src/Content/fluentCommon.css b/src/Content/fluentCommon.css index 692671a1..b924837f 100644 --- a/src/Content/fluentCommon.css +++ b/src/Content/fluentCommon.css @@ -163,7 +163,7 @@ fluent-button[appearance="subtle"]:is(:hover, .is-active):has(.button-label:not( content: "\2715"; /* Unicode ✕ multiplication X */ } -/* High contrast mode support - modern and legacy combined */ +/* High contrast mode support */ @media (prefers-contrast: high), (forced-colors: active), screen and (-ms-high-contrast: active) { .fluent-icon { color: ButtonText; diff --git a/src/Content/newDesktopFrame.css b/src/Content/newDesktopFrame.css index 92d0bbd0..c845a2a0 100644 --- a/src/Content/newDesktopFrame.css +++ b/src/Content/newDesktopFrame.css @@ -1,5 +1,6 @@ /* Import shared Fluent UI styles */ @import url("fluentCommon.css"); +@import url("rulesCommon.css"); .content-wrap { background: var(--white); @@ -317,4 +318,111 @@ word-break: break-word; font-size: 12px; font-family: var(--font-family); +} + +/* Desktop-specific overrides for violation card header */ +.violation-card-header { + align-items: flex-start; +} + +.violation-message { + padding: 4px; +} + +/* Inline mode - compact display for Summary headers */ +.violation-inline { + display: flex; + font-size: 0.75rem; + font-weight: 400; + margin-top: 4px; +} + +/* Card mode - full display for popovers and accordions */ +.violation-card { + display: flex; + font-size: 0.75rem; + font-weight: 400; + gap: 12px; + border-radius: 8px; + transition: all 0.2s ease; + flex-direction: column; + border: 1px solid var(--border-light-gray); +} + +.violation-card:hover { + border-color: var(--border-gray); +} + +.violation-card:last-child { + margin-bottom: 0; +} + +/* Desktop-specific: show violation details in cards */ +.violation-card .violation-details { + display: block; + padding: 0px; +} + +.diagnostic-content { + padding: 12px; + font-size: 13px; + line-height: 1.4; +} + +.diagnostics-accordion { + border-radius: 6px; + border: 1px solid var(--border-gray); +} + +.popover-row .cell-content-wrapper { + display: flex; + align-items: flex-start; + gap: 8px; + position: relative; +} + +.popover-row .cell-main-content { + flex: 1; + line-height: 1.4; +} + +.show-diagnostics-popover-btn { + border-radius: 50% !important; +} + +.show-diagnostics-popover-btn:not([data-severity]) { + display: none; +} + +.show-diagnostics-popover-btn:focus-visible { + outline: 2px solid var(--accent-color); + outline-offset: 2px; +} + +.show-diagnostics-popover-btn:hover { + transform: scale(1.1); +} + +.show-diagnostics-popover-btn .severity-icon { + font-size: 14px; + line-height: 1; +} + +.show-diagnostics-popover-btn .severity-icon::before { + content: var(--severity-icon); +} + +/* Other tab header with inline popover button */ +.other-header-wrapper { + display: flex; + align-items: center; +} + +/* Severity Badges - Fluent UI overrides */ +fluent-badge.severity-badge { + display: flex; + flex-shrink: 0; + padding: 4px 8px; + border-radius: 4px; + letter-spacing: 0.5px; } \ No newline at end of file diff --git a/src/Content/newMobilePaneIosFrame.css b/src/Content/newMobilePaneIosFrame.css index 491c9feb..e0b4d936 100644 --- a/src/Content/newMobilePaneIosFrame.css +++ b/src/Content/newMobilePaneIosFrame.css @@ -1,4 +1,5 @@ @import url("themeColors.css"); +@import url("rulesCommon.css"); /* Framework7 specific overrides */ @@ -165,6 +166,16 @@ a.tab-link { overflow-wrap: break-word; } +#antispam-view .accordion-item-content .violation-card .violation-parent-message { + padding-bottom: 0 !important; + margin-bottom: 0 !important; +} + +#antispam-view .accordion-item-content .violation-card .violation-details { + padding-bottom: 0 !important; + margin-bottom: 0 !important; +} + #antispam-view .accordion-item-content p { margin-top: 0 !important; } @@ -204,6 +215,49 @@ a.tab-link { margin: 5px 0; } +.diagnostics-section { + margin-bottom: 0; + padding-bottom: 0; +} + +.diagnostics-section .list { + margin-bottom: 0; +} + +.diagnostics-accordion { + margin-bottom: 0; +} + +.diagnostics-accordion .accordion-item-content { + padding: 0; +} + +.diagnostics-accordion .diagnostic-content { + margin: 10px; + padding-bottom: 0; +} + +.diagnostics-accordion .violation-card { + padding: 10px; + margin: 5px 0; +} + +.diagnostics-accordion .violation-details { + margin-bottom: 0; + padding-bottom: 0; +} + +.diagnostics-accordion .violation-rule { + padding-bottom: 0; + margin-bottom: 0; +} + +.diagnostics-accordion .item-title { + white-space: normal !important; + overflow: visible !important; + text-overflow: clip !important; +} + #orig-headers-ui { margin-top: 0; } diff --git a/src/Content/rulesCommon.css b/src/Content/rulesCommon.css new file mode 100644 index 00000000..974e1a23 --- /dev/null +++ b/src/Content/rulesCommon.css @@ -0,0 +1,124 @@ +@import url("themeColors.css"); + +/* Shared Rule Violation Styles */ +/* This file contains common violation display styles used across all UI implementations */ +/* Uses color variables from themeColors.css for consistency */ + +/* Violation Badges - Compact severity indicators */ +.severity-badge { + display: inline-block; + padding: 2px 6px; + border-radius: 3px; + font-size: 11px; + font-weight: 600; + line-height: 1.2; + text-transform: uppercase; + white-space: nowrap; + margin-left: 4px; +} + +/* Severity system - applies to all [data-severity] elements */ +[data-severity="error"] { + --severity-bg: var(--diagnostic-error-bg); + --severity-color: var(--diagnostic-error-text); + --severity-border: var(--diagnostic-error-border); + --severity-icon: "🔴"; +} + +[data-severity="warning"] { + --severity-bg: var(--diagnostic-warning-bg); + --severity-color: var(--diagnostic-warning-text); + --severity-border: var(--diagnostic-warning-border); + --severity-icon: "⚠️"; +} + +[data-severity="info"] { + --severity-bg: var(--diagnostic-info-bg); + --severity-color: var(--diagnostic-info-text); + --severity-border: var(--diagnostic-info-border); + --severity-icon: "ℹ️"; +} + +/* Apply severity colors to badges */ +.severity-badge[data-severity] { + background: var(--severity-bg); + color: var(--text-primary); +} + +/* Severity icons in badges */ +.severity-badge[data-severity]::before { + content: var(--severity-icon); + margin-right: 4px; +} + +/* Violation Messages */ +.violation-message { + font-weight: 600; +} + +.violation-message[data-severity="error"] { + color: var(--diagnostic-error-text); +} + +.violation-message[data-severity="warning"] { + color: var(--diagnostic-warning-text); +} + +.violation-message[data-severity="info"] { + color: var(--diagnostic-info-text); +} + +/* Violation Card Components */ +.violation-card-header { + display: flex; + align-items: center; +} + +.violation-card { + border-left: 3px solid var(--severity-border); + background-color: var(--severity-bg); +} + +.violation-details { + font-size: 12px; + color: var(--text-secondary); +} + +/* Violation Details */ +.violation-rule { + font-family: monospace; + font-size: 12px; + margin-bottom: 4px; +} + +.violation-parent-message { + font-style: italic; + font-size: 12px; +} + +/* Content Highlighting */ +.highlight-violation { + background-color: var(--highlight-warning-bg); + padding: 2px 4px; + border-radius: 2px; + font-weight: 600; +} + +/* Accessibility - Forced Colors Mode */ +@media (forced-colors: active) { + .severity-badge { + border: 1px solid ButtonText; + } + + .severity-badge[data-severity="error"], + .severity-badge[data-severity="warning"], + .severity-badge[data-severity="info"] { + background: ButtonFace; + color: ButtonText; + } + + .highlight-violation { + background: Highlight; + color: HighlightText; + } +} diff --git a/src/Content/themeColors.css b/src/Content/themeColors.css index c85cc559..962797e1 100644 --- a/src/Content/themeColors.css +++ b/src/Content/themeColors.css @@ -49,4 +49,23 @@ /* Status Colors */ --success-green: #107c10; --success-green-bg: rgba(240, 248, 240, 0.9); + + /* Rule Violation Highlight */ + --highlight-warning-bg: #fff176; /* Softer yellow - complements primary blue */ + --highlight-warning-text: #1b1b1b; /* Near black - 14.3:1 contrast */ + + /* Warning State */ + --diagnostic-warning-bg: #fff8e1; /* Warm cream - complements existing grays */ + --diagnostic-warning-text: #e65100; /* Deep orange - 4.5:1 contrast */ + --diagnostic-warning-border: var(--diagnostic-warning-text); + + /* Info/Neutral State */ + --diagnostic-info-bg: #e3f2fd; /* Light blue - complements primary */ + --diagnostic-info-text: var(--primary-blue); /* Brand blue #0078d4 - 4.53:1 contrast */ + --diagnostic-info-border: var(--primary-blue); + + /* Error State */ + --diagnostic-error-bg: #ffebee; /* Light red - complements existing palette */ + --diagnostic-error-text: #c62828; /* Dark red - 4.5:1 contrast */ + --diagnostic-error-border: var(--diagnostic-error-text); } \ No newline at end of file diff --git a/src/Content/uiToggle.css b/src/Content/uiToggle.css index 62066858..30b157fd 100644 --- a/src/Content/uiToggle.css +++ b/src/Content/uiToggle.css @@ -51,6 +51,13 @@ body, html { height: auto; } +/* Diagnostics dialog code-box sizing */ +fluent-dialog[aria-label="Diagnostics"] .code-box > pre { + background-color: var(--background-light-gray); + font-size: .8em; + line-height: 1.2; +} + /* Fluent UI Dialog customization */ fluent-dialog { --dialog-width: 400px; @@ -62,6 +69,12 @@ fluent-dialog[hidden] { display: none !important; } +fluent-dialog[aria-label="Diagnostics"] { + --dialog-width: calc(100% - 24px); + --dialog-max-width: 600px; + --dialog-height: auto; +} + .dialog-content { padding: 0 4px; display: flex; @@ -114,6 +127,30 @@ fieldset fluent-checkbox { min-width: 80px; } +/* Specific styling for diagnostics dialog actions */ +fluent-dialog[aria-label="Diagnostics"] .dialog-actions { + margin-bottom: 0; + border-top: none; +} + +/* Override general dialog spacing for diagnostics */ +fluent-dialog[aria-label="Diagnostics"] > div { + margin: 0; +} + +fluent-dialog[aria-label="Diagnostics"] .dialog-content { + margin: 0; + height: auto; + overflow: visible; + padding: var(--spacing-medium); +} + +fluent-dialog[aria-label="Diagnostics"] .code-box { + height: auto; + display: flex; + flex-direction: column; +} + /* Dialog content spacing */ fluent-dialog > div { margin: 12px 0; diff --git a/src/Pages/classicDesktopFrame.html b/src/Pages/classicDesktopFrame.html index 8484aa35..55386cba 100644 --- a/src/Pages/classicDesktopFrame.html +++ b/src/Pages/classicDesktopFrame.html @@ -13,6 +13,7 @@