Migrate to Bootstrap 5 (#7582)

This PR completes the migration from Bootstrap 4 to Bootstrap 5.3.5
following the plan outlined in
[docs/Bootstrap5Migration.md](https://github.com/compiler-explorer/compiler-explorer/blob/mg/bootstrap5/docs/Bootstrap5Migration.md).

## Migration Process

We followed a phased approach as documented in the migration plan:

1. **Phase 1: Dependency Updates and Basic Setup**
   - Updated Bootstrap from 4.6.2 to 5.3.5
   - Added @popperjs/core dependency (replacing Popper.js)
   - Updated Tom Select theme from bootstrap4 to bootstrap5

2. **Phase 2: Global CSS Class Migration**
   - Updated directional utility classes (ml/mr → ms/me)
- Updated floating utility classes (float-left/right → float-start/end)
   - Updated text alignment classes (text-left/right → text-start/end)

3. **Phase 3: HTML Attribute Updates**
- Updated data attributes to use Bootstrap 5 prefixes (data-bs-toggle,
data-bs-target, etc.)
   - Fixed tab navigation issues

4. **Phase 4: JavaScript API Compatibility Layer**
   - Created bootstrap-utils.ts compatibility layer
- Updated component initialization for modals, dropdowns, popovers, etc.

5. **Phase 5: Component Migration**
- Updated and tested specific components (modals, dropdowns, toasts,
etc.)
   - Fixed styling issues in cards and button groups

6. **Phase 6: Form System Updates**
   - Updated form control classes to Bootstrap 5 standards
   - Updated checkbox/radio markup patterns
   - Simplified input groups

7. **Phase 7: Navbar Structure Updates**
   - Updated navbar structure with container-fluid
   - Fixed responsive behavior

8. **Phase 8: SCSS Variables and Theming**
   - Added custom CSS fixes for navbar alignment
   - Verified theme compatibility

9. **Phase 9: Accessibility Improvements**
   - Updated sr-only to visually-hidden
   - Added proper ARIA attributes
   - Enhanced screen reader support

## Key Changes

- No more jQuery dependency in Bootstrap 5
- New prefix for data attributes (data-bs-*)
- Improved accessibility with ARIA attributes
- Updated positioning classes (start/end instead of left/right)
- Simplified input group structure

## Test Plan

1. **Navigation Testing**
   - Verify all dropdown menus open and close properly
   - Test mobile menu responsiveness
   - Check tab navigation in settings dialog

2. **Component Testing**
- Verify all modals open and close correctly (settings, share,
load/save)
   - Test tooltips and popovers
   - Check form controls in different dialogs

3. **Layout Testing**
   - Test responsiveness on different screen sizes
   - Verify proper alignment of elements
   - Check dark mode compatibility

4. **Specific Features to Test**
   - Compiler selection and options
   - Share dialog functionality
   - Settings dialog
   - Tree view (IDE mode)
   - Font selection dropdown

5. **Browser Testing**
   - Test in Chrome, Firefox, Safari
   - Test in mobile browsers

## Note on Further Improvements

After this migration is stable, we could consider Phase 12: removing
jQuery dependency entirely, as Bootstrap 5 no longer requires it. This
would be a separate effort.

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Matt Godbolt
2025-04-24 12:10:37 -05:00
committed by GitHub
parent 8232fbae8b
commit 637564f389
79 changed files with 1972 additions and 631 deletions

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ProjectTasksOptions" suppressed-tasks="Less;Babel;Pug/Jade" /> <component name="ProjectTasksOptions" suppressed-tasks="Less;Babel;Pug/Jade;SCSS" />
</project> </project>

62
CLAUDE.md Normal file
View File

@@ -0,0 +1,62 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Build & Test Commands
- Build: `npm run webpack`, `npm start`
- Dev Mode: `make dev`, `make gpu-dev`
- Lint: `npm run lint` (auto-fix), `npm run lint-check` (check only)
- Type Check: `npm run ts-check`
- Test: `npm run test` (all), `npm run test-min` (minimal)
- Test Single File: `npm run test -- --run base-compiler-tests.ts`
- Test Specific Pattern: `npm run test -- -t "should handle execution failures"`
- Cypress Tests: `npm run cypress`
- Pre-commit Check: `make pre-commit` or `npm run check`
## Important Workflow Requirements
- ALWAYS run `npm run lint` before any git operations (`git add`, `git commit`, etc.)
- The linter will automatically fix formatting issues, so this must be run before committing
- Failing to run the linter may result in style issues and commit failures
## Style Guidelines
- TypeScript: Strict typing, no implicit any, no unused locals
- Formatting: 4-space indentation, 120 char line width, single quotes
- No semicolon omission, prefer const/let over var
- Client-side: TypeScript transpiled to ES5 JavaScript. This process requires import of `blah.js` even though `blah.ts` is the actual filename
- ALWAYS place imports at the top of files, never inside functions or methods, unless absolutely necessary (and confirm before proposing)
- Use Underscore.js for utility functions
- Write tests for new server-side components
- Where appropriate suggest follow-up improvements to code to improve code quality, and DRY up where feasible
- Documentation is in `docs/` directory; update where necessary, in particular if anything about the RESTful API changes
- Don't add comments above code that's clearly self-documenting. For example, don't put comments like this:
```
// Initialises the thing
initialiseThing();
```
## Testing Guidelines
- Use Vitest for unit tests (compatible with Jest syntax)
- Tests are in the `/test` directory, typically named like the source files they test
- Use spy functions with `vi.spyOn()` for mocking dependencies
- Test structure follows describe/it pattern with descriptive test names
- Separate tests with clear section headers using comments for readability
- Consider cross-platform compatibility (especially Windows path handling)
- For complex files, organize tests by functionality rather than by method
- Use `beforeEach`/`afterEach` to set up and clean up test environment
- Remember to restore mocks with `vi.restoreAllMocks()` after tests
- Test both success and error cases
- Coverage is available with `npm test:coverage`
- For Windows-specific path issues, either:
- Skip tests with `if (process.platform === 'win32') return;`
- Write platform-specific assertions
- Use path-agnostic checks
## Compiler Testing Specifics
- Mock filesystem operations when testing file I/O
- Use `makeFakeCompilerInfo()` for creating test compiler configurations
- Use `makeCompilationEnvironment()` to create test environments
- Mock `exec` calls for testing compilation and execution
- For BaseCompiler, use the test utils from `/test/utils.js`
- Test specific combinations of compiler capabilities
- Focus tests on behavior, not implementation details
- Use platform-agnostic assertions where possible

View File

@@ -89,6 +89,8 @@ and shorter startup times.
You can also use `npm run dev` to run if `make dev` doesn't work on your machine. You can also use `npm run dev` to run if `make dev` doesn't work on your machine.
When making UI changes, we recommend following the [UI Testing Checklist](docs/TestingTheUi.md) to ensure all components work correctly.
Some languages need extra tools to demangle them, e.g. `rust`, `d`, or `haskell`. Such tools are kept separately in the Some languages need extra tools to demangle them, e.g. `rust`, `d`, or `haskell`. Such tools are kept separately in the
[tools repo](https://github.com/compiler-explorer/compiler-explorer-tools). [tools repo](https://github.com/compiler-explorer/compiler-explorer-tools).

387
docs/Bootstrap5Migration.md Normal file
View File

@@ -0,0 +1,387 @@
# Bootstrap 5 Migration Plan
This document outlines the step-by-step process for migrating Compiler Explorer from Bootstrap 4 to Bootstrap 5.3.5. The
migration will be completed incrementally to allow for testing between steps.
## Migration Strategy
We'll break down the migration into smaller, testable chunks rather than making all changes at once. This approach
allows for:
- Easier identification of issues
- Progressive testing by project maintainers
- Minimizing disruption to the codebase
**Progress tracking will be maintained in this document.**
## Phases & Current Progress
### Phase 1: Dependency Updates and Basic Setup ✅
- [x] Update package.json with Bootstrap 5.3.5
- [x] Add @popperjs/core dependency (replacing Popper.js)
- [x] Update Tom Select theme from bootstrap4 to bootstrap5
- [x] Update main import statements where Bootstrap is initialized
- [x] Update webpack configuration if needed for Bootstrap 5 compatibility
- [x] Verify the application still builds and runs with basic functionality
#### Notes for Human Testers (Phase 1)
- At this phase, the application will have console errors related to Bootstrap component initialization
- Tom Select dropdowns (like compiler picker) may have styling differences with the new bootstrap5 theme
- Initial page load should still work, but dropdown functionality will be broken
- Modal dialogs like Settings and Sharing may not function correctly
- The primary layout and basic display of panes should still function
### Phase 2: Global CSS Class Migration ✅
- [x] Update directional utility classes (ml/mr → ms/me)
- [x] Search and replace in .pug templates
- [x] Search and replace in .scss files
- [x] Search and replace in JavaScript/TypeScript files that generate HTML
- [x] Update floating utility classes (float-left/right → float-start/end)
- [x] Update text alignment classes (text-left/right → text-start/end)
- [x] Update other renamed classes (badge-pill → rounded-pill, etc.)
- [x] Test and verify styling changes
#### Notes for Human Testers (Phase 2)
- Look for proper spacing and margin alignment in the UI
- Specific components to check:
- Compiler output area: Check the spacing in compiler.pug (short-compiler-name, compile-info spans)
- Navbar spacing: The navbar items should maintain proper spacing (ms/me instead of ml/mr)
- Code editor components: Check the button and icon alignment in codeEditor.pug
- Tree view components: The tree.pug file had utility class changes
- Alert messages (widgets/alert.ts): Check that toast messages appear with correct alignment
- Compiler picker (compiler-picker.ts and compiler-picker-popup.ts): Check dropdown spacing
- Rounded badge display in menu-policies.pug (now using rounded-pill instead of badge-pill)
- The float-end class replaces float-right in the index.pug file's copy buttons
- Any LTR/RTL layout impacts should be especially checked for correct directionality
### Phase 3: HTML Attribute Updates ✅
- [x] Update data attributes across the codebase
- [x] data-toggle → data-bs-toggle
- [x] data-target → data-bs-target
- [x] data-dismiss → data-bs-dismiss
- [x] data-ride → data-bs-ride (not used in codebase)
- [x] data-spy → data-bs-spy (not used in codebase)
- [x] Other data attributes as needed
- [x] Test components to ensure they function correctly with new attributes
#### Notes for Human Testers (Phase 3)
- This phase should restore basic functionality of several Bootstrap components
- Specific components to check:
- Dropdowns: All dropdown menus throughout the application should open/close properly
- Modal dialogs: Settings, Sharing, and other modal dialogs should open/close correctly
- Tooltips: Hover tooltips (using data-bs-toggle="tooltip") should display properly
- Popovers: Any popovers used in the UI should function correctly
- Collapse components: Any collapsible sections should toggle properly
- Tabs: Any tabbed interfaces should switch between tabs correctly
- Watch for console errors related to Bootstrap component initialization
- Some JavaScript component initialization may still be broken until Phase 4 is completed
### Phase 4: JavaScript API Compatibility Layer ✅
- [x] Create a temporary Bootstrap compatibility utility module to abstract component initialization
- [x] Implement a hybrid approach with `bootstrap-utils.ts` as a compatibility layer
- [x] Mark clearly as temporary code to be removed after migration is complete
- [x] Define methods for each component type (Modal, Dropdown, Toast, etc.)
- [x] Update component initialization in key files:
- [x] widgets/alert.ts (modals and toasts)
- [x] sharing.ts (modals, tooltips, and dropdowns)
- [x] compiler-picker-popup.ts (modals)
- [x] load-save.ts (modals)
- [x] Other files with Bootstrap component initialization:
- [x] **Modal Initialization**:
- [x] widgets/site-templates-widget.ts
- [x] widgets/runtime-tools.ts
- [x] widgets/compiler-overrides.ts
- [x] widgets/timing-info-widget.ts
- [x] widgets/history-widget.ts
- [x] widgets/libs-widget.ts
- [x] main.ts
- [x] **Dropdown Handling**:
- [x] panes/tree.ts
- [x] panes/compiler.ts
- [x] panes/editor.ts
- [x] **Popover Handling**:
- [x] main.ts
- [x] widgets/compiler-version-info.ts
- [x] panes/executor.ts
- [x] panes/conformance-view.ts
- [x] panes/cfg-view.ts
- [x] Test the compatibility layer with basic components
#### Notes for Human Testers (Phase 4)
- This is one of the most critical phases as it involves creating a compatibility layer for the JavaScript API
- A new utility file will be created for component initialization that abstracts Bootstrap 5's new approach
- Key differences to watch for:
- Modal initialization and events: Bootstrap 5 uses a completely different event system
- Dropdown initialization: Works with data attributes but requires `data-bs-toggle` instead of `data-toggle`
- Toast components: The API has changed significantly
- Popover/Tooltip initialization: API changes but still support data attributes with proper prefixes
- Components to thoroughly test:
- Alert dialogs (widgets/alert.ts): Check all types of alerts (info, warning, error)
- Sharing functionality (sharing.ts): The share modal should work properly
- Compiler picker popup (compiler-picker-popup.ts): Should display and function correctly
- All dropdown menus: Should open and close properly
- All tooltips and popovers: Should display correctly on hover/click
- Watch for event handling issues where Bootstrap 4 events no longer exist or are renamed
### Phase 5: Component Migration (By Component Type) ✅
#### Modal Component Migration
- [x] Update modal implementation in alert.ts
- [x] Update modal usage in compiler-picker-popup.ts
- [x] Update modal handling in load-save.ts
- [x] Update modal event handling in sharing.ts
- [x] Test modal functionality thoroughly
#### Dropdown Component Migration
- [x] Update dropdown handling in sharing.ts
- [x] Update dropdown usage in compiler.ts, editor.ts, etc.
- [x] Test dropdown functionality thoroughly
#### Toast/Alert Component Migration
- [x] Update toast implementation in alert.ts
- [x] Update toast styling in explorer.scss
- [x] Test toast notifications and alerts
#### Popover/Tooltip Migration
- [x] Update tooltip initialization in sharing.ts
- [x] Update popover usage in compiler.ts, executor.ts, editor.ts, etc.
- [x] Test popover and tooltip functionality thoroughly
#### Card Component Updates
- [x] Review card usage and update to Bootstrap 5 standards
- [x] ~~Replace any card-deck implementations with grid system~~ (Not needed - card-deck not used in codebase)
- [x] Test card layouts, especially tab navigation within cards
#### Collapse Component Updates
- [x] ~~Update any collapse component implementations~~ (Not needed - minimal collapse usage in codebase)
- [x] Test collapse functionality (limited to navbar hamburger menu on mobile)
#### Button Group Updates
- [x] Review button group implementations
- [x] Update to Bootstrap 5 standards (no changes needed - Bootstrap 5 maintains same button group classes)
- [x] Test button group functionality in toolbars and dropdown menus
### Phase 6: Form System Updates ✅
- [x] Update form control classes to Bootstrap 5 standards
- [x] Update input group markup and classes
- [x] Update checkbox/radio markup to Bootstrap 5 standards
- [x] Update form validation classes and markup
- [x] ~~Consider implementing floating labels where appropriate (new in Bootstrap 5)~~ (Not needed for existing form
usage)
- [x] Test form functionality and appearance
### Phase 7: Navbar Structure Updates ✅
- [x] Update navbar structure in templates to match Bootstrap 5 requirements
- [x] Review custom navbar styling in explorer.scss
- [x] Test responsive behavior of navbar
- [x] Ensure mobile menu functionality works correctly
- [x] ~~Consider implementing offcanvas for mobile navigation (new in Bootstrap 5)~~ (Standard navbar collapse is
sufficient for current needs)
### Phase 8: SCSS Variables and Theming ✅
- [x] Review any custom SCSS that extends Bootstrap functionality
- [x] Update any custom themes to use Bootstrap 5 variables
- [x] Check z-index variable changes in Bootstrap 5
- [x] Add navbar container padding fix for proper alignment
- [x] Test theme switching functionality
### Phase 9: Accessibility Improvements ✅
- [x] Review ARIA attributes in custom component implementations
- [x] Leverage Bootstrap 5's improved accessibility features
- [x] Add ARIA labels and live regions for dynamic content
- [x] Enhance form controls with proper accessibility attributes
- [ ] ~~Test with screen readers and keyboard navigation~~ (left for future work)
- [ ] ~~Ensure color contrast meets accessibility guidelines~~ (left for future work)
### Phase 10: Final Testing and Refinement ✅
- [x] ~~Comprehensive testing across different viewports~~ cursory testing with a few viewports
- [x] Cross-browser testing (at least; looked in FireFox and we're good)
- [x] Fix any styling issues or inconsistencies
- [x] ~~Performance testing (Bootstrap 5 should be more performant)~~ (don't care; site is fine)
- [x] Ensure no regressions in functionality
## Key Learnings From Implementation
These insights were gathered during the migration process and may be helpful for future reference:
- **Data Attributes Still Work Without JavaScript Initialization**: Despite some documentation suggesting otherwise,
Bootstrap 5 components with data attributes (like tabs) still work without explicit JavaScript initialization. The key
is using the correct `data-bs-*` prefix.
- **Close Button Implementation Completely Changed**: Bootstrap 4 used `.close` class with a `&times;` entity inside a
span, while Bootstrap 5 uses `.btn-close` class with a background image and no inner content.
- **Tab Navigation Issues**: The tab navigation problems were fixed by simply updating data attributes, not by adding
JavaScript initialization.
- **jQuery Plugin Methods Removal**: jQuery methods like `.popover()` and `.dropdown('toggle')` need to be replaced with
code that uses the Bootstrap 5 API through a compatibility layer. Always use `BootstrapUtils` helper methods rather
than direct jQuery plugin calls.
- **Grid and Form Class Renaming**: Bootstrap 5 renamed several core classes, such as changing `.form-row` to `.row`.
This can cause subtle template selector issues in code that relies on these class names.
- **Don't Mix Data Attributes and JavaScript Modal Creation**: When creating modals via JavaScript (e.g., for
dynamically loaded content), don't include `data-bs-toggle="modal"` on the trigger element unless you also add a
matching `data-bs-target` attribute pointing to a valid modal element.
- **Modal Events Changed Significantly**: Bootstrap 5 modal events need to be attached directly to the native DOM
element rather than jQuery objects, and the event parameter type is different. For proper typing, import the `Modal`
type from bootstrap and use `Modal.Event` type.
- **jQuery Event Binding vs Native DOM Events**: Bootstrap 5 requires native DOM event binding instead of jQuery's
`.on()` method. Replace `$(selector).on('shown.bs.modal', handler)` with
`domElement.addEventListener('shown.bs.modal', handler)`. This is particularly important for modal events like '
shown.bs.modal'.
- **Tooltip API Changed**: The global `window.bootstrap.Tooltip` reference no longer exists. Import the `Tooltip` class
directly from bootstrap instead.
- **Input Group Structure Simplified**: Bootstrap 5 removed the need for `.input-group-prepend` and
`.input-group-append` wrapper divs. Buttons and other controls can now be direct children of the `.input-group`
container. This simplifies the markup but requires template updates.
- **TomSelect Widget Integration**: Bootstrap 5's switch from CSS triangles to SVG background images for dropdowns
caused issues with TomSelect. Adding back custom CSS for dropdown arrows was necessary to maintain correct appearance.
- **Btn-block Removed**: Bootstrap 5 removed the `.btn-block` class. Instead, the recommended approach is to wrap
buttons in a container with `.d-grid` and use standard `.btn` classes. This affects any full-width buttons in the
application.
- **Element Selection for Components**: When working with Bootstrap 5 components, prefer passing CSS selectors to
`BootstrapUtils` methods rather than jQuery objects, as this provides more consistent behavior.
## Fixed Issues & Completed Work
### UI Layout & Display Issues
- [x] Font dropdown styling fixed
- [x] Templates view proportions fixed (min-width added to columns and modal)
- [x] Dialog appearance fixed (updated close buttons to use `.btn-close`)
- [x] Dropdown positioning fixed (updated to `.dropdown-menu-end`)
- [x] TomSelect dropdown arrows fixed (custom CSS implementation)
- [x] IDE mode border styling improved (temporarily with `.list-group-flush`)
- [x] Sponsors window styling fixed (replaced `.btn-block` with `.d-grid` approach)
- [x] The X to close the community/alert notes is harder to see in dark mode than before (fixed: added
`filter: invert(100%)` to make btn-close buttons visible in dark themes)
- [x] TomSelect dropdowns for compilers are excessively long (both in executor view and normal view) (fixed manually)
- [x] Default text/placeholder text is too dark, making it hard to read (especially "Compiler options") (fixed manually)
- [x] Dropdown in the library menu has changed color (fixed: updated `.custom-select` to `.form-select` in theme files)
- [x] ~~Layout has changed slightly in the library menu~~ (decided it looks better now)
- [x] The "popout" on the TomSelect compiler dropdown is misaligned (fixed: updated styling for TomSelect components)
- [x] Compiler combobox rounding overlaps left border by 1 pixel (fixed: overrode CSS variables to reset Bootstrap 5's
negative margin)
- [x] Diff view - changing left/right side compiler/window turns combobox to a white background (fixed: removed
form-select class to avoid transparent background)
- [x] The popular arguments dropdown at the right of the options isn't properly aligned (fixed: updated dropdown styling
in compiler.pug)
- [x] Long compiler names wrap instead of widening the dropdown (fixed: improved styling for TomSelect dropdowns)
### Navigation & Functional Issues
- [x] Tab navigation fixed (updated data attributes to `data-bs-toggle="tab"`)
- [x] Share dialog functionality fixed (proper Bootstrap 5 modal initialization)
- [x] Sponsors modal error fixed (removed conflicting data attributes)
- [x] Share dropdown tooltip conflict fixed (moved tooltip to parent element)
- [x] History view is broken (empty when clicking radio buttons) (fixed: updated modal event binding from jQuery's
`.on('shown.bs.modal')` to native DOM `addEventListener('shown.bs.modal')`)
- [x] Conformance view's "add compiler" functionality is broken (fixed: template selector was looking for `.form-row`
which changed to `.row` in Bootstrap 5)
- [x] Need to check for more instances of old Bootstrap v4 code patterns (fixed: replaced `dropdown('toggle')` in
main.ts with `BootstrapUtils.getDropdownInstance()` and `.toggle()`)
- [x] Runtime tools window is broken - doesn't save settings anymore (fixed: updated modal hide event handling with
setElementEventHandler)
- [x] Emulation functionality is broken due to modal issues (fixed: replaced direct .modal() calls with
BootstrapUtils.showModal)
### Code Structure Improvements
- [x] Custom classes in runtime tools selection (`.custom-runtimetool`) and overrides selection (`.custom-override`) -
removed as they were superfluous
- [x] `.form-row` still used in theme files (dark-theme.scss, one-dark-theme.scss, pink-theme.scss) - replaced with
standard `.row`
- [x] Border directional properties in explorer.scss updated for better RTL support - added `border-inline-start` and
border radius logical properties with appropriate fallbacks for older browsers
- [x] Input group structures verified - all instances of the deprecated `.input-group-prepend` and `.input-group-append`
have already been updated to use Bootstrap 5's simplified approach
- [x] Toast header close button styling verified - explorer.scss already uses `.btn-close` consistently for toast
components
- [x] Event handlers verified - history-widget.ts and sharing.ts are correctly using native DOM addEventListener methods
with the appropriate Bootstrap 5 event names
## Future Work
### Phase 11: Documentation Update
- [ ] Update any documentation that references Bootstrap components
- [ ] Document custom component implementations
- [ ] Note any deprecated features or changes in functionality
### Phase 12: Optional jQuery Removal and Cleanup
- [ ] Create a plan for jQuery removal (if desired)
- [ ] Identify non-Bootstrap jQuery usage that would need refactoring
- [ ] Remove the temporary `bootstrap-utils.ts` compatibility layer
- [ ] Replace all uses with direct Bootstrap 5 API calls
- [ ] Document the native Bootstrap 5 API for future reference
- [ ] Investigate and fix modal accessibility warnings
- [ ] Address the warning: "Blocked aria-hidden on an element because its descendant retained focus"
- [ ] Update modal template markup to leverage Bootstrap 5.3's built-in support for the `inert` attribute
- [ ] Ensure proper focus management in modals for improved accessibility
### Additional Pending Issues
- [ ] Check Sentry for additional errors on the beta site
- [ ] Investigate the "focus" selected check boxes in the settings view. They're very light when focused, in particular
in pink theme. I couldn't work out how to fix this, but it seemed minor.
- [ ] The "pop out" div that's attached to the compiler picker doesn't work on the conformance view: this was broken
before. Essentially the z-order means it's drawn behind the lower conformance compilers and `z-index` can't fix it.
Needs a rethink of how this is done.
- [ ] File tracking issues for anything on this list we don't complete.
## Final Testing Checklist
Before considering the Bootstrap 5 migration complete, a comprehensive UI testing checklist was created and used to
verify functionality. This checklist has been completed with all tests passing. The tests cover all major UI components
that could be affected by the Bootstrap migration.
The checklist included:
- Modal dialogs (Settings, Share, Load/Save, etc.)
- Dropdown components (navigation, compiler options, TomSelect)
- Toast/Alert components
- Popovers and tooltips
- Card, button group, and form components
- Specialized views (Conformance, Tree, Visualization)
- Responsive behavior
A permanent version of this UI testing checklist has been created as a separate document and can be used for testing
future UI changes or upgrades: [UI Testing Checklist](TestingTheUi.md)
## Notes for Implementation
1. **Make minimal changes** in each step to allow for easier testing and troubleshooting
2. **Test thoroughly** after each phase before moving to the next
3. **Document issues** encountered during migration for future reference
4. **Focus on accessibility** to ensure the site remains accessible throughout changes
5. **Maintain browser compatibility** with all currently supported browsers
6. **Consider performance implications** of the changes
7. **NEVER mark any issue as fixed in this document** until you have explicit confirmation from the reviewer that the
issue is completely resolved
8. **NEVER commit changes** until you have explicit confirmation that the fix works correctly
## Technical References
- [Bootstrap 5 Migration Guide](https://getbootstrap.com/docs/5.0/migration/)
- [Bootstrap 5 Components Documentation](https://getbootstrap.com/docs/5.3/components/)
- [Bootstrap 5 Utilities Documentation](https://getbootstrap.com/docs/5.3/utilities/)
- [Bootstrap 5 Forms Documentation](https://getbootstrap.com/docs/5.3/forms/overview/)
- [Popper v2 Documentation](https://popper.js.org/docs/v2/)

240
docs/TestingTheUi.md Normal file
View File

@@ -0,0 +1,240 @@
# UI Testing Checklist for Compiler Explorer
This document provides a checklist for testing the Compiler Explorer UI components. Use this checklist for
major UI changes, framework updates, or when implementing significant new features.
## Modal Components
### Settings Modal
- Open and close using the "Settings" option in the "More" dropdown
- Test tab navigation between all tab sections (Colouring, Site behaviour, etc.)
- Verify form controls within settings (checkboxes, selects, inputs)
- Check that the close button works
- Verify proper modal appearance/styling in both light and dark themes
### Share Modal
- Open and close using the "Share" button
- Verify the URL is generated correctly
- Test the copy button
- Check that social sharing buttons display correctly
- Verify proper styling in both light and dark themes
- Test "Copied to clipboard" tooltip functionality
### Load/Save Modal
- Open and close using "Save" or "Load" options
- Test tab navigation between sections (Examples, Browser-local storage, etc.)
- Verify save functionality to browser storage
- Test loading from browser storage
- Check proper styling/layout in both light and dark themes
### Compiler Picker Modal
- Open using the popout button next to a compiler selector
- Test filter functionality (architecture, compiler type, search)
- Verify compiler selection works
- Check proper styling in both light and dark themes
### Other Modals
- Test confirmation dialogs (alert.ts)
- Test library selection modal
- Test compiler overrides modal
- Test runtime tools modal
- Test templates modal
- Verify proper styling in both light and dark themes
## Dropdown Components
### Main Navigation Dropdowns
- Test "More" dropdown menu (all items work and have proper styling)
- Test "Other" dropdown menu (all items work and have proper styling)
- Verify dropdowns are properly positioned (not clipped)
- Test on different screen sizes to ensure responsive behavior
### Compiler Option Dropdowns
- Test filter dropdowns in compiler pane
- Test "Add new..." dropdown
- Test "Add tool..." dropdown
- Test popular arguments dropdown
- Verify proper positioning, especially for dropdowns at the right edge of the screen
### Editor Dropdowns
- Test language selector dropdown
- Test font size dropdown
- Verify proper styling and positioning
### TomSelect Dropdowns
- Test compiler selectors
- Test library version selectors
- Verify dropdown arrows appear correctly
- Verify dropdown items are styled correctly
## Toast/Alert Components
### Alert Notifications
- Trigger various notifications (info, warning, error)
- Verify proper styling
- Test auto-dismiss functionality
- Check that close button works
- Test stacking behavior of multiple notifications
### Alert Dialogs
- Test info/warning/error alert dialogs (using the Alert class)
- Verify proper styling and positioning
- Check button functionality within dialogs
## Popover/Tooltip Components
### Tooltips
- Hover over various buttons with tooltips (toolbar buttons, share button, etc.)
- Verify tooltip text appears correctly
- Check tooltip positioning (above/below/left/right of trigger)
- Verify proper styling in both light and dark themes
### Popovers
- Trigger popovers on compiler info
- Check popover content displays correctly
- Verify popover positioning
- Test dismissal by clicking outside
- Verify proper styling in both light and dark themes
## Card Components
- Check card styling in modals (Settings, Load/Save, etc.)
- Verify tab navigation within card headers
- Test card body content layout
- Check responsive behavior on different screen sizes
## Button Group Components
### Toolbar Button Groups
- Test button groups in compiler pane toolbar
- Test button groups in editor pane toolbar
- Verify proper alignment and styling
- Check dropdown buttons within button groups
### Other Button Groups
- Test font size button group
- Test bottom bar button groups
- Verify proper styling in both light and dark themes
## Collapse Components
- Test mobile view hamburger menu
- Verify menu expands/collapses correctly
- Check that all menu items are accessible in collapsed mode
## Specialized Views
### Conformance View
- Test compiler selectors and options
- Verify results display correctly
- Test the "add compiler" functionality
- Verify that compilers can be added and removed
- Check that conformance testing works end-to-end
### Tree View (IDE Mode)
- Check tree structure and file display
- Test right-click menus and dropdowns
- Verify file manipulation controls
### Visualization Components
- Test CFG view rendering and controls
- Check opt pipeline viewer
- Verify AST view
### Sponsor Window
- Check sponsor list display
- Verify modal dialog appearance and functionality
## Form Components
- Verify form control styling (inputs, selects, checkboxes)
- Test input groups with buttons
- Check validation states
## Responsive Behavior
- Test at various viewport sizes
- Verify mobile menu functionality
- Check input group stacking behavior
## Runtime Tool Integration
### Runtime Tools
- Open the runtime tools window from compiler pane
- Change settings and click outside the modal to close
- Verify settings are properly saved
- Test with multiple runtime tool options
- Verify event handling properly handles modal opening/closing
### Emulation Features
- Test BBC emulation by clicking emulator links
- Check Z80 emulation features (e.g. https://godbolt.org/z/qnE7jhnvc)
- Verify emulator modals open properly
- Test interaction between emulator windows and the main interface
## Diff View
- Test changing compilers in both left and right panes
- Verify backgrounds remain themed correctly in dark mode
- Check that the diff view layout is correct (no excessive height)
- Confirm that input groups and buttons are properly sized
- Test different diff view types (Assembly, Compiler output, etc.)
## TomSelect and Input Components
### Compiler Selection Dropdowns
- Verify long compiler names display properly without excessive wrapping
- Check that dropdowns expand to fit compiler names rather than wrapping text
- Test the flex-grow behavior of dropdown elements
- Check the alignment of the popout button on all dropdowns
- Verify border colors in dark themes are appropriate
### Placeholder Text
- Check visibility and contrast of placeholder text in all input fields
- Specifically test "Compiler options" field visibility
- Verify that all placeholder text is readable in both light and dark themes
## Library Components
### Library Menu
- Check dropdown colors and layout
- Verify all library functionality works correctly
- Test adding and removing libraries
- Check library version selection
## History View
- Verify the history view populates correctly when clicking radio buttons
- Test all history functions (load, delete, etc.)
## General Testing
- Check pixel-perfect alignment of elements (compare with live site)
- Test all components for unexpected behavioral differences
- Verify theme switching works correctly for all components
- Test cross-browser compatibility (at least Firefox and Chrome)
- Check accessibility features (tab navigation, screen reader support)

View File

@@ -92,7 +92,7 @@ async function generateScreenshot(url: string, output_path: string, settings, wi
}, settings); }, settings);
await page.goto(url); await page.goto(url);
//await sleep(2000); //await sleep(2000);
//await page.click(".modal.show button.btn.btn-outline-primary[data-dismiss=modal]"); //await page.click(".modal.show button.btn.btn-outline-primary[data-bs-dismiss=modal]");
//await sleep(5000); //await sleep(5000);
//await page.click("#simplecook .btn.btn-primary.btn-sm.cook-do-consent"); //await page.click("#simplecook .btn.btn-primary.btn-sm.cook-do-consent");
await page.evaluate(() => { await page.evaluate(() => {

25
package-lock.json generated
View File

@@ -18,11 +18,12 @@
"@flatten-js/interval-tree": "^1.1.3", "@flatten-js/interval-tree": "^1.1.3",
"@fortawesome/fontawesome-free": "^6.7.2", "@fortawesome/fontawesome-free": "^6.7.2",
"@orchidjs/sifter": "^1.1.0", "@orchidjs/sifter": "^1.1.0",
"@popperjs/core": "^2.11.8",
"@sentry/browser": "^7.120.3", "@sentry/browser": "^7.120.3",
"@sentry/node": "^7.120.3", "@sentry/node": "^7.120.3",
"@types/semver": "^7.7.0", "@types/semver": "^7.7.0",
"big-integer": "^1.6.52", "big-integer": "^1.6.52",
"bootstrap": "^4.6.2", "bootstrap": "^5.3.5",
"buffer": "^6.0.3", "buffer": "^6.0.3",
"chart.js": "^4.4.9", "chart.js": "^4.4.9",
"clipboard": "^2.0.11", "clipboard": "^2.0.11",
@@ -48,7 +49,6 @@
"nopt": "^8.1.0", "nopt": "^8.1.0",
"p-queue": "^8.1.0", "p-queue": "^8.1.0",
"path-browserify": "^1.0.1", "path-browserify": "^1.0.1",
"popper.js": "^1.16.1",
"profanities": "^3.0.1", "profanities": "^3.0.1",
"prom-client": "^15.1.3", "prom-client": "^15.1.3",
"pug": "^3.0.3", "pug": "^3.0.3",
@@ -3135,7 +3135,6 @@
"version": "2.11.8", "version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
"dev": true,
"license": "MIT", "license": "MIT",
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
@@ -5773,9 +5772,9 @@
"license": "ISC" "license": "ISC"
}, },
"node_modules/bootstrap": { "node_modules/bootstrap": {
"version": "4.6.2", "version": "5.3.5",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.2.tgz", "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.5.tgz",
"integrity": "sha512-51Bbp/Uxr9aTuy6ca/8FbFloBUJZLHwnhTcnjIeRn2suQWsWzcuJhGjKDB5eppVte/8oCdOL3VuwxvZDUggwGQ==", "integrity": "sha512-ct1CHKtiobRimyGzmsSldEtM03E8fcEX4Tb3dGXz1V8faRwM50+vfHwTzOxB3IlKO7m+9vTH3s/3C6T2EAPeTA==",
"funding": [ "funding": [
{ {
"type": "github", "type": "github",
@@ -5788,8 +5787,7 @@
], ],
"license": "MIT", "license": "MIT",
"peerDependencies": { "peerDependencies": {
"jquery": "1.9.1 - 3", "@popperjs/core": "^2.11.8"
"popper.js": "^1.16.1"
} }
}, },
"node_modules/bowser": { "node_modules/bowser": {
@@ -10767,17 +10765,6 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/popper.js": {
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
"integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==",
"deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1",
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.5.3", "version": "8.5.3",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",

View File

@@ -31,7 +31,7 @@
"@sentry/node": "^7.120.3", "@sentry/node": "^7.120.3",
"@types/semver": "^7.7.0", "@types/semver": "^7.7.0",
"big-integer": "^1.6.52", "big-integer": "^1.6.52",
"bootstrap": "^4.6.2", "bootstrap": "^5.3.5",
"buffer": "^6.0.3", "buffer": "^6.0.3",
"chart.js": "^4.4.9", "chart.js": "^4.4.9",
"clipboard": "^2.0.11", "clipboard": "^2.0.11",
@@ -57,7 +57,7 @@
"nopt": "^8.1.0", "nopt": "^8.1.0",
"p-queue": "^8.1.0", "p-queue": "^8.1.0",
"path-browserify": "^1.0.1", "path-browserify": "^1.0.1",
"popper.js": "^1.16.1", "@popperjs/core": "^2.11.8",
"profanities": "^3.0.1", "profanities": "^3.0.1",
"prom-client": "^15.1.3", "prom-client": "^15.1.3",
"pug": "^3.0.3", "pug": "^3.0.3",

471
static/bootstrap-utils.ts Normal file
View File

@@ -0,0 +1,471 @@
// Copyright (c) 2025, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
/**
* TEMPORARY COMPATIBILITY LAYER
*
* This module provides utilities to help transition from Bootstrap 4's jQuery-based API
* to Bootstrap 5's vanilla JavaScript API. This is intended as a temporary solution
* during the migration from Bootstrap 4 to 5 and should be removed once the migration
* is complete.
*
* The goal is to minimize changes throughout the codebase by centralizing the Bootstrap
* API changes in this file, while still allowing for gradual migration to direct API calls.
*
* @deprecated This module should be removed after the Bootstrap 5 migration is complete.
*/
import $ from 'jquery';
import 'bootstrap';
import {Collapse, Dropdown, Modal, Popover, Tab, Toast, Tooltip} from 'bootstrap';
// Private event listener tracking map
const eventListenerMap = new WeakMap<HTMLElement, Map<string, EventListener>>();
/**
* Helper method to get an HTMLElement from various input types
* @param elementOrSelector Element, jQuery object, or selector
* @returns HTMLElement or null
*/
function getElement(elementOrSelector: string | HTMLElement | JQuery): HTMLElement | null {
if (!elementOrSelector) return null;
if (typeof elementOrSelector === 'string') {
return document.querySelector(elementOrSelector as string);
}
if (elementOrSelector instanceof HTMLElement) {
return elementOrSelector;
}
if (elementOrSelector instanceof $) {
return (elementOrSelector as JQuery)[0] as HTMLElement;
}
return null;
}
/**
* Private helper that sets an event handler on a single DOM element
* Handles the removal of existing handlers and tracking of the new one
*
* @param domElement The DOM element to attach the event to
* @param eventName The event name (e.g., 'hidden.bs.modal', 'shown.bs.modal', 'click', etc.)
* @param handler The event handler function
*/
function setDomElementEventHandler(domElement: HTMLElement, eventName: string, handler: (event: Event) => void): void {
// Initialize nested map structure if needed
if (!eventListenerMap.has(domElement)) {
eventListenerMap.set(domElement, new Map());
}
const elementEvents = eventListenerMap.get(domElement)!;
// Remove existing handler if present
if (elementEvents.has(eventName)) {
const oldHandler = elementEvents.get(eventName)!;
domElement.removeEventListener(eventName, oldHandler);
}
// Store and add new handler
elementEvents.set(eventName, handler);
domElement.addEventListener(eventName, handler);
}
/**
* Registers an event handler on element(s), removing any previous handler for the same event
* Similar to jQuery's off().on() pattern
* Works with both single elements and jQuery collections with multiple elements
*
* @param element The element(s) or jQuery object to attach the event to
* @param eventName The event name (e.g., 'hidden.bs.modal', 'shown.bs.modal', 'click', etc.)
* @param handler The event handler function
*/
export function setElementEventHandler(
element: JQuery<HTMLElement> | HTMLElement,
eventName: string,
handler: (event: Event) => void,
): void {
// If jQuery object with potentially multiple elements
if (!(element instanceof HTMLElement)) {
// Loop through all elements in the jQuery collection
element.each((_index, domElement) => {
setDomElementEventHandler(domElement, eventName, handler);
});
return;
}
// Otherwise it's a single DOM element
setDomElementEventHandler(element, eventName, handler);
}
/**
* Initialize a modal
* @param elementOrSelector Element or selector for the modal
* @param options Modal options
* @returns Modal instance
* @throws Error if the element cannot be found
*/
export function initModal(elementOrSelector: string | HTMLElement | JQuery, options?: Partial<Modal.Options>): Modal {
const element = getElement(elementOrSelector);
if (!element) throw new Error(`Failed to find element for modal: ${elementOrSelector}`);
return new Modal(element, options);
}
/**
* Initialize a modal if the element exists, returning null otherwise
* @param elementOrSelector Element or selector for the modal
* @param options Modal options
* @returns Modal instance or null if the element cannot be found
*/
export function initModalIfExists(
elementOrSelector: string | HTMLElement | JQuery,
options?: Partial<Modal.Options>,
): Modal | null {
const element = getElement(elementOrSelector);
if (!element) return null;
return new Modal(element, options);
}
/**
* Get an existing modal instance for an element
* @param elementOrSelector Element or selector for the modal
* @returns Existing modal instance or null if not found
*/
export function getModalInstance(elementOrSelector: string | HTMLElement | JQuery): Modal | null {
const element = getElement(elementOrSelector);
if (!element) return null;
return Modal.getInstance(element);
}
/**
* Show a modal
* @param elementOrSelector Element or selector for the modal
* @param relatedTarget Optional related target element
*/
export function showModal(elementOrSelector: string | HTMLElement | JQuery, relatedTarget?: HTMLElement): void {
const element = getElement(elementOrSelector);
if (!element) return;
const modal = Modal.getInstance(element) || new Modal(element);
modal.show(relatedTarget);
}
/**
* Hide a modal
* @param elementOrSelector Element or selector for the modal
*/
export function hideModal(elementOrSelector: string | HTMLElement | JQuery): void {
const element = getElement(elementOrSelector);
if (!element) return;
const modal = Modal.getInstance(element);
if (modal) modal.hide();
}
/**
* Initialize a toast
* @param elementOrSelector Element or selector for the toast
* @param options Toast options
* @returns Toast instance
*/
export function initToast(elementOrSelector: string | HTMLElement | JQuery, options?: Partial<Toast.Options>): Toast {
const element = getElement(elementOrSelector);
if (!element) throw new Error(`Failed to find element for toast: ${elementOrSelector}`);
return new Toast(element, options);
}
/**
* Initialize a toast if the element exists
* @param elementOrSelector Element or selector for the toast
* @param options Toast options
* @returns Toast instance or null if element doesn't exist
*/
export function initToastIfExists(
elementOrSelector: string | HTMLElement | JQuery,
options?: Partial<Toast.Options>,
): Toast | null {
const element = getElement(elementOrSelector);
if (!element) return null;
return new Toast(element, options);
}
/**
* Show a toast
* @param elementOrSelector Element or selector for the toast
*/
export function showToast(elementOrSelector: string | HTMLElement | JQuery): void {
const element = getElement(elementOrSelector);
if (!element) return;
const toast = Toast.getInstance(element) || new Toast(element);
toast.show();
}
/**
* Hide a toast
* @param elementOrSelector Element or selector for the toast
*/
export function hideToast(elementOrSelector: string | HTMLElement | JQuery): void {
const element = getElement(elementOrSelector);
if (!element) return;
const toast = Toast.getInstance(element);
if (toast) toast.hide();
}
/**
* Initialize a dropdown
* @param elementOrSelector Element or selector for the dropdown
* @param options Dropdown options
* @returns Dropdown instance
* @throws Error if the element cannot be found
*/
export function initDropdown(
elementOrSelector: string | HTMLElement | JQuery,
options?: Partial<Dropdown.Options>,
): Dropdown {
const element = getElement(elementOrSelector);
if (!element) throw new Error(`Failed to find element for dropdown: ${elementOrSelector}`);
return new Dropdown(element, options);
}
/**
* Initialize a dropdown if the element exists, returning null otherwise
* @param elementOrSelector Element or selector for the dropdown
* @param options Dropdown options
* @returns Dropdown instance or null if the element cannot be found
*/
export function initDropdownIfExists(
elementOrSelector: string | HTMLElement | JQuery,
options?: Partial<Dropdown.Options>,
): Dropdown | null {
const element = getElement(elementOrSelector);
if (!element) return null;
return new Dropdown(element, options);
}
/**
* Get an existing dropdown instance for an element
* @param elementOrSelector Element or selector for the dropdown
* @returns Existing dropdown instance or null if not found
*/
export function getDropdownInstance(elementOrSelector: string | HTMLElement | JQuery): Dropdown | null {
const element = getElement(elementOrSelector);
if (!element) return null;
return Dropdown.getInstance(element);
}
/**
* Show a dropdown
* @param elementOrSelector Element or selector for the dropdown
*/
export function showDropdown(elementOrSelector: string | HTMLElement | JQuery): void {
const element = getElement(elementOrSelector);
if (!element) return;
const dropdown = Dropdown.getInstance(element) || new Dropdown(element);
dropdown.show();
}
/**
* Hide a dropdown
* @param elementOrSelector Element or selector for the dropdown
*/
export function hideDropdown(elementOrSelector: string | HTMLElement | JQuery): void {
const element = getElement(elementOrSelector);
if (!element) return;
const dropdown = Dropdown.getInstance(element);
if (dropdown) dropdown.hide();
}
/**
* Initialize a tooltip
* @param elementOrSelector Element or selector for the tooltip
* @param options Tooltip options
* @returns Tooltip instance
*/
export function initTooltip(
elementOrSelector: string | HTMLElement | JQuery,
options?: Partial<Tooltip.Options>,
): Tooltip {
const element = getElement(elementOrSelector);
if (!element) throw new Error(`Failed to find element for tooltip: ${elementOrSelector}`);
return new Tooltip(element, options);
}
/**
* Initialize a tooltip if the element exists
* @param elementOrSelector Element or selector for the tooltip
* @param options Tooltip options
* @returns Tooltip instance or null if element doesn't exist
*/
export function initTooltipIfExists(
elementOrSelector: string | HTMLElement | JQuery,
options?: Partial<Tooltip.Options>,
): Tooltip | null {
const element = getElement(elementOrSelector);
if (!element) return null;
return new Tooltip(element, options);
}
/**
* Initialize a popover
* @param elementOrSelector Element or selector for the popover
* @param options Popover options
* @returns Popover instance
* @throws Error if the element cannot be found
*/
export function initPopover(
elementOrSelector: string | HTMLElement | JQuery,
options?: Partial<Popover.Options>,
): Popover {
const element = getElement(elementOrSelector);
if (!element) throw new Error(`Failed to find element for popover: ${elementOrSelector}`);
return new Popover(element, options);
}
/**
* Initialize a popover if the element exists, returning null otherwise
* @param elementOrSelector Element or selector for the popover
* @param options Popover options
* @returns Popover instance or null if the element cannot be found
*/
export function initPopoverIfExists(
elementOrSelector: string | HTMLElement | JQuery,
options?: Partial<Popover.Options>,
): Popover | null {
const element = getElement(elementOrSelector);
if (!element) return null;
return new Popover(element, options);
}
/**
* Get an existing popover instance for an element
* @param elementOrSelector Element or selector for the popover
* @returns Existing popover instance or null if not found
*/
export function getPopoverInstance(elementOrSelector: string | HTMLElement | JQuery): Popover | null {
const element = getElement(elementOrSelector);
if (!element) return null;
return Popover.getInstance(element);
}
/**
* Initialize a tab
* @param elementOrSelector Element or selector for the tab
* @returns Tab instance
*/
export function initTab(elementOrSelector: string | HTMLElement | JQuery): Tab {
const element = getElement(elementOrSelector);
if (!element) throw new Error(`Failed to find element for tab: ${elementOrSelector}`);
return new Tab(element);
}
/**
* Initialize a tab if the element exists
* @param elementOrSelector Element or selector for the tab
* @returns Tab instance or null if element doesn't exist
*/
export function initTabIfExists(elementOrSelector: string | HTMLElement | JQuery): Tab | null {
const element = getElement(elementOrSelector);
if (!element) return null;
return new Tab(element);
}
/**
* Initialize a collapse
* @param elementOrSelector Element or selector for the collapse
* @param options Collapse options
* @returns Collapse instance
*/
export function initCollapse(
elementOrSelector: string | HTMLElement | JQuery,
options?: Partial<Collapse.Options>,
): Collapse {
const element = getElement(elementOrSelector);
if (!element) throw new Error(`Failed to find element for collapse: ${elementOrSelector}`);
return new Collapse(element, options);
}
/**
* Initialize a collapse if the element exists
* @param elementOrSelector Element or selector for the collapse
* @param options Collapse options
* @returns Collapse instance or null if element doesn't exist
*/
export function initCollapseIfExists(
elementOrSelector: string | HTMLElement | JQuery,
options?: Partial<Collapse.Options>,
): Collapse | null {
const element = getElement(elementOrSelector);
if (!element) return null;
return new Collapse(element, options);
}
/**
* Hide an existing popover if it exists
* @param elementOrSelector Element or selector for the popover
*/
export function hidePopover(elementOrSelector: string | HTMLElement | JQuery): void {
const popover = getPopoverInstance(elementOrSelector);
if (popover) popover.hide();
}
/**
* Show an existing popover if it exists
* @param elementOrSelector Element or selector for the popover
*/
export function showPopover(elementOrSelector: string | HTMLElement | JQuery): void {
const popover = getPopoverInstance(elementOrSelector);
if (popover) popover.show();
}
/**
* Show an existing modal if it exists (uses existing instance only)
* @param elementOrSelector Element or selector for the modal
*/
export function showModalIfExists(elementOrSelector: string | HTMLElement | JQuery): void {
const modal = getModalInstance(elementOrSelector);
if (modal) modal.show();
}

View File

@@ -28,7 +28,7 @@ import {SentryCapture, SetupSentry, setSentryLayout} from './sentry.js';
SetupSentry(); SetupSentry();
import 'whatwg-fetch'; import 'whatwg-fetch';
import 'popper.js'; import '@popperjs/core';
import 'bootstrap'; import 'bootstrap';
import $ from 'jquery'; import $ from 'jquery';
@@ -62,6 +62,7 @@ import {ComponentConfig, EmptyCompilerState, StateWithId, StateWithLanguage} fro
import {CompilerExplorerOptions} from './global.js'; import {CompilerExplorerOptions} from './global.js';
import * as utils from '../shared/common-utils.js'; import * as utils from '../shared/common-utils.js';
import * as BootstrapUtils from './bootstrap-utils.js';
import {ParseFiltersAndOutputOptions} from './features/filters.interfaces.js'; import {ParseFiltersAndOutputOptions} from './features/filters.interfaces.js';
import {localStorage, sessionThenLocalStorage} from './local.js'; import {localStorage, sessionThenLocalStorage} from './local.js';
import {Printerinator} from './print-view.js'; import {Printerinator} from './print-view.js';
@@ -79,7 +80,7 @@ if (!window.PRODUCTION && !options.embedded) {
//css //css
require('bootstrap/dist/css/bootstrap.min.css'); require('bootstrap/dist/css/bootstrap.min.css');
require('golden-layout/src/css/goldenlayout-base.css'); require('golden-layout/src/css/goldenlayout-base.css');
require('tom-select/dist/css/tom-select.bootstrap4.css'); require('tom-select/dist/css/tom-select.bootstrap5.css');
require('./styles/colours.scss'); require('./styles/colours.scss');
require('./styles/explorer.scss'); require('./styles/explorer.scss');
@@ -225,7 +226,7 @@ function setupButtons(options: CompilerExplorerOptions, hub: Hub) {
window.location.reload(); window.location.reload();
}); });
$('#history').modal(); BootstrapUtils.showModal('#history');
}); });
$('#ui-apply-default-font-scale').on('click', () => { $('#ui-apply-default-font-scale').on('click', () => {
@@ -530,7 +531,7 @@ function initShortlinkInfoButton() {
buttonText.html(''); buttonText.html('');
const button = $('.shortlinkInfo'); const button = $('.shortlinkInfo');
button.popover({ BootstrapUtils.initPopover(button, {
html: true, html: true,
title: 'Link created', title: 'Link created',
content: formatISODate(dt, true), content: formatISODate(dt, true),
@@ -677,11 +678,13 @@ function start() {
setupButtons(options, hub); setupButtons(options, hub);
} }
const addDropdown = $('#addDropdown');
function setupAdd<C>(thing: JQuery, func: () => ComponentConfig<C>) { function setupAdd<C>(thing: JQuery, func: () => ComponentConfig<C>) {
(layout.createDragSource(thing, func as any) as any)._dragListener.on('dragStart', () => { (layout.createDragSource(thing, func as any) as any)._dragListener.on('dragStart', () => {
addDropdown.dropdown('toggle'); const addDropdown = unwrap(
BootstrapUtils.getDropdownInstance('#addDropdown'),
'Dropdown instance not found for #addDropdown',
);
addDropdown.toggle();
}); });
thing.on('click', () => { thing.on('click', () => {

View File

@@ -30,7 +30,7 @@ function ensureShownMessage(message: string, motdNode: JQuery) {
motdNode.find('.content').html(message); motdNode.find('.content').html(message);
motdNode.removeClass('d-none'); motdNode.removeClass('d-none');
motdNode motdNode
.find('.close') .find('.btn-close')
.on('click', () => { .on('click', () => {
motdNode.addClass('d-none'); motdNode.addClass('d-none');
}) })

View File

@@ -25,8 +25,8 @@
// This jQuery import needs to be here, because noscript.ts is a different entrypoint than the rest of the code. // This jQuery import needs to be here, because noscript.ts is a different entrypoint than the rest of the code.
// See webpack.config.esm.ts -> entry for more details. // See webpack.config.esm.ts -> entry for more details.
import $ from 'jquery'; import $ from 'jquery';
import '@popperjs/core';
import 'bootstrap'; import 'bootstrap';
import 'popper.js';
import {Toggles} from './widgets/toggles.js'; import {Toggles} from './widgets/toggles.js';
import './styles/noscript.scss'; import './styles/noscript.scss';

View File

@@ -26,6 +26,7 @@ import * as fileSaver from 'file-saver';
import $ from 'jquery'; import $ from 'jquery';
import * as monaco from 'monaco-editor'; import * as monaco from 'monaco-editor';
import _ from 'underscore'; import _ from 'underscore';
import * as BootstrapUtils from '../bootstrap-utils.js';
import {Pane} from './pane.js'; import {Pane} from './pane.js';
import {Container} from 'golden-layout'; import {Container} from 'golden-layout';
@@ -281,7 +282,9 @@ export class Cfg extends Pane<CfgState> {
if (this.tooltipOpen) { if (this.tooltipOpen) {
if (!e.target.classList.contains('fold') && $(e.target).parents('.popover.in').length === 0) { if (!e.target.classList.contains('fold') && $(e.target).parents('.popover.in').length === 0) {
this.tooltipOpen = false; this.tooltipOpen = false;
$('.fold').popover('hide'); $('.fold').each((_, element) => {
BootstrapUtils.hidePopover(element);
});
} }
} }
}); });
@@ -389,25 +392,27 @@ export class Cfg extends Pane<CfgState> {
}" aria-describedby="wtf">&#8943;</span>`; }" aria-describedby="wtf">&#8943;</span>`;
} }
div.innerHTML = lines.join('<br/>'); div.innerHTML = lines.join('<br/>');
for (const fold of div.getElementsByClassName('fold')) { for (const foldElement of div.getElementsByClassName('fold')) {
$(fold) const fold = foldElement as HTMLElement;
.popover({
content: unwrap(fold.getAttribute('data-extra')), BootstrapUtils.initPopover(fold, {
html: true, content: unwrap(fold.getAttribute('data-extra')),
placement: 'top', html: true,
template: placement: 'top',
'<div class="popover cfg-fold-popover" role="tooltip">' + template:
'<div class="arrow"></div>' + '<div class="popover cfg-fold-popover" role="tooltip">' +
'<h3 class="popover-header"></h3>' + '<div class="arrow"></div>' +
'<div class="popover-body"></div>' + '<h3 class="popover-header"></h3>' +
'</div>', '<div class="popover-body"></div>' +
}) '</div>',
.on('show.bs.popover', () => { });
this.tooltipOpen = true;
}) BootstrapUtils.setElementEventHandler(fold, 'show.bs.popover', () => {
.on('hide.bs.popover', () => { this.tooltipOpen = true;
this.tooltipOpen = false; });
}); BootstrapUtils.setElementEventHandler(fold, 'hide.bs.popover', () => {
this.tooltipOpen = false;
});
} }
// So because this is async there's a race condition here if you rapidly switch functions. // So because this is async there's a race condition here if you rapidly switch functions.
// This can be triggered by loading an example program. Because the fix going to be tricky I'll defer // This can be triggered by loading an example program. Because the fix going to be tricky I'll defer
@@ -500,7 +505,11 @@ export class Cfg extends Pane<CfgState> {
// Display the cfg for the specified function if it exists // Display the cfg for the specified function if it exists
// This function sets this.state.selectedFunction if the input is non-null and valid // This function sets this.state.selectedFunction if the input is non-null and valid
async selectFunction(name: string | null) { async selectFunction(name: string | null) {
$('.fold').popover('dispose'); $('.fold').each((_, element) => {
const popover = BootstrapUtils.getPopoverInstance(element);
if (popover) popover.dispose();
// We need to dispose here, not just hide
});
this.blockContainer.innerHTML = ''; this.blockContainer.innerHTML = '';
this.svg.innerHTML = ''; this.svg.innerHTML = '';
this.estimatedPNGSize.innerHTML = ''; this.estimatedPNGSize.innerHTML = '';
@@ -669,7 +678,9 @@ export class Cfg extends Pane<CfgState> {
const topBarHeight = utils.updateAndCalcTopBarHeight(this.domRoot, this.topBar, this.hideable); const topBarHeight = utils.updateAndCalcTopBarHeight(this.domRoot, this.topBar, this.hideable);
this.graphContainer.style.width = `${unwrap(this.domRoot.width())}px`; this.graphContainer.style.width = `${unwrap(this.domRoot.width())}px`;
this.graphContainer.style.height = `${unwrap(this.domRoot.height()) - topBarHeight}px`; this.graphContainer.style.height = `${unwrap(this.domRoot.height()) - topBarHeight}px`;
$('.fold').popover('hide'); $('.fold').each((_, element) => {
BootstrapUtils.hidePopover(element);
});
}); });
} }

View File

@@ -42,6 +42,7 @@ import {
import {CompilerInfo} from '../../types/compiler.interfaces.js'; import {CompilerInfo} from '../../types/compiler.interfaces.js';
import {ResultLine} from '../../types/resultline/resultline.interfaces.js'; import {ResultLine} from '../../types/resultline/resultline.interfaces.js';
import {getAssemblyDocumentation} from '../api/api.js'; import {getAssemblyDocumentation} from '../api/api.js';
import * as BootstrapUtils from '../bootstrap-utils.js';
import * as codeLensHandler from '../codelens-handler.js'; import * as codeLensHandler from '../codelens-handler.js';
import * as colour from '../colour.js'; import * as colour from '../colour.js';
import {OptPipelineBackendOptions} from '../compilation/opt-pipeline-output.interfaces.js'; import {OptPipelineBackendOptions} from '../compilation/opt-pipeline-output.interfaces.js';
@@ -678,7 +679,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
const newPaneDropdown = this.domRoot.find('.new-pane-dropdown'); const newPaneDropdown = this.domRoot.find('.new-pane-dropdown');
const hidePaneAdder = () => { const hidePaneAdder = () => {
newPaneDropdown.dropdown('hide'); BootstrapUtils.hideDropdown(newPaneDropdown);
}; };
// Note that the .d.ts file lies in more than 1 way! // Note that the .d.ts file lies in more than 1 way!
@@ -729,7 +730,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
.createDragSource(this.flagsButton, createFlagsView as any) .createDragSource(this.flagsButton, createFlagsView as any)
// @ts-ignore // @ts-ignore
._dragListener.on('dragStart', () => popularArgumentsMenu.dropdown('hide')); ._dragListener.on('dragStart', () => BootstrapUtils.hideDropdown(popularArgumentsMenu));
this.flagsButton.on('click', () => { this.flagsButton.on('click', () => {
const insertPoint = const insertPoint =
@@ -1868,7 +1869,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
dismissTime: 10000, dismissTime: 10000,
onBeforeShow: elem => { onBeforeShow: elem => {
elem.find('#miracle_emulink').on('click', () => { elem.find('#miracle_emulink').on('click', () => {
dialog.modal(); BootstrapUtils.showModal(dialog);
const miracleMenuFrame = dialog.find('#miracleemuframe')[0]; const miracleMenuFrame = dialog.find('#miracleemuframe')[0];
assert(miracleMenuFrame instanceof HTMLIFrameElement); assert(miracleMenuFrame instanceof HTMLIFrameElement);
@@ -1896,7 +1897,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
dismissTime: 10000, dismissTime: 10000,
onBeforeShow: elem => { onBeforeShow: elem => {
elem.find('#jsspeccy_emulink').on('click', () => { elem.find('#jsspeccy_emulink').on('click', () => {
dialog.modal(); BootstrapUtils.showModal(dialog);
const speccyemuframe = dialog.find('#speccyemuframe')[0]; const speccyemuframe = dialog.find('#speccyemuframe')[0];
assert(speccyemuframe instanceof HTMLIFrameElement); assert(speccyemuframe instanceof HTMLIFrameElement);
@@ -1923,7 +1924,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
dismissTime: 10000, dismissTime: 10000,
onBeforeShow: elem => { onBeforeShow: elem => {
elem.find('#emulink').on('click', () => { elem.find('#emulink').on('click', () => {
dialog.modal(); BootstrapUtils.showModal(dialog);
const jsbeebemuframe = dialog.find('#jsbeebemuframe')[0]; const jsbeebemuframe = dialog.find('#jsbeebemuframe')[0];
assert(jsbeebemuframe instanceof HTMLIFrameElement); assert(jsbeebemuframe instanceof HTMLIFrameElement);
@@ -1950,7 +1951,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
dismissTime: 10000, dismissTime: 10000,
onBeforeShow: elem => { onBeforeShow: elem => {
elem.find('#emulink').on('click', () => { elem.find('#emulink').on('click', () => {
dialog.modal(); BootstrapUtils.showModal(dialog);
const jsnesemuframe = dialog.find('#jsnesemuframe')[0]; const jsnesemuframe = dialog.find('#jsnesemuframe')[0];
assert(jsnesemuframe instanceof HTMLIFrameElement); assert(jsnesemuframe instanceof HTMLIFrameElement);
@@ -2683,7 +2684,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
initToolButtons(): void { initToolButtons(): void {
this.toolsMenu = this.domRoot.find('.new-tool-dropdown'); this.toolsMenu = this.domRoot.find('.new-tool-dropdown');
const hideToolDropdown = () => { const hideToolDropdown = () => {
this.toolsMenu?.dropdown('hide'); if (this.toolsMenu) BootstrapUtils.hideDropdown(this.toolsMenu);
}; };
this.toolsMenu.empty(); this.toolsMenu.empty();
@@ -3053,14 +3054,14 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
this.prependOptions.has(target as unknown as Element).length === 0 && this.prependOptions.has(target as unknown as Element).length === 0 &&
target.closest('.popover').length === 0 target.closest('.popover').length === 0
) )
this.prependOptions.popover('hide'); BootstrapUtils.hidePopover(this.prependOptions);
if ( if (
!target.is(this.fullCompilerName) && !target.is(this.fullCompilerName) &&
this.fullCompilerName.has(target as unknown as Element).length === 0 && this.fullCompilerName.has(target as unknown as Element).length === 0 &&
target.closest('.popover').length === 0 target.closest('.popover').length === 0
) )
this.fullCompilerName.popover('hide'); BootstrapUtils.hidePopover(this.fullCompilerName);
}); });
} }
@@ -3104,7 +3105,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
// Dismiss the popover on escape. // Dismiss the popover on escape.
$(document).on('keyup.editable', e => { $(document).on('keyup.editable', e => {
if (e.which === 27) { if (e.which === 27) {
this.libsButton.popover('hide'); BootstrapUtils.hidePopover(this.libsButton);
} }
}); });
@@ -3118,7 +3119,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
elem.has(target as unknown as Element).length === 0 && elem.has(target as unknown as Element).length === 0 &&
target.closest('.popover').length === 0 target.closest('.popover').length === 0
) { ) {
elem.popover('hide'); BootstrapUtils.hidePopover(elem);
} }
}); });
} }
@@ -3460,8 +3461,13 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
setCompilationOptionsPopover(content: string | null, warnings: string[]): void { setCompilationOptionsPopover(content: string | null, warnings: string[]): void {
const infoLine = const infoLine =
'<div class="compiler-arg-warning info">You can configure icon animations in Settings>Compilation</div>\n'; '<div class="compiler-arg-warning info">You can configure icon animations in Settings>Compilation</div>\n';
this.prependOptions.popover('dispose');
this.prependOptions.popover({ // Dispose any existing popover
const existingPopover = BootstrapUtils.getPopoverInstance(this.prependOptions);
if (existingPopover) existingPopover.dispose();
// Create new popover
BootstrapUtils.initPopover(this.prependOptions, {
content: content:
warnings.map(w => `<div class="compiler-arg-warning">${w}</div>`).join('\n') + warnings.map(w => `<div class="compiler-arg-warning">${w}</div>`).join('\n') +
'\n' + '\n' +

View File

@@ -29,6 +29,7 @@ import {escapeHTML, unique} from '../../shared/common-utils.js';
import {CompilationResult} from '../../types/compilation/compilation.interfaces.js'; import {CompilationResult} from '../../types/compilation/compilation.interfaces.js';
import {CompilerInfo} from '../../types/compiler.interfaces.js'; import {CompilerInfo} from '../../types/compiler.interfaces.js';
import {unwrapString} from '../assert.js'; import {unwrapString} from '../assert.js';
import * as BootstrapUtils from '../bootstrap-utils.js';
import {CompilationStatus} from '../compiler-service.interfaces.js'; import {CompilationStatus} from '../compiler-service.interfaces.js';
import {CompilerService} from '../compiler-service.js'; import {CompilerService} from '../compiler-service.js';
import * as Components from '../components.js'; import * as Components from '../components.js';
@@ -100,7 +101,7 @@ export class Conformance extends Pane<ConformanceViewState> {
// Dismiss the popover on escape. // Dismiss the popover on escape.
$(document).on('keyup.editable', e => { $(document).on('keyup.editable', e => {
if (e.which === 27) { if (e.which === 27) {
this.libsButton.popover('hide'); BootstrapUtils.hidePopover(this.libsButton);
} }
}); });
@@ -114,7 +115,7 @@ export class Conformance extends Pane<ConformanceViewState> {
elem.has(target as unknown as Element).length === 0 && elem.has(target as unknown as Element).length === 0 &&
target.closest('.popover').length === 0 target.closest('.popover').length === 0
) { ) {
elem.popover('hide'); BootstrapUtils.hidePopover(elem);
} }
}); });
} }
@@ -150,7 +151,7 @@ export class Conformance extends Pane<ConformanceViewState> {
this.conformanceContentRoot = this.domRoot.find('.conformance-wrapper'); this.conformanceContentRoot = this.domRoot.find('.conformance-wrapper');
this.selectorList = this.domRoot.find('.compiler-list'); this.selectorList = this.domRoot.find('.compiler-list');
this.addCompilerButton = this.domRoot.find('.add-compiler'); this.addCompilerButton = this.domRoot.find('.add-compiler');
this.selectorTemplate = $('#compiler-selector').find('.form-row'); this.selectorTemplate = $('#compiler-selector').find('.row');
this.topBar = this.domRoot.find('.top-bar'); this.topBar = this.domRoot.find('.top-bar');
this.libsButton = this.topBar.find('.show-libs'); this.libsButton = this.topBar.find('.show-libs');
this.hideable = this.domRoot.find('.hideable'); this.hideable = this.domRoot.find('.hideable');
@@ -225,15 +226,11 @@ export class Conformance extends Pane<ConformanceViewState> {
.on('change', onOptionsChange) .on('change', onOptionsChange)
.on('keyup', onOptionsChange); .on('keyup', onOptionsChange);
newSelector newSelector.find('.close-compiler').on('click', () => {
.find('.close') this.removeCompilerPicker(newCompilerEntry);
.not('.extract-compiler') });
.not('.copy-compiler')
.on('click', () => {
this.removeCompilerPicker(newCompilerEntry);
});
newSelector.find('.close.copy-compiler').on('click', () => { newSelector.find('.copy-compiler').on('click', () => {
const config: AddCompilerPickerConfig = { const config: AddCompilerPickerConfig = {
compilerId: newCompilerEntry.picker?.lastCompilerId ?? '', compilerId: newCompilerEntry.picker?.lastCompilerId ?? '',
options: newCompilerEntry.optionsField?.val() || '', options: newCompilerEntry.optionsField?.val() || '',
@@ -301,15 +298,19 @@ export class Conformance extends Pane<ConformanceViewState> {
): void {} ): void {}
setCompilationOptionsPopover(element: JQuery<HTMLElement> | null, content: string): void { setCompilationOptionsPopover(element: JQuery<HTMLElement> | null, content: string): void {
element?.popover('dispose'); if (element) {
element?.popover({ const existingPopover = BootstrapUtils.getPopoverInstance(element);
content: content || 'No options in use', if (existingPopover) existingPopover.dispose();
template:
'<div class="popover' + BootstrapUtils.initPopover(element, {
(content ? ' compiler-options-popover' : '') + content: content || 'No options in use',
'" role="tooltip"><div class="arrow"></div>' + template:
'<h3 class="popover-header"></h3><div class="popover-body"></div></div>', '<div class="popover' +
}); (content ? ' compiler-options-popover' : '') +
'" role="tooltip"><div class="arrow"></div>' +
'<h3 class="popover-header"></h3><div class="popover-body"></div></div>',
});
}
} }
removeCompilerPicker(compilerEntry: CompilerEntry): void { removeCompilerPicker(compilerEntry: CompilerEntry): void {

View File

@@ -482,10 +482,7 @@ export class Diff extends MonacoPane<monaco.editor.IStandaloneDiffEditor, DiffSt
) { ) {
if (!compiler) return; if (!compiler) return;
options = options || ''; options = options || '';
let name = compiler.name + ' ' + options; const name = compiler.name + ' ' + options;
// TODO: tomselect doesn't play nicely with CSS tricks for truncation; this is the best I can do
const maxLength = 30;
if (name.length > maxLength - 3) name = name.substring(0, maxLength - 3) + '...';
this.compilers[id] = { this.compilers[id] = {
id: id, id: id,
name: name, name: name,

View File

@@ -29,6 +29,7 @@ import * as monaco from 'monaco-editor';
import * as monacoVim from 'monaco-vim'; import * as monacoVim from 'monaco-vim';
import TomSelect from 'tom-select'; import TomSelect from 'tom-select';
import _ from 'underscore'; import _ from 'underscore';
import * as BootstrapUtils from '../bootstrap-utils.js';
import * as colour from '../colour.js'; import * as colour from '../colour.js';
import * as Components from '../components.js'; import * as Components from '../components.js';
import * as monacoConfig from '../monaco-config.js'; import * as monacoConfig from '../monaco-config.js';
@@ -472,7 +473,7 @@ export class Editor extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Edit
} }
enableVim(): void { enableVim(): void {
const statusElem = this.domRoot.find('#v-status')[0]; const statusElem = this.domRoot.find('.v-status')[0];
const vimMode = monacoVim.initVimMode(this.editor, statusElem); const vimMode = monacoVim.initVimMode(this.editor, statusElem);
this.vimMode = vimMode; this.vimMode = vimMode;
this.vimFlag.prop('class', 'btn btn-info'); this.vimFlag.prop('class', 'btn btn-info');
@@ -481,7 +482,7 @@ export class Editor extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Edit
disableVim(): void { disableVim(): void {
this.vimMode.dispose(); this.vimMode.dispose();
this.domRoot.find('#v-status').html(''); this.domRoot.find('.v-status').html('');
this.vimFlag.prop('class', 'btn btn-light'); this.vimFlag.prop('class', 'btn btn-light');
(this.editor as any).vimInUse = false; (this.editor as any).vimInUse = false;
} }
@@ -520,8 +521,8 @@ export class Editor extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Edit
this.addExecutorButton = this.domRoot.find('.btn.add-executor'); this.addExecutorButton = this.domRoot.find('.btn.add-executor');
this.conformanceViewerButton = this.domRoot.find('.btn.conformance'); this.conformanceViewerButton = this.domRoot.find('.btn.conformance');
const addEditorButton = this.domRoot.find('.btn.add-editor'); const addEditorButton = this.domRoot.find('.btn.add-editor');
const toggleVimButton = this.domRoot.find('#vim-flag'); const toggleVimButton = this.domRoot.find('.vim-flag');
this.vimFlag = this.domRoot.find('#vim-flag'); this.vimFlag = this.domRoot.find('.vim-flag');
toggleVimButton.on('click', () => { toggleVimButton.on('click', () => {
if ((this.editor as any).vimInUse) { if ((this.editor as any).vimInUse) {
this.disableVim(); this.disableVim();
@@ -541,7 +542,7 @@ export class Editor extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Edit
}); });
this.languageInfoButton = this.domRoot.find('.language-info'); this.languageInfoButton = this.domRoot.find('.language-info');
this.languageInfoButton.popover({}); BootstrapUtils.initPopover(this.languageInfoButton);
this.languageBtn = this.domRoot.find('.change-language'); this.languageBtn = this.domRoot.find('.change-language');
const changeLanguageButton = this.languageBtn[0]; const changeLanguageButton = this.languageBtn[0];
assert(changeLanguageButton instanceof HTMLSelectElement); assert(changeLanguageButton instanceof HTMLSelectElement);
@@ -598,7 +599,10 @@ export class Editor extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Edit
.createDragSource(dragSource, dragConfig) .createDragSource(dragSource, dragConfig)
// @ts-expect-error: createDragSource returns not void // @ts-expect-error: createDragSource returns not void
._dragListener.on('dragStart', () => { ._dragListener.on('dragStart', () => {
paneAdderDropdown.dropdown('toggle'); const dropdown = BootstrapUtils.getDropdownInstance(paneAdderDropdown);
if (dropdown) {
dropdown.toggle();
}
}); });
dragSource.on('click', () => { dragSource.on('click', () => {
@@ -1921,7 +1925,7 @@ export class Editor extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Edit
): string { ): string {
let result = let result =
'<div class="d-flex" style="align-items: center">' + '<div class="d-flex" style="align-items: center">' +
'<div class="mr-1 d-flex" style="align-items: center">' + '<div class="me-1 d-flex" style="align-items: center">' +
'<img src="' + '<img src="' +
(data.logoData ? data.logoData : '') + (data.logoData ? data.logoData : '') +
'" class="' + '" class="' +
@@ -1961,9 +1965,12 @@ export class Editor extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Edit
onCompiler(compilerId: number, compiler: unknown, options: string, editorId: number, treeId: number): void {} onCompiler(compilerId: number, compiler: unknown, options: string, editorId: number, treeId: number): void {}
updateLanguageTooltip() { updateLanguageTooltip() {
this.languageInfoButton.popover('dispose'); // Dispose existing popover instance
const existingPopover = BootstrapUtils.getPopoverInstance(this.languageInfoButton);
if (existingPopover) existingPopover.dispose();
if (this.currentLanguage?.tooltip) { if (this.currentLanguage?.tooltip) {
this.languageInfoButton.popover({ BootstrapUtils.initPopover(this.languageInfoButton, {
title: 'More info about this language', title: 'More info about this language',
content: this.currentLanguage.tooltip, content: this.currentLanguage.tooltip,
container: 'body', container: 'body',

View File

@@ -38,6 +38,7 @@ import {Language} from '../../types/languages.interfaces.js';
import {ResultLine} from '../../types/resultline/resultline.interfaces.js'; import {ResultLine} from '../../types/resultline/resultline.interfaces.js';
import {Artifact, ArtifactType} from '../../types/tool.interfaces.js'; import {Artifact, ArtifactType} from '../../types/tool.interfaces.js';
import {Filter as AnsiToHtml} from '../ansi-to-html.js'; import {Filter as AnsiToHtml} from '../ansi-to-html.js';
import * as BootstrapUtils from '../bootstrap-utils.js';
import {CompilationStatus as CompilerServiceCompilationStatus} from '../compiler-service.interfaces.js'; import {CompilationStatus as CompilerServiceCompilationStatus} from '../compiler-service.interfaces.js';
import {CompilerService} from '../compiler-service.js'; import {CompilerService} from '../compiler-service.js';
import {ICompilerShared} from '../compiler-shared.interfaces.js'; import {ICompilerShared} from '../compiler-shared.interfaces.js';
@@ -799,15 +800,19 @@ export class Executor extends Pane<ExecutorState> {
!target.is(this.prependOptions) && !target.is(this.prependOptions) &&
this.prependOptions.has(target as any).length === 0 && this.prependOptions.has(target as any).length === 0 &&
target.closest('.popover').length === 0 target.closest('.popover').length === 0
) ) {
this.prependOptions.popover('hide'); const popover = BootstrapUtils.getPopoverInstance(this.prependOptions);
if (popover) popover.hide();
}
if ( if (
!target.is(this.fullCompilerName) && !target.is(this.fullCompilerName) &&
this.fullCompilerName.has(target as any).length === 0 && this.fullCompilerName.has(target as any).length === 0 &&
target.closest('.popover').length === 0 target.closest('.popover').length === 0
) ) {
this.fullCompilerName.popover('hide'); const popover = BootstrapUtils.getPopoverInstance(this.fullCompilerName);
if (popover) popover.hide();
}
}); });
this.optionsField.val(this.options); this.optionsField.val(this.options);
@@ -963,7 +968,8 @@ export class Executor extends Pane<ExecutorState> {
// Dismiss the popover on escape. // Dismiss the popover on escape.
$(document).on('keyup.editable', e => { $(document).on('keyup.editable', e => {
if (e.which === 27) { if (e.which === 27) {
this.libsButton.popover('hide'); const popover = BootstrapUtils.getPopoverInstance(this.libsButton);
if (popover) popover.hide();
} }
}); });
@@ -997,7 +1003,8 @@ export class Executor extends Pane<ExecutorState> {
const elem = this.libsButton; const elem = this.libsButton;
const target = $(e.target); const target = $(e.target);
if (!target.is(elem) && elem.has(target as any).length === 0 && target.closest('.popover').length === 0) { if (!target.is(elem) && elem.has(target as any).length === 0 && target.closest('.popover').length === 0) {
elem.popover('hide'); const popover = BootstrapUtils.getPopoverInstance(elem);
if (popover) popover.hide();
} }
}); });
@@ -1186,8 +1193,12 @@ export class Executor extends Pane<ExecutorState> {
} }
setCompilationOptionsPopover(content: string | null) { setCompilationOptionsPopover(content: string | null) {
this.prependOptions.popover('dispose'); // Dispose of existing popover
this.prependOptions.popover({ const existingPopover = BootstrapUtils.getPopoverInstance(this.prependOptions);
if (existingPopover) existingPopover.dispose();
// Initialize new popover
BootstrapUtils.initPopover(this.prependOptions, {
content: content || 'No options in use', content: content || 'No options in use',
template: template:
'<div class="popover' + '<div class="popover' +

View File

@@ -29,6 +29,7 @@ import TomSelect from 'tom-select';
import _ from 'underscore'; import _ from 'underscore';
import {escapeHTML} from '../../shared/common-utils.js'; import {escapeHTML} from '../../shared/common-utils.js';
import {assert, unwrap, unwrapString} from '../assert.js'; import {assert, unwrap, unwrapString} from '../assert.js';
import * as BootstrapUtils from '../bootstrap-utils.js';
import * as Components from '../components.js'; import * as Components from '../components.js';
import {EventHub} from '../event-hub.js'; import {EventHub} from '../event-hub.js';
import {Hub} from '../hub.js'; import {Hub} from '../hub.js';
@@ -448,7 +449,11 @@ export class Tree {
(this.container.layoutManager.createDragSource(dragSource, dragConfig.bind(this)) as any)._dragListener.on( (this.container.layoutManager.createDragSource(dragSource, dragConfig.bind(this)) as any)._dragListener.on(
'dragStart', 'dragStart',
() => { () => {
this.domRoot.find('.add-pane').dropdown('toggle'); const dropdown = this.domRoot.find('.add-pane');
const dropdownInstance = BootstrapUtils.getDropdownInstance(dropdown);
if (dropdownInstance) {
dropdownInstance.toggle();
}
}, },
); );

View File

@@ -48,7 +48,7 @@ export function setupRealDark(hub: Hub) {
$('#settings .theme').val('real-dark').trigger('change'); $('#settings .theme').val('real-dark').trigger('change');
} }
}); });
$('#true-dark .content .close').on('click', e => { $('#true-dark .content .dark-close').on('click', e => {
local.localStorage.set(localKey, 'hidden'); local.localStorage.set(localKey, 'hidden');
toggleButton(); toggleButton();
toggleOverlay(); toggleOverlay();

View File

@@ -22,18 +22,20 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {Modal, Tooltip} from 'bootstrap';
import ClipboardJS from 'clipboard'; import ClipboardJS from 'clipboard';
import GoldenLayout from 'golden-layout'; import GoldenLayout from 'golden-layout';
import $ from 'jquery'; import $ from 'jquery';
import _ from 'underscore'; import _ from 'underscore';
import {unwrap} from './assert.js';
import * as BootstrapUtils from './bootstrap-utils.js';
import {sessionThenLocalStorage} from './local.js'; import {sessionThenLocalStorage} from './local.js';
import {options} from './options.js'; import {options} from './options.js';
import * as url from './url.js'; import * as url from './url.js';
import ClickEvent = JQuery.ClickEvent;
import TriggeredEvent = JQuery.TriggeredEvent;
import {SentryCapture} from './sentry.js'; import {SentryCapture} from './sentry.js';
import {Settings, SiteSettings} from './settings.js'; import {Settings, SiteSettings} from './settings.js';
import ClickEvent = JQuery.ClickEvent;
const cloneDeep = require('lodash.clonedeep'); const cloneDeep = require('lodash.clonedeep');
@@ -87,24 +89,31 @@ export class Sharing {
private layout: GoldenLayout; private layout: GoldenLayout;
private lastState: any; private lastState: any;
private share: JQuery; private readonly share: JQuery;
private shareShort: JQuery; private readonly shareTooltipTarget: JQuery;
private shareFull: JQuery; private readonly shareShort: JQuery;
private shareEmbed: JQuery; private readonly shareFull: JQuery;
private readonly shareEmbed: JQuery;
private settings: SiteSettings; private settings: SiteSettings;
private clippyButton: ClipboardJS | null; private clippyButton: ClipboardJS | null;
private readonly shareLinkDialog: HTMLElement;
constructor(layout: any) { constructor(layout: any) {
this.layout = layout; this.layout = layout;
this.lastState = null; this.lastState = null;
this.shareLinkDialog = unwrap(document.getElementById('sharelinkdialog'), 'Share modal element not found');
this.share = $('#share'); this.share = $('#share');
this.shareTooltipTarget = $('#share-tooltip-target');
this.shareShort = $('#shareShort'); this.shareShort = $('#shareShort');
this.shareFull = $('#shareFull'); this.shareFull = $('#shareFull');
this.shareEmbed = $('#shareEmbed'); this.shareEmbed = $('#shareEmbed');
[this.shareShort, this.shareFull, this.shareEmbed].forEach(el =>
el.on('click', e => BootstrapUtils.showModal(this.shareLinkDialog, e.currentTarget)),
);
this.settings = Settings.getStoredSettings(); this.settings = Settings.getStoredSettings();
this.clippyButton = null; this.clippyButton = null;
@@ -122,9 +131,9 @@ export class Sharing {
}); });
this.layout.on('stateChanged', this.onStateChanged.bind(this)); this.layout.on('stateChanged', this.onStateChanged.bind(this));
$('#sharelinkdialog') BootstrapUtils.initModal(this.shareLinkDialog);
.on('show.bs.modal', this.onOpenModalPane.bind(this)) this.shareLinkDialog.addEventListener('show.bs.modal', this.onOpenModalPane.bind(this));
.on('hidden.bs.modal', this.onCloseModalPane.bind(this)); this.shareLinkDialog.addEventListener('hidden.bs.modal', this.onCloseModalPane.bind(this));
this.layout.eventHub.on('settingsChange', (newSettings: SiteSettings) => { this.layout.eventHub.on('settingsChange', (newSettings: SiteSettings) => {
this.settings = newSettings; this.settings = newSettings;
@@ -176,11 +185,16 @@ export class Sharing {
} }
} }
private onOpenModalPane(event: TriggeredEvent<HTMLElement, undefined, HTMLElement, HTMLElement>): void { private onOpenModalPane(event: Event): void {
// @ts-ignore The property is added by bootstrap const modalEvent = event as Modal.Event;
const button = $(event.relatedTarget); if (!modalEvent.relatedTarget) {
const currentBind = Sharing.bindToLinkType(button.data('bind')); throw new Error('No relatedTarget found in modal event');
const modal = $(event.currentTarget); }
const button = $(modalEvent.relatedTarget);
const bindStr = button.data('bind') as string;
const currentBind = Sharing.bindToLinkType(bindStr);
const modal = $(event.currentTarget as HTMLElement);
const socialSharingElements = modal.find('.socialsharing'); const socialSharingElements = modal.find('.socialsharing');
const permalink = modal.find('.permalink'); const permalink = modal.find('.permalink');
const embedsettings = modal.find('#embedsettings'); const embedsettings = modal.find('#embedsettings');
@@ -262,15 +276,16 @@ export class Sharing {
} }
} }
private onClipButtonPressed(event: ClickEvent, type: LinkType): void { private onClipButtonPressed(event: ClickEvent, type: LinkType): boolean {
// Don't let the modal show up. // Don't let the modal show up.
// We need this because the button is a child of the dropdown-item with a data-toggle=modal // We need this because the button is a child of the dropdown-item with a data-bs-toggle=modal
if (Sharing.isNavigatorClipboardAvailable()) { if (Sharing.isNavigatorClipboardAvailable()) {
event.stopPropagation();
this.copyLinkTypeToClipboard(type); this.copyLinkTypeToClipboard(type);
// As we prevented bubbling, the dropdown won't close by itself. We need to trigger it manually event.stopPropagation();
this.share.dropdown('hide'); // As we prevented bubbling, the dropdown won't close by itself.
BootstrapUtils.hideDropdown(this.share);
} }
return false;
} }
private getLinkOfType(type: LinkType): Promise<string> { private getLinkOfType(type: LinkType): Promise<string> {
@@ -278,7 +293,7 @@ export class Sharing {
return new Promise<string>((resolve, reject) => { return new Promise<string>((resolve, reject) => {
Sharing.getLinks(config, type, (error: any, newUrl: string, extra: string, updateState: boolean) => { Sharing.getLinks(config, type, (error: any, newUrl: string, extra: string, updateState: boolean) => {
if (error || !newUrl) { if (error || !newUrl) {
this.displayTooltip(this.share, 'Oops, something went wrong'); this.displayTooltip(this.shareTooltipTarget, 'Oops, something went wrong');
SentryCapture(error, 'Getting short link failed'); SentryCapture(error, 'Getting short link failed');
reject(); reject();
} else { } else {
@@ -295,7 +310,7 @@ export class Sharing {
const config = this.layout.toConfig(); const config = this.layout.toConfig();
Sharing.getLinks(config, type, (error: any, newUrl: string, extra: string, updateState: boolean) => { Sharing.getLinks(config, type, (error: any, newUrl: string, extra: string, updateState: boolean) => {
if (error || !newUrl) { if (error || !newUrl) {
this.displayTooltip(this.share, 'Oops, something went wrong'); this.displayTooltip(this.shareTooltipTarget, 'Oops, something went wrong');
SentryCapture(error, 'Getting short link failed'); SentryCapture(error, 'Getting short link failed');
} else { } else {
if (updateState) { if (updateState) {
@@ -306,16 +321,33 @@ export class Sharing {
}); });
} }
// TODO we can consider using bootstrap's "Toast" support in future.
private displayTooltip(where: JQuery, message: string): void { private displayTooltip(where: JQuery, message: string): void {
where.tooltip('dispose'); // First dispose any existing tooltip
where.tooltip({ const tooltipEl = where[0];
placement: 'bottom', if (!tooltipEl) return;
trigger: 'manual',
title: message, const existingTooltip = Tooltip.getInstance(tooltipEl);
}); if (existingTooltip) {
where.tooltip('show'); existingTooltip.dispose();
// Manual triggering of tooltips does not hide them automatically. This timeout ensures they do }
setTimeout(() => where.tooltip('hide'), 1500);
// Create and show new tooltip
try {
const tooltip = BootstrapUtils.initTooltip(tooltipEl, {
placement: 'bottom',
trigger: 'manual',
title: message,
});
tooltip.show();
// Manual triggering of tooltips does not hide them automatically. This timeout ensures they do
setTimeout(() => tooltip.hide(), 1500);
} catch (e) {
// If element doesn't exist, just silently fail
console.warn('Could not show tooltip:', e);
}
} }
private openShareModalForType(type: LinkType): void { private openShareModalForType(type: LinkType): void {
@@ -336,7 +368,7 @@ export class Sharing {
if (Sharing.isNavigatorClipboardAvailable()) { if (Sharing.isNavigatorClipboardAvailable()) {
navigator.clipboard navigator.clipboard
.writeText(link) .writeText(link)
.then(() => this.displayTooltip(this.share, 'Link copied to clipboard')) .then(() => this.displayTooltip(this.shareTooltipTarget, 'Link copied to clipboard'))
.catch(() => this.openShareModalForType(type)); .catch(() => this.openShareModalForType(type));
} else { } else {
this.openShareModalForType(type); this.openShareModalForType(type);
@@ -397,7 +429,7 @@ export class Sharing {
): string { ): string {
const embedUrl = Sharing.getEmbeddedUrl(config, root, isReadOnly, extraOptions); const embedUrl = Sharing.getEmbeddedUrl(config, root, isReadOnly, extraOptions);
// The attributes must be double quoted, the full url's rison contains single quotes // The attributes must be double quoted, the full url's rison contains single quotes
return `<iframe width="800px" height="200px" src="${embedUrl}"></iframe>`; return `<iframe width='800px' height='200px' src='${embedUrl}'></iframe>`;
} }
private static getEmbeddedUrl(config: any, root: string, readOnly: boolean, extraOptions: object): string { private static getEmbeddedUrl(config: any, root: string, readOnly: boolean, extraOptions: object): string {
@@ -455,7 +487,7 @@ export class Sharing {
const newElement = baseTemplate.children('a.share-item').clone(); const newElement = baseTemplate.children('a.share-item').clone();
if (service.logoClass) { if (service.logoClass) {
newElement.prepend( newElement.prepend(
$('<span>').addClass('dropdown-icon mr-1').addClass(service.logoClass).prop('title', serviceName), $('<span>').addClass('dropdown-icon me-1').addClass(service.logoClass).prop('title', serviceName),
); );
} }
if (service.text) { if (service.text) {

View File

@@ -36,6 +36,11 @@ body {
padding: 0; padding: 0;
} }
// Fix for Bootstrap 5 navbar
.navbar .container-fluid {
padding-left: 0;
}
.navbar-brand img.logo-overlay { .navbar-brand img.logo-overlay {
position: absolute; position: absolute;
top: 0; top: 0;
@@ -75,13 +80,23 @@ body {
.compiler-picker { .compiler-picker {
min-width: 14em; min-width: 14em;
flex-grow: 0.2 !important; // Ensures the compiler picker doesn't flex-grow too much and take up more space
}
// Prevent TomSelect items from spilling onto multiple lines
.ts-wrapper .item {
white-space: nowrap;
overflow: hidden;
} }
.compiler-picker .ts-input { .compiler-picker .ts-input {
text-align: center; text-align: center;
border-left: none !important; border-left: none !important; /* Fallback for older browsers */
border-top-left-radius: 0; border-inline-start: none !important; /* Logical property for RTL support */
border-bottom-left-radius: 0; border-top-left-radius: 0; /* Fallback for older browsers */
border-bottom-left-radius: 0; /* Fallback for older browsers */
border-start-start-radius: 0; /* Logical property for RTL support */
border-end-start-radius: 0; /* Logical property for RTL support */
} }
.function-picker { .function-picker {
@@ -294,6 +309,12 @@ pre.content.wrap * {
} }
} }
#site-template-loader {
.modal-dialog {
min-width: 800px; /* ensure template modal has adequate width */
}
}
.modal-body { .modal-body {
min-height: 200px; min-height: 200px;
overflow-y: auto; /* make body scrollable -> keep Header & Footer onscreen, if possible */ overflow-y: auto; /* make body scrollable -> keep Header & Footer onscreen, if possible */
@@ -377,7 +398,7 @@ pre.content.wrap * {
max-width: 100% !important; max-width: 100% !important;
} }
.toast-header .close { .toast-header .btn-close {
float: left; float: left;
margin-right: 5px; margin-right: 5px;
} }
@@ -387,17 +408,25 @@ pre.content.wrap * {
} }
.font-size-list { .font-size-list {
min-width: 43px !important; min-width: 60px !important;
max-height: 70% !important; max-height: 70vh !important;
overflow-y: scroll; overflow-y: auto;
width: auto; width: auto;
// For my fellow Firefox users // For my fellow Firefox users
scrollbar-width: thin; scrollbar-width: thin;
padding: 0.25rem 0;
} }
.font-size-list button { .font-size-list button {
margin-left: 0 !important; margin-left: 0 !important;
border-radius: 0; border-radius: 0;
width: 100%;
text-align: center;
padding: 0.25rem 0.5rem;
}
.font-size-list button:hover {
background-color: rgba(0, 0, 0, 0.075);
} }
.font-option { .font-option {
@@ -893,17 +922,18 @@ div.populararguments div.dropdown-menu {
} }
.corporate .ces { .corporate .ces {
font-size: 165%; .btn {
height: 9em; --bs-btn-font-size: 24px;
}
height: 20em;
} }
.legendary .ces { .legendary .ces {
font-size: 165%; .btn {
height: 7em; --bs-btn-font-size: 24px;
} padding: 1em;
}
.legendary .ces button { height: 20em;
padding: 1em;
} }
.ces-logo { .ces-logo {
@@ -1115,7 +1145,7 @@ div.populararguments div.dropdown-menu {
margin-right: 2pt; margin-right: 2pt;
} }
span.badge.badge-pill { span.badge.rounded-pill {
margin-left: 2pt; margin-left: 2pt;
} }
@@ -1185,11 +1215,42 @@ html[data-theme='one-dark'] {
padding-right: 2rem !important; padding-right: 2rem !important;
} }
/* Add back the dropdown arrow for TomSelect in Bootstrap 5 */
.ts-wrapper.single .ts-control:not(.input-active)::after {
content: " ";
display: block;
position: absolute;
top: 50%;
right: 15px;
margin-top: -3px;
width: 0;
height: 0;
border-style: solid;
border-width: 5px 5px 0 5px;
border-color: #343a40 transparent transparent transparent;
}
/* Styling for the dropdown arrow when dropdown is active */
.ts-wrapper.single.dropdown-active .ts-control::after {
margin-top: -4px;
border-width: 0 5px 5px 5px;
border-color: transparent transparent #343a40 transparent;
}
// Fix Bootstrap 5's input-group border overlap issues with TomSelect
// Bootstrap applies a -1px margin to elements matching `:not(:first-child)` in input groups
// TomSelect creates a wrapper (.ts-wrapper) around the select element, which becomes a
// child of the input-group. This wrapper gets the negative margin, causing it to shift
// left by 1px and visually overflow its container boundary.
.input-group > .ts-wrapper:not(:first-child) {
margin-left: 0 !important; // Reset Bootstrap's negative margin for TomSelect wrappers
}
.copy-link-btn { .copy-link-btn {
padding-top: 0; padding-top: 0;
} }
.conformance-wrapper .compiler-list .form-row { .conformance-wrapper .compiler-list .row {
padding-top: 3px; padding-top: 3px;
} }
@@ -1229,8 +1290,10 @@ html[data-theme='one-dark'] {
.prepend-options, .prepend-options,
.picker-popout-button { .picker-popout-button {
border-top-right-radius: 0; border-top-right-radius: 0; /* Fallback for older browsers */
border-bottom-right-radius: 0; border-bottom-right-radius: 0; /* Fallback for older browsers */
border-start-end-radius: 0; /* Logical property for RTL support */
border-end-end-radius: 0; /* Logical property for RTL support */
} }
.picker-popout-button { .picker-popout-button {
@@ -1257,7 +1320,9 @@ html[data-theme='one-dark'] {
} }
.popular-arguments-btn { .popular-arguments-btn {
border-top-right-radius: 0; border-top-right-radius: 0; /* Fallback for older browsers */
border-start-end-radius: 0; /* Logical property for RTL support */
height: 100%;
} }
.panel-compilation { .panel-compilation {
@@ -1315,9 +1380,14 @@ html[data-theme='one-dark'] {
> div { > div {
padding: 0; padding: 0;
flex-shrink: 0; flex-shrink: 0;
&.site-templates-list-col {
min-width: 150px;
width: 20%;
}
&.site-template-preview-col { &.site-template-preview-col {
flex-grow: 1; flex-grow: 1;
flex-shrink: 1; flex-shrink: 1;
min-width: 400px;
img { img {
width: 1000px; width: 1000px;
aspect-ratio: 1000 / 800; aspect-ratio: 1000 / 800;
@@ -1579,11 +1649,11 @@ html[data-theme='one-dark'] {
} }
} }
.close { .dark-close {
position: absolute; position: absolute;
bottom: 0px; bottom: 0;
right: -12px; right: -12px;
//background: #e787e7; padding: 0;
background: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, #1a1a1a 48%, #1a1a1a 100%); background: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, #1a1a1a 48%, #1a1a1a 100%);
width: 24px; width: 24px;
height: 24px; height: 24px;

View File

@@ -28,12 +28,24 @@ body {
background-color: #333 !important; background-color: #333 !important;
} }
// Border color adjustments for form elements in dark theme
.form-select,
.form-control,
.input-group-text,
.ts-wrapper .ts-control {
--bs-border-color: #444;
}
input, input,
textarea { textarea {
color: #eee !important; color: #eee !important;
background-color: #474747; background-color: #474747;
border: 0 !important; border: 0 !important;
&::placeholder {
color: #888 !important;
}
&:focus { &:focus {
color: #eee !important; color: #eee !important;
background-color: #474747; background-color: #474747;
@@ -166,7 +178,7 @@ textarea.form-control {
.conformance-wrapper { .conformance-wrapper {
background-color: #1e1e1e !important; background-color: #1e1e1e !important;
.compiler-list .form-row { .compiler-list .row {
border-bottom: 1px solid #3e3e3e; border-bottom: 1px solid #3e3e3e;
} }
} }
@@ -184,7 +196,7 @@ textarea.form-control {
background-color: rgba(49, 54, 60, 0.85); background-color: rgba(49, 54, 60, 0.85);
} }
.custom-select { .form-select {
background-color: #76a1c8 !important; background-color: #76a1c8 !important;
} }
@@ -196,6 +208,11 @@ textarea.form-control {
color: #fff !important; color: #fff !important;
background-color: #23272b !important; background-color: #23272b !important;
} }
&.active {
background-color: #235765 !important;
color: #fff !important;
}
} }
.dropdown-menu { .dropdown-menu {
@@ -366,8 +383,8 @@ textarea.form-control {
color: #eee !important; color: #eee !important;
background-color: #333 !important; background-color: #333 !important;
.close { .btn-close {
color: #fff; filter: invert(100%);
} }
} }
@@ -437,8 +454,8 @@ textarea.form-control {
background-color: #aa3333 !important; background-color: #aa3333 !important;
color: #fff; color: #fff;
.close { .btn-close {
color: #fff; filter: invert(100%);
} }
} }
@@ -451,8 +468,8 @@ textarea.form-control {
background-color: #222 !important; background-color: #222 !important;
color: #fff; color: #fff;
.close { .btn-close {
color: #fff; filter: invert(100%);
} }
} }

View File

@@ -164,16 +164,16 @@ a.navbar-brand img.logo.normal {
.notification-error { .notification-error {
background-color: indianred; background-color: indianred;
color: white; color: white;
.close { .btn-close {
color: white; filter: invert(100%);
} }
} }
.notification-on { .notification-on {
background-color: green; background-color: green;
color: #fff; color: #fff;
.close { .btn-close {
color: white; filter: invert(100%);
} }
} }
@@ -346,7 +346,7 @@ div.argmenuitem span.argdescription {
color: #007bfd; color: #007bfd;
} }
.conformance-wrapper .compiler-list .form-row { .conformance-wrapper .compiler-list .row {
border-bottom: 1px solid #e3e3e3; border-bottom: 1px solid #e3e3e3;
} }
@@ -387,6 +387,11 @@ div.argmenuitem span.argdescription {
background-color: #dae0e5; background-color: #dae0e5;
} }
.dropdown-item.active {
background-color: #007bfd;
color: white;
}
.currentCursorPosition { .currentCursorPosition {
color: #15a3b9; color: #15a3b9;
background-color: darken(rgba(248, 249, 250, 0.85), 3%); background-color: darken(rgba(248, 249, 250, 0.85), 3%);

View File

@@ -39,11 +39,23 @@ body {
background-color: $base !important; background-color: $base !important;
} }
// Border color adjustments for form elements in dark theme
.form-select,
.form-control,
.input-group-text,
.ts-wrapper .ts-control {
--bs-border-color: $dark;
}
input { input {
color: #eee !important; color: #eee !important;
background-color: $lighter; background-color: $lighter;
border: 0 !important; border: 0 !important;
&::placeholder {
color: #888 !important;
}
&:focus { &:focus {
color: #eee !important; color: #eee !important;
background-color: $lightest; background-color: $lightest;
@@ -204,7 +216,7 @@ textarea.form-control {
.conformance-wrapper { .conformance-wrapper {
background-color: #1e1e1e !important; background-color: #1e1e1e !important;
.compiler-list .form-row { .compiler-list .row {
border-bottom: 1px solid #3e3e3e; border-bottom: 1px solid #3e3e3e;
} }
} }
@@ -222,7 +234,7 @@ textarea.form-control {
background-color: opacify($dark, 0.8); background-color: opacify($dark, 0.8);
} }
.custom-select { .form-select {
background-color: #dddddd !important; background-color: #dddddd !important;
color: #000 !important; color: #000 !important;
} }
@@ -235,6 +247,11 @@ textarea.form-control {
color: #fff !important; color: #fff !important;
background-color: $lighter !important; background-color: $lighter !important;
} }
&.active {
background-color: #405f9d !important;
color: #fff !important;
}
} }
.dropdown-menu { .dropdown-menu {
@@ -325,7 +342,7 @@ textarea.form-control {
} }
} }
#v-status { .v-status {
color: #eee !important; color: #eee !important;
} }
@@ -422,8 +439,8 @@ textarea.form-control {
color: #eee !important; color: #eee !important;
background-color: $base !important; background-color: $base !important;
.close { .btn-close {
color: #eee; filter: invert(90%);
} }
} }
@@ -493,8 +510,8 @@ textarea.form-control {
background-color: #aa3333 !important; background-color: #aa3333 !important;
color: #fff; color: #fff;
.close { .btn-close {
color: #fff; filter: invert(100%);
} }
} }
@@ -507,8 +524,8 @@ textarea.form-control {
background-color: #222 !important; background-color: #222 !important;
color: #fff; color: #fff;
.close { .btn-close {
color: #fff; filter: invert(100%);
} }
} }

View File

@@ -36,6 +36,14 @@ body {
background-color: $base !important; background-color: $base !important;
} }
// Border color adjustments for form elements
.form-select,
.form-control,
.input-group-text,
.ts-wrapper .ts-control {
--bs-border-color: $dark;
}
input { input {
//color: #000 !important; //color: #000 !important;
background-color: $base; background-color: $base;
@@ -174,7 +182,7 @@ textarea.form-control {
.conformance-wrapper { .conformance-wrapper {
background-color: #1e1e1e !important; background-color: #1e1e1e !important;
.compiler-list .form-row { .compiler-list .row {
border-bottom: 1px solid #3e3e3e; border-bottom: 1px solid #3e3e3e;
} }
} }
@@ -188,7 +196,7 @@ textarea.form-control {
background-color: opacify($dark, 0.8); background-color: opacify($dark, 0.8);
} }
.custom-select { .form-select {
background-color: #76a1c8 !important; background-color: #76a1c8 !important;
} }
@@ -198,6 +206,11 @@ textarea.form-control {
&:hover { &:hover {
background-color: $dark !important; background-color: $dark !important;
} }
&.active {
background-color: $darker !important;
color: #212529 !important;
}
} }
.dropdown-menu { .dropdown-menu {
@@ -383,10 +396,6 @@ textarea.form-control {
color: #212529 !important; color: #212529 !important;
background-color: $base !important; background-color: $base !important;
border-bottom-color: $dark; border-bottom-color: $dark;
.close {
color: #212529;
}
} }
.nav.nav-tabs { .nav.nav-tabs {
@@ -459,8 +468,8 @@ textarea.form-control {
background-color: #aa3333 !important; background-color: #aa3333 !important;
color: #fff; color: #fff;
.close { .btn-close {
color: #fff; filter: invert(100%);
} }
} }
@@ -473,8 +482,8 @@ textarea.form-control {
background-color: #222 !important; background-color: #222 !important;
color: #fff; color: #fff;
.close { .btn-close {
color: #fff; filter: invert(100%);
} }
} }

View File

@@ -24,6 +24,7 @@
import $ from 'jquery'; import $ from 'jquery';
import * as BootstrapUtils from '../bootstrap-utils.js';
import {AlertAskOptions, AlertEnterTextOptions, AlertNotifyOptions} from './alert.interfaces.js'; import {AlertAskOptions, AlertEnterTextOptions, AlertNotifyOptions} from './alert.interfaces.js';
export class Alert { export class Alert {
@@ -57,10 +58,10 @@ export class Alert {
modal.toggleClass('error-alert', isError === true); modal.toggleClass('error-alert', isError === true);
modal.find('.modal-title').html(title); modal.find('.modal-title').html(title);
modal.find('.modal-body').html(body); modal.find('.modal-body').html(body);
modal.modal(); BootstrapUtils.showModal(modal);
if (onClose) { if (onClose) {
modal.off('hidden.bs.modal'); BootstrapUtils.setElementEventHandler(modal, 'hidden.bs.modal', onClose);
modal.on('hidden.bs.modal', onClose);
} }
return modal; return modal;
} }
@@ -83,10 +84,10 @@ export class Alert {
modal.find('.modal-footer .no').removeClass('btn-link').addClass(askOptions.noClass); modal.find('.modal-footer .no').removeClass('btn-link').addClass(askOptions.noClass);
} }
if (askOptions.onClose) { if (askOptions.onClose) {
modal.off('hidden.bs.modal'); BootstrapUtils.setElementEventHandler(modal, 'hidden.bs.modal', askOptions.onClose);
modal.on('hidden.bs.modal', askOptions.onClose);
} }
modal.modal();
BootstrapUtils.showModal(modal);
return modal; return modal;
} }
@@ -108,10 +109,8 @@ export class Alert {
const newElement = $(` const newElement = $(`
<div class="toast" tabindex="-1" role="alert" aria-live="assertive" aria-atomic="true"> <div class="toast" tabindex="-1" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-header ${alertClass}"> <div class="toast-header ${alertClass}">
<strong class="mr-auto">${this.prefixMessage}</strong> <strong class="me-auto">${this.prefixMessage}</strong>
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close"> <button type="button" class="ms-2 mb-1 btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
<span aria-hidden="true">&times;</span>
</button>
</div> </div>
<div class="toast-body ${alertClass}"> <div class="toast-body ${alertClass}">
<span id="msg">${body}</span> <span id="msg">${body}</span>
@@ -119,21 +118,26 @@ export class Alert {
</div> </div>
`); `);
container.append(newElement); container.append(newElement);
newElement.toast({ const toastOptions = {
autohide: autoDismiss, autohide: autoDismiss,
delay: dismissTime, delay: dismissTime,
}); };
BootstrapUtils.initToast(newElement, toastOptions);
if (group !== '') { if (group !== '') {
if (collapseSimilar) { if (collapseSimilar) {
// Only collapsing if a group has been specified // Only collapsing if a group has been specified
const old = container.find(`[data-group="${group}"]`); const old = container.find(`[data-group="${group}"]`);
old.toast('hide'); old.each((_, element) => {
old.remove(); BootstrapUtils.hideToast(element);
$(element).remove();
});
} }
newElement.attr('data-group', group); newElement.attr('data-group', group);
} }
onBeforeShow(newElement); onBeforeShow(newElement);
newElement.toast('show'); BootstrapUtils.showToast(newElement);
} }
/** /**
@@ -174,14 +178,13 @@ export class Alert {
noButton.removeClass('btn-light').addClass(askOptions.noClass); noButton.removeClass('btn-light').addClass(askOptions.noClass);
} }
if (askOptions.onClose) { if (askOptions.onClose) {
modal.off('hidden.bs.modal'); BootstrapUtils.setElementEventHandler(modal, 'hidden.bs.modal', askOptions.onClose);
modal.on('hidden.bs.modal', askOptions.onClose);
} }
modal.on('shown.bs.modal', () => { BootstrapUtils.setElementEventHandler(modal, 'shown.bs.modal', () => {
answerEdit.trigger('focus'); answerEdit.trigger('focus');
}); });
modal.modal(); BootstrapUtils.showModal(modal);
return modal; return modal;
} }
} }

View File

@@ -30,6 +30,7 @@ import {
EnvVarOverrides, EnvVarOverrides,
} from '../../types/compilation/compiler-overrides.interfaces.js'; } from '../../types/compilation/compiler-overrides.interfaces.js';
import {assert, unwrap} from '../assert.js'; import {assert, unwrap} from '../assert.js';
import * as BootstrapUtils from '../bootstrap-utils.js';
import {CompilerInfo} from '../compiler.interfaces.js'; import {CompilerInfo} from '../compiler.interfaces.js';
import {localStorage} from '../local.js'; import {localStorage} from '../local.js';
import {options} from '../options.js'; import {options} from '../options.js';
@@ -160,8 +161,8 @@ export class CompilerOverridesWidget {
btn.addClass('active'); btn.addClass('active');
} else if (state instanceof IncompatibleState) { } else if (state instanceof IncompatibleState) {
btn.prop('disabled', true); btn.prop('disabled', true);
btn.prop('data-toggle', 'tooltip'); btn.prop('data-bs-toggle', 'tooltip');
btn.prop('data-placement', 'top'); btn.prop('data-bs-placement', 'top');
btn.prop('title', state.reason); btn.prop('title', state.reason);
} }
div.data('ov-name', fave.name); div.data('ov-name', fave.name);
@@ -416,9 +417,8 @@ export class CompilerOverridesWidget {
const lastOverrides = JSON.stringify(this.configured); const lastOverrides = JSON.stringify(this.configured);
const popup = this.popupDomRoot.modal();
// popup is shared, so clear the events first // popup is shared, so clear the events first
popup.off('hidden.bs.modal').on('hidden.bs.modal', () => { BootstrapUtils.setElementEventHandler(this.popupDomRoot, 'hidden.bs.modal', () => {
this.configured = this.loadStateFromUI(); this.configured = this.loadStateFromUI();
const newOverrides = JSON.stringify(this.configured); const newOverrides = JSON.stringify(this.configured);
@@ -428,5 +428,7 @@ export class CompilerOverridesWidget {
this.onChangeCallback(); this.onChangeCallback();
} }
}); });
BootstrapUtils.showModal(this.popupDomRoot);
} }
} }

View File

@@ -29,6 +29,7 @@ import * as sifter from '@orchidjs/sifter';
import {escapeHTML, intersection, remove, unique} from '../../shared/common-utils.js'; import {escapeHTML, intersection, remove, unique} from '../../shared/common-utils.js';
import {CompilerInfo} from '../../types/compiler.interfaces.js'; import {CompilerInfo} from '../../types/compiler.interfaces.js';
import {unwrap, unwrapString} from '../assert.js'; import {unwrap, unwrapString} from '../assert.js';
import * as BootstrapUtils from '../bootstrap-utils.js';
import {CompilerService} from '../compiler-service.js'; import {CompilerService} from '../compiler-service.js';
import {highlight} from '../highlight.js'; import {highlight} from '../highlight.js';
import {CompilerPicker} from './compiler-picker.js'; import {CompilerPicker} from './compiler-picker.js';
@@ -64,7 +65,7 @@ export class CompilerPickerPopup {
this.categoryFilters = []; this.categoryFilters = [];
this.searchBar.val(''); this.searchBar.val('');
this.modal.on('shown.bs.modal', () => { BootstrapUtils.setElementEventHandler(this.modal, 'shown.bs.modal', () => {
this.searchBar[0].focus(); this.searchBar[0].focus();
}); });
} }
@@ -186,7 +187,7 @@ export class CompilerPickerPopup {
` `
<div class="compiler d-flex" data-value="${compiler.id}"> <div class="compiler d-flex" data-value="${compiler.id}">
<div>${searchRegexes ? highlight(name, searchRegexes) : name}</div> <div>${searchRegexes ? highlight(name, searchRegexes) : name}</div>
<div title="Click to mark or unmark as a favorite" class="ml-auto toggle-fav"> <div title="Click to mark or unmark as a favorite" class="ms-auto toggle-fav">
<i class="${extraClasses}"></i> <i class="${extraClasses}"></i>
</div> </div>
</div> </div>
@@ -264,10 +265,10 @@ export class CompilerPickerPopup {
show() { show() {
this.searchBar.trigger('input'); this.searchBar.trigger('input');
this.fillCompilers(); this.fillCompilers();
this.modal.modal({}); BootstrapUtils.showModal(this.modal);
} }
hide() { hide() {
this.modal.modal('hide'); BootstrapUtils.hideModal(this.modal);
} }
} }

View File

@@ -135,7 +135,7 @@ export class CompilerPicker {
'<div class="d-flex"><div>' + '<div class="d-flex"><div>' +
escapeHtml(data.name) + escapeHtml(data.name) +
'</div>' + '</div>' +
'<div title="Click to mark or unmark as a favorite" class="ml-auto toggle-fav">' + '<div title="Click to mark or unmark as a favorite" class="ms-auto toggle-fav">' +
'<i class="' + '<i class="' +
extraClasses + extraClasses +
'"></i>' + '"></i>' +
@@ -143,6 +143,7 @@ export class CompilerPicker {
'</div>' '</div>'
); );
}, },
item: (data, escapeHtml) => `<div title="${escapeHtml(data.name)}">${escapeHtml(data.name)}</div>`,
}, },
}); });

View File

@@ -24,6 +24,7 @@
import $ from 'jquery'; import $ from 'jquery';
import {escapeHTML} from '../../shared/common-utils.js'; import {escapeHTML} from '../../shared/common-utils.js';
import * as BootstrapUtils from '../bootstrap-utils.js';
import {options} from '../options.js'; import {options} from '../options.js';
export type CompilerVersionInfo = {version: string; fullVersion?: string}; export type CompilerVersionInfo = {version: string; fullVersion?: string};
@@ -78,14 +79,19 @@ function reallySetCompilerVersionPopover(
.on('click', () => { .on('click', () => {
versionContent.toggle(); versionContent.toggle();
hiddenVersionText.toggle(); hiddenVersionText.toggle();
pane.fullCompilerName.popover('update'); const popover = BootstrapUtils.getPopoverInstance(pane.fullCompilerName);
if (popover) popover.update();
}); });
hiddenSection.append(hiddenVersionText).append(clickToExpandContent); hiddenSection.append(hiddenVersionText).append(clickToExpandContent);
bodyContent.append(hiddenSection); bodyContent.append(hiddenSection);
} }
pane.fullCompilerName.popover('dispose'); // Dispose of existing popover
pane.fullCompilerName.popover({ const existingPopover = BootstrapUtils.getPopoverInstance(pane.fullCompilerName);
if (existingPopover) existingPopover.dispose();
// Initialize new popover
BootstrapUtils.initPopover(pane.fullCompilerName, {
html: true, html: true,
title: notification title: notification
? ($.parseHTML('<span>Compiler Version: ' + notification + '</span>')[0] as Element) ? ($.parseHTML('<span>Compiler Version: ' + notification + '</span>')[0] as Element)

View File

@@ -47,11 +47,7 @@ function makeFontSizeDropdown(elem: JQuery, obj: FontScale, buttonDropdown: JQue
for (let i = 8; i <= 30; i++) { for (let i = 8; i <= 30; i++) {
const item = $('<button></button>'); const item = $('<button></button>');
item.attr('data-value', i) item.attr('data-value', i).addClass('dropdown-item').text(i).appendTo(elem).on('click', onClickEvent);
.addClass('dropdown-item btn btn-sm btn-light')
.text(i)
.appendTo(elem)
.on('click', onClickEvent);
if (obj.scale === i) { if (obj.scale === i) {
item.addClass('active'); item.addClass('active');

View File

@@ -25,9 +25,10 @@
import $ from 'jquery'; import $ from 'jquery';
import {editor} from 'monaco-editor'; import {editor} from 'monaco-editor';
import {pluck} from 'underscore'; import {pluck} from 'underscore';
import {unwrap} from '../assert.js';
import * as BootstrapUtils from '../bootstrap-utils.js';
import {EditorSource, HistoryEntry, sortedList} from '../history.js'; import {EditorSource, HistoryEntry, sortedList} from '../history.js';
import ITextModel = editor.ITextModel; import ITextModel = editor.ITextModel;
import {unwrap} from '../assert.js';
type Entry = {dt: number; name: string; load: () => void}; type Entry = {dt: number; name: string; load: () => void};
@@ -72,7 +73,9 @@ export class HistoryWidget {
name: `${dt.replace(/\s\(.*\)/, '')} (${languages})`, name: `${dt.replace(/\s\(.*\)/, '')} (${languages})`,
load: () => { load: () => {
this.onLoad(data); this.onLoad(data);
this.modal?.modal('hide'); if (this.modal) {
BootstrapUtils.hideModal(this.modal);
}
}, },
}; };
}), }),
@@ -140,7 +143,8 @@ export class HistoryWidget {
this.onLoadCallback = onLoad; this.onLoadCallback = onLoad;
// It can't tell that we initialize modal on initializeIfNeeded, so it sticks to the possibility of it being null // It can't tell that we initialize modal on initializeIfNeeded, so it sticks to the possibility of it being null
unwrap(this.modal).on('shown.bs.modal', () => this.resizeLayout()); const modalElement = unwrap(this.modal)[0];
unwrap(this.modal).modal(); modalElement.addEventListener('shown.bs.modal', () => this.resizeLayout());
BootstrapUtils.showModal(modalElement);
} }
} }

View File

@@ -24,6 +24,7 @@
import $ from 'jquery'; import $ from 'jquery';
import {unwrapString} from '../assert.js'; import {unwrapString} from '../assert.js';
import * as BootstrapUtils from '../bootstrap-utils.js';
import {localStorage} from '../local.js'; import {localStorage} from '../local.js';
import {Library, LibraryVersion} from '../options.interfaces.js'; import {Library, LibraryVersion} from '../options.interfaces.js';
import {options} from '../options.js'; import {options} from '../options.js';
@@ -187,32 +188,32 @@ export class LibsWidget {
this.domRoot.addClass('mobile'); this.domRoot.addClass('mobile');
} }
this.domRoot BootstrapUtils.setElementEventHandler(this.domRoot, 'shown.bs.modal', () => {
.on('shown.bs.modal', () => { searchInput.trigger('focus');
searchInput.trigger('focus');
for (const filter of this.filters) { for (const filter of this.filters) {
const filterResult = filter(this.currentCompilerId, this.currentLangId); const filterResult = filter(this.currentCompilerId, this.currentLangId);
if (filterResult !== null) { if (filterResult !== null) {
const alertSystem = new Alert(); const alertSystem = new Alert();
alertSystem.notify(`${filterResult.title}: ${filterResult.content}`, { alertSystem.notify(`${filterResult.title}: ${filterResult.content}`, {
group: 'libs', group: 'libs',
alertClass: 'notification-error', alertClass: 'notification-error',
}); });
break; break;
}
} }
}) }
.on('hide.bs.modal', () => { });
this.hidePopups();
}); BootstrapUtils.setElementEventHandler(this.domRoot, 'hide.bs.modal', () => {
this.hidePopups();
});
searchInput.on('input', this.startSearching.bind(this)); searchInput.on('input', this.startSearching.bind(this));
this.domRoot.find('.lib-search-button').on('click', this.startSearching.bind(this)); this.domRoot.find('.lib-search-button').on('click', this.startSearching.bind(this));
this.dropdownButton.on('click', () => { this.dropdownButton.on('click', () => {
this.domRoot.modal({}); BootstrapUtils.showModal(this.domRoot);
}); });
this.updateButton(); this.updateButton();
@@ -342,11 +343,14 @@ export class LibsWidget {
} }
hidePopups() { hidePopups() {
this.searchResults.find('.lib-info-button').popover('hide'); this.searchResults.find('.lib-info-button').each((_, el) => BootstrapUtils.hidePopover($(el)));
} }
clearSearchResults() { clearSearchResults() {
this.searchResults.find('.lib-info-button').popover('dispose'); this.searchResults.find('.lib-info-button').each((_, el) => {
const popover = BootstrapUtils.getPopoverInstance($(el));
if (popover) popover.dispose();
});
this.searchResults.html(''); this.searchResults.html('');
} }
@@ -498,7 +502,7 @@ export class LibsWidget {
'<div class="arrow"></div>' + '<div class="arrow"></div>' +
'<h3 class="popover-header"></h3><div class="popover-body"></div>' + '<h3 class="popover-header"></h3><div class="popover-body"></div>' +
'</div>'; '</div>';
infoButton.popover({ BootstrapUtils.initPopover(infoButton, {
html: true, html: true,
title: 'Build info for ' + getCompilerName(this.currentCompilerId), title: 'Build info for ' + getCompilerName(this.currentCompilerId),
content: () => { content: () => {

View File

@@ -29,6 +29,7 @@ import {escapeHTML} from '../../shared/common-utils.js';
import {Language} from '../../types/languages.interfaces.js'; import {Language} from '../../types/languages.interfaces.js';
import {SourceApiEntry} from '../../types/source.interfaces.js'; import {SourceApiEntry} from '../../types/source.interfaces.js';
import {unwrap, unwrapString} from '../assert.js'; import {unwrap, unwrapString} from '../assert.js';
import * as BootstrapUtils from '../bootstrap-utils.js';
import {HistorySource} from '../history.js'; import {HistorySource} from '../history.js';
import {localStorage} from '../local.js'; import {localStorage} from '../local.js';
import {Alert} from './alert.js'; import {Alert} from './alert.js';
@@ -98,7 +99,9 @@ export class LoadSave {
window.location.origin + this.base + 'source/builtin/load/' + element.lang + '/' + element.file, window.location.origin + this.base + 'source/builtin/load/' + element.lang + '/' + element.file,
response => this.onLoad(response.file), response => this.onLoad(response.file),
); );
this.modal?.modal('hide'); if (this.modal) {
BootstrapUtils.hideModal(this.modal);
}
} }
private static populate(root: JQuery, list: PopulateItem[]) { private static populate(root: JQuery, list: PopulateItem[]) {
@@ -143,7 +146,9 @@ export class LoadSave {
name: name, name: name,
load: () => { load: () => {
this.onLoad(data); this.onLoad(data);
this.modal?.modal('hide'); if (this.modal) {
BootstrapUtils.hideModal(this.modal);
}
}, },
delete: () => { delete: () => {
this.alertSystem.ask( this.alertSystem.ask(
@@ -183,7 +188,9 @@ export class LoadSave {
name: dt.replace(/\s\(.*\)/, ''), name: dt.replace(/\s\(.*\)/, ''),
load: () => { load: () => {
this.onLoad(data.source); this.onLoad(data.source);
this.modal?.modal('hide'); if (this.modal) {
BootstrapUtils.hideModal(this.modal);
}
}, },
}; };
}), }),
@@ -214,7 +221,9 @@ export class LoadSave {
}; };
reader.readAsText(file); reader.readAsText(file);
} }
this.modal?.modal('hide'); if (this.modal) {
BootstrapUtils.hideModal(this.modal);
}
} }
public run(onLoad, editorText, currentLanguage: Language) { public run(onLoad, editorText, currentLanguage: Language) {
@@ -224,7 +233,11 @@ export class LoadSave {
this.populateLocalHistory(); this.populateLocalHistory();
this.onLoadCallback = onLoad; this.onLoadCallback = onLoad;
unwrap(this.modal).find('.local-file').attr('accept', currentLanguage.extensions.join(',')); unwrap(this.modal).find('.local-file').attr('accept', currentLanguage.extensions.join(','));
this.populateBuiltins().then(() => this.modal?.modal()); this.populateBuiltins().then(() => {
if (this.modal) {
BootstrapUtils.showModal(this.modal);
}
});
} }
private onSaveToBrowserStorage() { private onSaveToBrowserStorage() {
@@ -238,7 +251,9 @@ export class LoadSave {
LoadSave.setLocalFile(name, this.editorText); LoadSave.setLocalFile(name, this.editorText);
}; };
if (name in LoadSave.getLocalFiles()) { if (name in LoadSave.getLocalFiles()) {
this.modal?.modal('hide'); if (this.modal) {
BootstrapUtils.hideModal(this.modal);
}
this.alertSystem.ask( this.alertSystem.ask(
'Replace current?', 'Replace current?',
`Do you want to replace the existing saved file '${escapeHTML(name)}'?`, `Do you want to replace the existing saved file '${escapeHTML(name)}'?`,
@@ -246,7 +261,9 @@ export class LoadSave {
); );
} else { } else {
doneCallback(); doneCallback();
this.modal?.modal('hide'); if (this.modal) {
BootstrapUtils.hideModal(this.modal);
}
} }
} }

View File

@@ -32,6 +32,7 @@ import {
RuntimeToolType, RuntimeToolType,
} from '../../types/execution/execution.interfaces.js'; } from '../../types/execution/execution.interfaces.js';
import {assert} from '../assert.js'; import {assert} from '../assert.js';
import * as BootstrapUtils from '../bootstrap-utils.js';
import {CompilerInfo} from '../compiler.interfaces.js'; import {CompilerInfo} from '../compiler.interfaces.js';
import {localStorage} from '../local.js'; import {localStorage} from '../local.js';
import {options} from '../options.js'; import {options} from '../options.js';
@@ -392,9 +393,8 @@ export class RuntimeToolsWidget {
const lastOverrides = JSON.stringify(this.configured); const lastOverrides = JSON.stringify(this.configured);
const popup = this.popupDomRoot.modal();
// popup is shared, so clear the events first // popup is shared, so clear the events first
popup.off('hidden.bs.modal').on('hidden.bs.modal', () => { BootstrapUtils.setElementEventHandler(this.popupDomRoot, 'hidden.bs.modal', () => {
this.configured = this.loadStateFromUI(); this.configured = this.loadStateFromUI();
const newOverrides = JSON.stringify(this.configured); const newOverrides = JSON.stringify(this.configured);
@@ -404,5 +404,7 @@ export class RuntimeToolsWidget {
this.onChangeCallback(); this.onChangeCallback();
} }
}); });
BootstrapUtils.showModal(this.popupDomRoot);
} }
} }

View File

@@ -28,6 +28,7 @@ import GoldenLayout from 'golden-layout';
import {escapeHTML} from '../../shared/common-utils.js'; import {escapeHTML} from '../../shared/common-utils.js';
import {SiteTemplateResponse, UserSiteTemplate} from '../../types/features/site-templates.interfaces.js'; import {SiteTemplateResponse, UserSiteTemplate} from '../../types/features/site-templates.interfaces.js';
import {assert, unwrap, unwrapString} from '../assert.js'; import {assert, unwrap, unwrapString} from '../assert.js';
import * as BootstrapUtils from '../bootstrap-utils.js';
import {localStorage} from '../local.js'; import {localStorage} from '../local.js';
import {Settings} from '../settings.js'; import {Settings} from '../settings.js';
import * as url from '../url.js'; import * as url from '../url.js';
@@ -174,7 +175,7 @@ class SiteTemplatesWidget {
this.populated = true; this.populated = true;
} }
show() { show() {
this.modal.modal('show'); BootstrapUtils.showModal(this.modal);
if (!this.populated) { if (!this.populated) {
this.populate(); this.populate();
} }

View File

@@ -24,11 +24,12 @@
import {Chart, ChartData, defaults} from 'chart.js'; import {Chart, ChartData, defaults} from 'chart.js';
import $ from 'jquery'; import $ from 'jquery';
import {Settings} from '../settings.js';
import 'chart.js/auto';
import {isString} from '../../shared/common-utils.js'; import {isString} from '../../shared/common-utils.js';
import {CompilationResult} from '../../types/compilation/compilation.interfaces.js'; import {CompilationResult} from '../../types/compilation/compilation.interfaces.js';
import {unwrap} from '../assert.js'; import {unwrap} from '../assert.js';
import * as BootstrapUtils from '../bootstrap-utils.js';
import {Settings} from '../settings.js';
import 'chart.js/auto';
type Data = ChartData<'bar', number[], string> & {steps: number}; type Data = ChartData<'bar', number[], string> & {steps: number};
@@ -188,7 +189,7 @@ function displayData(data: Data) {
}, },
}, },
}); });
modal.modal('show'); BootstrapUtils.showModal(modal);
} }
export function displayCompilationTiming(compileResult: CompilationResult | null, totalTime: number) { export function displayCompilationTiming(compileResult: CompilationResult | null, totalTime: number) {

View File

@@ -24,8 +24,8 @@ block content
div.ces-item-description= level.description div.ces-item-description= level.description
.ces-top .ces-top
each sponsor in level.sponsors each sponsor in level.sponsors
.ces .ces.d-grid
button.btn-block.btn-secondary(title=sponsor.title onclick=sponsor.onclick disabled=!sponsor.url style=sponsor.style) button.btn.btn-secondary(title=sponsor.title onclick=sponsor.onclick disabled=!sponsor.url style=sponsor.style)
if sponsor.displayType === 'SideBySide' if sponsor.displayType === 'SideBySide'
.d-flex .d-flex
.ces-item-title .ces-item-title

View File

@@ -1,4 +1,4 @@
.btn-group.btn-group-sm(role="group" aria-label="Font size") .btn-group.btn-group-sm(role="group" aria-label="Font size")
button.dropdown-toggle.btn.btn-light.fs-button(type="button" title="Change font size\nTip #1: You can use scroll wheel while hovering over this button\nTip #2: You can ctrl+click this button to reset to default" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" data-flip="false" data-boundary="viewport") button.dropdown-toggle.btn.btn-light.fs-button(type="button" title="Change font size\nTip #1: You can use scroll wheel while hovering over this button\nTip #2: You can ctrl+click this button to reset to default" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" data-bs-flip="false" data-bs-boundary="viewport")
span.fa.fa-font #[b.caret] span.fa.fa-font #[b.caret]
.dropdown-menu.font-size-list(aria-labelledby="fs-button") .dropdown-menu.font-size-list(aria-labelledby="fs-button")

View File

@@ -2,111 +2,116 @@ extends _layout.pug
block prepend content block prepend content
nav.navbar.navbar-godbolt.navbar-expand-md.navbar-light.bg-light nav.navbar.navbar-godbolt.navbar-expand-md.navbar-light.bg-light
include logo.pug .container-fluid
button.navbar-toggler(type="button" data-toggle="collapse" data-target="#navbarContent" aria-controls="navbarContent" aria-expanded="false" aria-label="Toggle navigation") include logo.pug
span.navbar-toggler-icon button.navbar-toggler(type="button" data-bs-toggle="collapse" data-bs-target="#navbarContent" aria-controls="navbarContent" aria-expanded="false" aria-label="Toggle navigation")
.collapse.navbar-collapse#navbarContent span.navbar-toggler-icon
ul.navbar-nav.navbar-left.mr-auto .collapse.navbar-collapse#navbarContent
li.nav-item.dropdown ul.navbar-nav.me-auto.mb-2.mb-md-0
button.btn.btn-light.nav-link.dropdown-toggle#addDropdown(role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false") Add...
div.dropdown-menu(aria-labelledby="addDropdown")
button.dropdown-item.btn#add-editor(title="Click or drag to desired destination")
span.dropdown-icon.fa.fa-code
| Source Editor
button.dropdown-item.btn#add-diff(title="Click or drag to desired destination")
span.dropdown-icon.fas.fa-exchange-alt
| Diff View
button.dropdown-item.btn#add-tree(title="Click or drag to desired destination")
span.dropdown-icon.fa.fa-list-alt
| Tree (IDE Mode)
li.nav-item.dropdown
button.btn.btn-light.nav-link.dropdown-toggle#moreDropdown(role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" data-cy="more-dropdown-btn") More
div.dropdown-menu(aria-labelledby="moreDropdown")
button.dropdown-item.btn#setting(data-target="#settings" data-toggle="modal")
span.dropdown-icon.fas.fa-sliders-h
| Settings
div.dropdown-divider
a.dropdown-item.btn#ui-brokenlink
span.dropdown-icon.fas.fa-undo-alt
| Reset UI layout
button.dropdown-item.btn#ui-reset(data-cy="reset-ui-btn")
span.dropdown-icon.fas.fa-trash-alt
| Reset code and UI layout
button.dropdown-item.btn#ui-duplicate
span.dropdown-icon.fas.fa-external-link-alt
| Open new tab
button.dropdown-item.btn#ui-history
span.dropdown-icon.fas.fa-exchange-alt
| History
div.dropdown-divider
button.dropdown-item.btn#ui-apply-default-font-scale
span.dropdown-icon.fas.fa-font
| Apply Default Font Scale
li.nav-item
button.btn.btn-light.nav-link#loadSiteTemplate(role="button") Templates
li.nav-item.shortlinkInfo.d-none
button.btn.btn-light.nav-link.shortlinkInfoBtn
span.fa.fa-info-circle
span.hideable.shortlinkInfoText
li.nav-item.ui-presentation-control.d-none
a.nav-link.ui-presentation-first(href="javascript:;")
span.dropdown-icon.fas.fa-fast-backward
| Start
li.nav-item.ui-presentation-control.d-none
a.nav-link.ui-presentation-prev(href="javascript:;")
span.dropdown-icon.fas.fa-backward
| Previous
li.nav-item.ui-presentation-control.d-none
a.nav-link.ui-presentation-next(href="javascript:;")
span.dropdown-icon.fas.fa-forward
| Next
ul#motd.navbar-nav.navbar-left.mr-auto.community-advert.d-none
span.content
| Thanks for using Compiler Explorer
span.community-hide(title="Hide and never show again" aria-label="Close")
button.close(type="button" aria-hidden="true") &times;
ul.navbar-nav.navbar-right
if showSponsors
li.nav-item.btn.btn-outline-info#ces(data-toggle="modal")
span#ces-banner-text Sponsors
include sponsor-icons.pug
li.nav-item.dropdown
button.btn.btn-light.nav-link.dropdown-toggle#share(role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false") Share
div.dropdown-menu.dropdown-menu-right
if storageSolution !== "null"
a.dropdown-item#shareShort(href="javascript:;" data-target="#sharelinkdialog" data-toggle="modal" data-bind="Short")
span.dropdown-icon.fas.fa-cloud.d-inline
| Short Link
button.btn.btn-sm.copy-link-btn.clip-icon.float-right(data-toggle="none")
span.dropdown-icon.fa.fa-clipboard
a.dropdown-item#shareFull(href="javascript:;" data-target="#sharelinkdialog" data-toggle="modal" data-bind="Full")
span.dropdown-icon.fas.fa-store-slash.d-inline
| Full Link
button.btn.btn-sm.copy-link-btn.clip-icon.float-right(data-toggle="none")
span.dropdown-icon.fa.fa-clipboard
a.dropdown-item#shareEmbed(href="javascript:;" data-target="#sharelinkdialog" data-toggle="modal" data-bind="Embed")
span.dropdown-icon.fas.fa-window-restore.d-inline
| Embed in iframe
// This is not an oversight, there is in fact a .float-right missing here that's there in the other 2
button.btn.btn-sm.copy-link-btn.clip-icon(data-toggle="none")
span.dropdown-icon.fa.fa-clipboard
if policies.cookies.enabled || policies.privacy.enabled
li.nav-item.dropdown li.nav-item.dropdown
button.btn.btn-light.nav-link.dropdown-toggle#policiesDropdown(role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false") button.btn.btn-light.nav-link.dropdown-toggle#addDropdown(role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false") Add...
| Policies div.dropdown-menu(aria-labelledby="addDropdown")
span#policyBellNotification.d-none.badge.badge-pill.badge-primary button.dropdown-item.btn#add-editor(title="Click or drag to desired destination")
span.fa.fa-bell span.dropdown-icon.fa.fa-code
div.dropdown-menu.dropdown-menu-right(aria-labelledby="policiesDropdown") | Source Editor
include menu-policies.pug button.dropdown-item.btn#add-diff(title="Click or drag to desired destination")
li.nav-item.dropdown span.dropdown-icon.fas.fa-exchange-alt
button.btn.btn-light.nav-link.dropdown-toggle#otherDropdown(role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false") Other | Diff View
div.dropdown-menu.dropdown-menu-right(aria-labelledby="otherDropdown") button.dropdown-item.btn#add-tree(title="Click or drag to desired destination")
include menu-other.pug span.dropdown-icon.fa.fa-list-alt
| Tree (IDE Mode)
li.nav-item.dropdown
button.btn.btn-light.nav-link.dropdown-toggle#moreDropdown(role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" data-cy="more-dropdown-btn") More
div.dropdown-menu(aria-labelledby="moreDropdown")
button.dropdown-item.btn#setting(data-bs-target="#settings" data-bs-toggle="modal")
span.dropdown-icon.fas.fa-sliders-h
| Settings
div.dropdown-divider
a.dropdown-item.btn#ui-brokenlink
span.dropdown-icon.fas.fa-undo-alt
| Reset UI layout
button.dropdown-item.btn#ui-reset(data-cy="reset-ui-btn")
span.dropdown-icon.fas.fa-trash-alt
| Reset code and UI layout
button.dropdown-item.btn#ui-duplicate
span.dropdown-icon.fas.fa-external-link-alt
| Open new tab
button.dropdown-item.btn#ui-history
span.dropdown-icon.fas.fa-exchange-alt
| History
div.dropdown-divider
button.dropdown-item.btn#ui-apply-default-font-scale
span.dropdown-icon.fas.fa-font
| Apply Default Font Scale
li.nav-item
button.btn.btn-light.nav-link#loadSiteTemplate(role="button") Templates
li.nav-item.shortlinkInfo.d-none
button.btn.btn-light.nav-link.shortlinkInfoBtn
span.fa.fa-info-circle
span.hideable.shortlinkInfoText
li.nav-item.ui-presentation-control.d-none
a.nav-link.ui-presentation-first(href="javascript:;")
span.dropdown-icon.fas.fa-fast-backward
| Start
li.nav-item.ui-presentation-control.d-none
a.nav-link.ui-presentation-prev(href="javascript:;")
span.dropdown-icon.fas.fa-backward
| Previous
li.nav-item.ui-presentation-control.d-none
a.nav-link.ui-presentation-next(href="javascript:;")
span.dropdown-icon.fas.fa-forward
| Next
ul#motd.navbar-nav.community-advert.d-none.mx-auto
span.content
| Thanks for using Compiler Explorer
span.community-hide(title="Hide and never show again" aria-label="Close")
button.btn-close(type="button" aria-label="Close")
ul.navbar-nav.ms-auto.mb-2.mb-md-0
if showSponsors
li.nav-item.btn.btn-outline-info#ces
span#ces-banner-text Sponsors
include sponsor-icons.pug
li.nav-item.dropdown#share-tooltip-target
button.btn.btn-light.nav-link.dropdown-toggle#share(role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false") Share
div.dropdown-menu.dropdown-menu-end
// In order for these to support both the clipboard button _and_ optionally opening the dialog, we can't
// use `data-bs-toggle="modal"` on the `<a>` itself, as that would prevent the clipboard button from working.
// Bootstrap's default blanket event handler beats out any event handler we manually attach to the button.
if storageSolution !== "null"
a.dropdown-item#shareShort(href="javascript:;" data-bs-target="#sharelinkdialog" data-bind="Short")
span.dropdown-icon.fas.fa-cloud.d-inline
| Short Link
button.btn.btn-sm.copy-link-btn.clip-icon.float-end(data-bs-target="" data-bs-toggle="none")
span.dropdown-icon.fa.fa-clipboard
a.dropdown-item#shareFull(href="javascript:;" data-bs-target="#sharelinkdialog" data-bind="Full")
span.dropdown-icon.fas.fa-store-slash.d-inline
| Full Link
button.btn.btn-sm.copy-link-btn.clip-icon.float-end(data-bs-toggle="none")
span.dropdown-icon.fa.fa-clipboard
a.dropdown-item#shareEmbed(href="javascript:;" data-bs-target="#sharelinkdialog" data-bind="Embed")
span.dropdown-icon.fas.fa-window-restore.d-inline
| Embed in iframe
// This is not an oversight, there is in fact a .float-end missing here that's there in the other 2
button.btn.btn-sm.copy-link-btn.clip-icon(data-bs-toggle="none")
span.dropdown-icon.fa.fa-clipboard
if policies.cookies.enabled || policies.privacy.enabled
li.nav-item.dropdown
button.btn.btn-light.nav-link.dropdown-toggle#policiesDropdown(role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false")
| Policies
span#policyBellNotification.d-none.badge.rounded-pill.bg-primary
span.fa.fa-bell
div.dropdown-menu.dropdown-menu-end(aria-labelledby="policiesDropdown")
include menu-policies.pug
li.nav-item.dropdown
button.btn.btn-light.nav-link.dropdown-toggle#otherDropdown(role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false") Other
div.dropdown-menu.dropdown-menu-end(aria-labelledby="otherDropdown")
include menu-other.pug
block append footer block append footer
include popups/_all include popups/_all
#real-dark #real-dark
#true-dark #true-dark
.content Try the real dark theme 🔦 .content Try the real dark theme 🔦
.close button.btn.dark-close.fas.fa-times(type="button" aria-label="Close")
i.fa-solid.fa-xmark

View File

@@ -1,10 +1,10 @@
button.dropdown-item.btn#cookies button.dropdown-item.btn#cookies
span.dropdown-icon.fas.fa-cookie-bite span.dropdown-icon.fas.fa-cookie-bite
| Cookie policy | Cookie policy
span#cookiesBellNotification.d-none.badge.badge-pill.badge-primary span#cookiesBellNotification.d-none.badge.rounded-pill.bg-primary
span.fa.fa-bell span.fa.fa-bell
button.dropdown-item.btn#privacy button.dropdown-item.btn#privacy
span.dropdown-icon.fas.fa-user-lock span.dropdown-icon.fas.fa-user-lock
| Privacy policy | Privacy policy
span#privacyBellNotification.d-none.badge.badge-pill.badge-primary span#privacyBellNotification.d-none.badge.rounded-pill.bg-primary
span.fa.fa-bell span.fa.fa-bell

View File

@@ -6,7 +6,7 @@ block content
input#filterAnsi(name='filterAnsi' value='true' type='hidden') input#filterAnsi(name='filterAnsi' value='true' type='hidden')
.form-pair.inlined.dropdown .form-pair.inlined.dropdown
a.btn.nodropdown-toggle#outputDropdown(href="javascript:;" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false") a.btn.nodropdown-toggle#outputDropdown(href="javascript:;" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false")
span.fas.fa-cog span.fas.fa-cog
| Output | Output
@@ -14,7 +14,7 @@ block content
include ../options-output.pug include ../options-output.pug
.form-pair.inlined.dropdown .form-pair.inlined.dropdown
a.btn.nodropdown-toggle#filtersDropdown(href="javascript:;" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false") a.btn.nodropdown-toggle#filtersDropdown(href="javascript:;" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false")
span.fas.fa-filter span.fas.fa-filter
| Filters | Filters

View File

@@ -1,7 +1,7 @@
block footer block footer
nav.bg-light nav.bg-light
if mobileViewer if mobileViewer
a.btn.nodropdown-toggle#otherDropdown(href="javascript:;" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false") Other a.btn.nodropdown-toggle#otherDropdown(href="javascript:;" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false") Other
.noscriptdropdown(aria-labelledby="otherDropdown") .noscriptdropdown(aria-labelledby="otherDropdown")
include ../menu-other.pug include ../menu-other.pug

View File

@@ -1,11 +1,11 @@
.header .header
nav.navbar.navbar-godbolt.navbar-expand-md.navbar-light.bg-light nav.navbar.navbar-godbolt.navbar-expand-md.navbar-light.bg-light
include ../logo.pug include ../logo.pug
#motd.navbar-nav.navbar-center.mr-auto.community-advert.d-none #motd.navbar-nav.navbar-center.me-auto.community-advert.d-none
span.content span.content
| Thanks for using Compiler Explorer | Thanks for using Compiler Explorer
.navbar-nav.navbar-right .navbar-nav.navbar-right
if showSponsors if showSponsors
li.nav-item.btn.btn-outline-primary#ces(data-toggle="modal") li.nav-item.btn.btn-outline-primary#ces(data-bs-toggle="modal")
a(href=`${httpRoot}noscript/sponsors`) Sponsors a(href=`${httpRoot}noscript/sponsors`) Sponsors
include ../sponsor-icons.pug include ../sponsor-icons.pug

View File

@@ -1,7 +1,7 @@
block header block header
nav.bg-light nav.bg-light
if mobileViewer if mobileViewer
a.btn.nodropdown-toggle#languageDropdown(href="javascript:;" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false") Language a.btn.nodropdown-toggle#languageDropdown(href="javascript:;" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false") Language
.noscriptdropdown(aria-labelledby="languageDropdown") .noscriptdropdown(aria-labelledby="languageDropdown")
each language in languages each language in languages

View File

@@ -3,9 +3,7 @@
.modal-content .modal-content
.modal-header .modal-header
h5.modal-title Something alert worthy h5.modal-title Something alert worthy
button.close(type="button" data-dismiss="modal" aria-hidden="true" aria-label="Close") button.btn-close(type="button" data-bs-dismiss="modal" aria-hidden="true" aria-label="Close")
span(aria-hidden="true")
| &times;
.modal-body .modal-body
.modal-footer .modal-footer
button.btn.btn-outline-primary(type="button" data-dismiss="modal" data-cy="close-alert-btn") Close button.btn.btn-outline-primary(type="button" data-bs-dismiss="modal" data-cy="close-alert-btn") Close

View File

@@ -3,9 +3,7 @@
.modal-content .modal-content
.modal-header .modal-header
h5.modal-title Compilers h5.modal-title Compilers
button.close(type="button" data-dismiss="modal" aria-hidden="true" aria-label="Close") button.btn-close(type="button" data-bs-dismiss="modal" aria-hidden="true" aria-label="Close")
span(aria-hidden="true")
| &times;
.modal-body .modal-body
.card .card
.card-body .card-body

View File

@@ -3,12 +3,11 @@
.modal-content .modal-content
.modal-header .modal-header
h5.modal-title Enter something here h5.modal-title Enter something here
button.close(type="button" data-dismiss="modal" aria-hidden="true" aria-label="Close") button.btn-close(type="button" data-bs-dismiss="modal" aria-hidden="true" aria-label="Close")
span(aria-hidden="true")
| &times;
.modal-body .modal-body
.question .mb-3
input.question-answer label.form-label.question
input.form-control.question-answer
.modal-footer .modal-footer
button.btn.btn-light.no(type="button" data-dismiss="modal") Cancel button.btn.btn-light.no(type="button" data-bs-dismiss="modal") Cancel
button.btn.btn-light.yes(type="button" data-dismiss="modal") Ok button.btn.btn-light.yes(type="button" data-bs-dismiss="modal") Ok

View File

@@ -3,9 +3,7 @@
.modal-content .modal-content
.modal-header .modal-header
h5.modal-title History h5.modal-title History
button.close(type="button" data-dismiss="modal" aria-hidden="true" aria-label="Close") button.btn-close(type="button" data-bs-dismiss="modal" aria-hidden="true" aria-label="Close")
span(aria-hidden="true")
| &times;
.modal-body .modal-body
.card .card
.card-body .card-body
@@ -18,4 +16,4 @@
div.src-content#load-history-src-display(style="height:600px;overflow-y:scroll;flex:2") div.src-content#load-history-src-display(style="height:600px;overflow-y:scroll;flex:2")
div.monaco-placeholder div.monaco-placeholder
.modal-footer .modal-footer
button.btn.btn-outline-primary(type="button" data-dismiss="modal") Close button.btn.btn-outline-primary(type="button" data-bs-dismiss="modal") Close

View File

@@ -3,9 +3,7 @@
.modal-content .modal-content
.modal-header .modal-header
h5.modal-title Libraries h5.modal-title Libraries
button.close(type="button" data-dismiss="modal" aria-hidden="true" aria-label="Close") button.btn-close(type="button" data-bs-dismiss="modal" aria-hidden="true" aria-label="Close")
span(aria-hidden="true")
| &times;
.modal-body .modal-body
.card .card
.card-body .card-body
@@ -13,8 +11,9 @@
.row .row
.col-lg .col-lg
div(style='text-align: center') div(style='text-align: center')
input.lib-search-input(value="" placeholder="Search text") .input-group.mb-3
button.btn.btn-primary.lib-search-button(type="button") Search input.form-control.lib-search-input(value="" placeholder="Search text")
button.btn.btn-primary.lib-search-button(type="button") Search
hr hr
.row .row
.col-lg.libs-how-to-use .col-lg.libs-how-to-use

View File

@@ -3,17 +3,15 @@
.modal-content(style="max-width: unset") .modal-content(style="max-width: unset")
.modal-header .modal-header
h5.modal-title Load and save editor text h5.modal-title Load and save editor text
button.close(type="button" data-dismiss="modal" aria-hidden="true" aria-label="Close") button.btn-close(type="button" data-bs-dismiss="modal" aria-label="Close")
span(aria-hidden="true")
| &times;
.modal-body .modal-body
.card .card
.card-header .card-header
ul.nav.nav-tabs.card-header-tabs(role="tablist") ul.nav.nav-tabs.card-header-tabs(role="tablist")
li.nav-item(role="presentation"): a.nav-link.active(href="#load-examples" role="tab" data-toggle="tab") Examples li.nav-item(role="presentation"): a.nav-link.active(href="#load-examples" role="tab" data-bs-toggle="tab") Examples
li.nav-item(role="presentation"): a.nav-link(href="#load-browser-local" role="tab" data-toggle="tab") Browser-local storage li.nav-item(role="presentation"): a.nav-link(href="#load-browser-local" role="tab" data-bs-toggle="tab") Browser-local storage
li.nav-item(role="presentation"): a.nav-link(href="#load-browser-local-history" role="tab" data-toggle="tab") Browser-local history li.nav-item(role="presentation"): a.nav-link(href="#load-browser-local-history" role="tab" data-bs-toggle="tab") Browser-local history
li.nav-item(role="presentation"): a.nav-link(href="#load-file-system" role="tab" data-toggle="tab") File system li.nav-item(role="presentation"): a.nav-link(href="#load-file-system" role="tab" data-bs-toggle="tab") File system
.card-body.tab-content .card-body.tab-content
#load-examples.tab-pane.active(role="tabpanel") #load-examples.tab-pane.active(role="tabpanel")
h4.card-title Load from examples: h4.card-title Load from examples:
@@ -23,26 +21,26 @@
h4.card-title Load from browser-local storage: h4.card-title Load from browser-local storage:
ul.local-storage.small-v-scrollable ul.local-storage.small-v-scrollable
li.template li.template
a.mr-3(href="javascript:;") a.me-3(href="javascript:;")
button.overwrite.btn.btn-sm.btn-light.mr-1 button.overwrite.btn.btn-sm.btn-light.me-1
span.fa.fa-edit.mr-1 span.fa.fa-edit.me-1
| Overwrite | Overwrite
button.delete.btn.btn-sm.btn-light button.delete.btn.btn-sm.btn-light
span.fa.fa-trash.mr-1 span.fa.fa-trash.me-1
| Delete | Delete
.input-group .input-group.mb-3
input.save-name(type="text" size="20" placeholder="Set name of state") input.form-control.save-name(type="text" placeholder="Set name of state")
.input-group-append button.btn.btn-light.save-button(type="button") Save to browser-local storage
button.btn.btn-light.save-button(type="button") Save to browser-local storage
#load-browser-local-history.tab-pane(role="tabpanel") #load-browser-local-history.tab-pane(role="tabpanel")
h4.card-title Load from browser-local history: h4.card-title Load from browser-local history:
ul.local-history.small-v-scrollable ul.local-history.small-v-scrollable
li.template: a(href="javascript:;") li.template: a(href="javascript:;")
#load-file-system.tab-pane(role="tabpanel") #load-file-system.tab-pane(role="tabpanel")
h4.card-title Load/save to your system h4.card-title Load/save to your system
label.btn.btn-outline-secondary.btn-file(role="button") Load from a local file .mb-3
input.local-file(type="file") label.form-label Load from a local file
label.btn.btn-outline-secondary.btn-file.save-btn(role="button") Save to file input.form-control.local-file(type="file")
input.save-file(type="button") .mb-3
button.btn.btn-outline-secondary.save-btn(type="button") Save to file
.modal-footer .modal-footer
button.btn.btn-outline-primary(type="button" data-dismiss="modal" aria-label="Close") Close button.btn.btn-outline-primary(type="button" data-bs-dismiss="modal" aria-label="Close") Close

View File

@@ -3,9 +3,7 @@
.modal-content .modal-content
.modal-header .modal-header
h5.modal-title Compiler overrides h5.modal-title Compiler overrides
button.close(type="button" data-dismiss="modal" aria-hidden="true" aria-label="Close") button.btn-close(type="button" data-bs-dismiss="modal" aria-hidden="true" aria-label="Close")
span(aria-hidden="true")
| &times;
.modal-body .modal-body
.card .card
.card-body .card-body
@@ -25,8 +23,8 @@
span.override-name Compiler environment variables span.override-name Compiler environment variables
.card-body .card-body
span.description One environment variable per line, KEY=VALUE, that will be set during compilation. span.description One environment variable per line, KEY=VALUE, that will be set during compilation.
span.custom-override span
textarea.envvars(cols="30") textarea.form-control.envvars(cols="30", rows="3")
.possible-overrides.items .possible-overrides.items
.overrides-favorites-col.col-md .overrides-favorites-col.col-md
h6 Favorites h6 Favorites

View File

@@ -3,9 +3,7 @@
.modal-content .modal-content
.modal-header .modal-header
h5.modal-title Rename Pane h5.modal-title Rename Pane
button.close(type="button" data-dismiss="modal" aria-hidden="true" aria-label="Close") button.btn-close(type="button" data-bs-dismiss="modal" aria-hidden="true" aria-label="Close")
span(aria-hidden="true")
| &times;
.modal-body .modal-body
.card .card
.card-body .card-body
@@ -13,5 +11,5 @@
.input-group .input-group
input#renamepaneinput.form-control.panename(type="text" placeholder="New pane name" size="1024") input#renamepaneinput.form-control.panename(type="text" placeholder="New pane name" size="1024")
.modal-footer .modal-footer
button.btn.btn-outline-secondary(type="button" data-dismiss="modal") Close button.btn.btn-outline-secondary(type="button" data-bs-dismiss="modal") Close
button#renamepanesubmit.btn.btn-outline-primary(type="submit" data-dismiss="modal" value="Save Changes") Save Changes button#renamepanesubmit.btn.btn-outline-primary(type="submit" data-bs-dismiss="modal" value="Save Changes") Save Changes

View File

@@ -3,9 +3,7 @@
.modal-content .modal-content
.modal-header .modal-header
h5.modal-title Runtime tools h5.modal-title Runtime tools
button.close(type="button" data-dismiss="modal" aria-hidden="true" aria-label="Close") button.btn-close(type="button" data-bs-dismiss="modal" aria-hidden="true" aria-label="Close")
span(aria-hidden="true")
| &times;
.modal-body .modal-body
.card .card
.card-body .card-body
@@ -23,8 +21,8 @@
span.runtimetool-name Runtime environment variables span.runtimetool-name Runtime environment variables
.card-body .card-body
span.description One environment variable per line, KEY=VALUE. span.description One environment variable per line, KEY=VALUE.
span.custom-runtimetool span
textarea.envvars(cols="30") textarea.form-control.envvars(cols="30", rows="3")
.possible-runtimetools.items .possible-runtimetools.items
.runtimetools-favorites-col.col-md .runtimetools-favorites-col.col-md
h6 Favorites h6 Favorites

View File

@@ -1,18 +1,17 @@
mixin checkbox(cls, text) mixin checkbox(cls, text)
.checkbox .form-check
label input.form-check-input(class=cls type="checkbox" id="settings-checkbox-"+cls)
input(class=cls type="checkbox") label.form-check-label(for="settings-checkbox-"+cls)!= text
!= text
mixin select(cls, text) mixin select(cls, text)
div .mb-3
label!= text label.form-label(for="settings-select-"+cls)!= text
select(class=cls) select.form-select(class=cls id="settings-select-"+cls)
mixin input(cls, type, text, style) mixin input(cls, type, text, style)
div .mb-3
label!= text label.form-label(for="settings-input-"+cls)!= text
input(class=cls type=type style=style) input.form-control(class=cls type=type style=style id="settings-input-"+cls)
#settings.modal.fade.gl_keep(tabindex="-1" role="dialog") #settings.modal.fade.gl_keep(tabindex="-1" role="dialog")
@@ -21,8 +20,7 @@ mixin input(cls, type, text, style)
.modal-header .modal-header
h5.modal-title h5.modal-title
| Compiler Explorer Settings | Compiler Explorer Settings
button.close(type="button" data-dismiss="modal" aria-label="Close") button.btn-close(type="button" data-bs-dismiss="modal" aria-label="Close")
span(aria-hidden="true") &times;
.modal-body .modal-body
.card .card
.card-header .card-header
@@ -30,53 +28,54 @@ mixin input(cls, type, text, style)
| preserved as part of shared URLs, and are persisted locally using browser | preserved as part of shared URLs, and are persisted locally using browser
| local storage. | local storage.
ul.nav.nav-tabs.card-header-tabs(role="tablist") ul.nav.nav-tabs.card-header-tabs(role="tablist")
li.nav-item(role="presentation"): a.nav-link.active(href="#colouring" role="tab" data-toggle="tab") Colouring li.nav-item(role="presentation"): a.nav-link.active(href="#colouring" role="tab" data-bs-toggle="tab") Colouring
li.nav-item(role="presentation"): a.nav-link(href="#site-behaviour" role="tab" data-toggle="tab") Site behaviour li.nav-item(role="presentation"): a.nav-link(href="#site-behaviour" role="tab" data-bs-toggle="tab") Site behaviour
li.nav-item(role="presentation"): a.nav-link(href="#keybindings" role="tab" data-toggle="tab") Keybindings li.nav-item(role="presentation"): a.nav-link(href="#keybindings" role="tab" data-bs-toggle="tab") Keybindings
li.nav-item(role="presentation"): a.nav-link(href="#editor" role="tab" data-toggle="tab") Editor li.nav-item(role="presentation"): a.nav-link(href="#editor" role="tab" data-bs-toggle="tab") Editor
li.nav-item(role="presentation"): a.nav-link(href="#compilation" role="tab" data-toggle="tab") Compilation li.nav-item(role="presentation"): a.nav-link(href="#compilation" role="tab" data-bs-toggle="tab") Compilation
.card-body.tab-content .card-body.tab-content
#colouring.tab-pane.active.form-group(role="group") #colouring.tab-pane.active(role="group")
+select("theme", "Site theme") +select("theme", "Site theme")
+checkbox("colourise", "Colourise lines to show how the source maps to the output") +checkbox("colourise", "Colourise lines to show how the source maps to the output")
+checkbox("alwaysEnableAllSchemes", "Make all colour schemes available regardless of theme") +checkbox("alwaysEnableAllSchemes", "Make all colour schemes available regardless of theme")
+select("colourScheme", "Line highlighting colour scheme") +select("colourScheme", "Line highlighting colour scheme")
#site-behaviour.tab-pane.form-group(role="group") #site-behaviour.tab-pane(role="group")
label .mb-3
| Default language label.form-label(for="settings-select-defaultLanguage") Default language
small(style="margin-left: 3px") small(style="margin-left: 3px")
span.fas.fa-info-circle(title="New editors only (Reset UI to clear yours)" aria-label="New editors only (Reset UI to clear yours)") span.fas.fa-info-circle(title="New editors only (Reset UI to clear yours)" aria-label="New editors only (Reset UI to clear yours)")
select.defaultLanguage select.form-select.defaultLanguage(id="settings-select-defaultLanguage")
+checkbox("keepMultipleTabs", "Keep page status per tab") +checkbox("keepMultipleTabs", "Keep page status per tab")
+checkbox("allowStoreCodeDebug", "Allow my source code to be temporarily stored for diagnostic purposes in the event of an error") +checkbox("allowStoreCodeDebug", "Allow my source code to be temporarily stored for diagnostic purposes in the event of an error")
+checkbox("newEditorLastLang", "Use last selected language when opening new Editors") +checkbox("newEditorLastLang", "Use last selected language when opening new Editors")
+checkbox("enableCommunityAds", "Show community events") +checkbox("enableCommunityAds", "Show community events")
#keybindings.tab-pane.form-group(role="group") #keybindings.tab-pane(role="group")
+checkbox("useVim", "Vim editor mode") +checkbox("useVim", "Vim editor mode")
.the-save-option-to-auto-share .the-save-option-to-auto-share
label mb-3
kbd Ctrl label.form-label(for="settings-checkbox-enableCtrlS")
| + kbd Ctrl
kbd S | +
| &nbsp;behaviour kbd S
select.enableCtrlS | &nbsp;behaviour
.checkbox.the-save-option-to-tree-save select.form-select.enableCtrlS(id="settings-checkbox-enableCtrlS")
label .form-check.the-save-option-to-tree-save
input.enableCtrlStree(type="checkbox") input.form-check-input.enableCtrlStree(type="checkbox" id="settings-checkbox-enableCtrlStree")
label.form-check-label(for="settings-checkbox-enableCtrlStree")
| Make | Make
kbd Ctrl kbd Ctrl
| + | +
kbd S kbd S
| &nbsp;include and save the file to a Tree if that's added to the UI | &nbsp;include and save the file to a Tree if that's added to the UI
.checkbox.the-popup-dialog-box-option .form-check.the-popup-dialog-box-option
label input.form-check-input.enableSharingPopover(type="checkbox" id="settings-checkbox-enableSharingPopover")
input.enableSharingPopover(type="checkbox") label.form-check-label(for="settings-checkbox-enableSharingPopover")
| Pop up a dialog box when | Pop up a dialog box when
kbd Ctrl kbd Ctrl
| + | +
kbd S kbd S
| &nbsp; is set to create a short link. | &nbsp; is set to create a short link.
#editor.tab-pane.form-group(role="group") #editor.tab-pane(role="group")
+input("editorsFFont", "text", "Desired Font Family in editors", "width:100%") +input("editorsFFont", "text", "Desired Font Family in editors", "width:100%")
+select("defaultFontScale", "Default font scale") +select("defaultFontScale", "Default font scale")
+checkbox("editorsFLigatures", "Enable font ligatures") +checkbox("editorsFLigatures", "Enable font ligatures")
@@ -97,19 +96,20 @@ mixin input(cls, type, text, style)
+checkbox("wordWrap", "Enable Word Wrapping") +checkbox("wordWrap", "Enable Word Wrapping")
+checkbox("enableCodeLens", "Enable CodeLens features (requires refresh to take effect)") +checkbox("enableCodeLens", "Enable CodeLens features (requires refresh to take effect)")
+checkbox("colouriseBrackets", "Colourise matching bracket pairs") +checkbox("colouriseBrackets", "Colourise matching bracket pairs")
#compilation.tab-pane.form-group(role="group") #compilation.tab-pane(role="group")
div div
+checkbox("compileOnChange", "Compile automatically when source changes") +checkbox("compileOnChange", "Compile automatically when source changes")
+checkbox("autoDelayBeforeCompile", "Use automatic delay before compiling") +checkbox("autoDelayBeforeCompile", "Use automatic delay before compiling")
div div
label Delay before compiling:&nbsp; mb-3
span.delay-current-value &nbsp; label.form-label(for="settings-input-delay") Delay before compiling:&nbsp;
.slider-input span.delay-current-value &nbsp;
b 0.25s .slider-input(id="settings-input-delay")
input.delay(type="range") b 0.25s
b 3s input.delay(type="range")
b 3s
+checkbox("formatOnCompile", "Enable formatting on compilation (for supported languages)") +checkbox("formatOnCompile", "Enable formatting on compilation (for supported languages)")
+checkbox("executorCompileOnChange", "Compile executor automatically when arguments change") +checkbox("executorCompileOnChange", "Compile executor automatically when arguments change")
+checkbox("shakeStatusIconOnWarnings", "Shake the status icon on argument warnings") +checkbox("shakeStatusIconOnWarnings", "Shake the status icon on argument warnings")
.modal-footer .modal-footer
button.btn.btn-outline-primary(type="button" data-dismiss="modal") Close button.btn.btn-outline-primary(type="button" data-bs-dismiss="modal") Close

View File

@@ -3,26 +3,23 @@
.modal-content .modal-content
.modal-header .modal-header
h5.modal-title Share h5.modal-title Share
button.close(type="button" data-dismiss="modal" aria-hidden="true" aria-label="Close") button.btn-close(type="button" data-bs-dismiss="modal" aria-hidden="true" aria-label="Close")
span(aria-hidden="true")
| &times;
.modal-body .modal-body
.card .card
.card-body .card-body
.container .container
label.form-label.visually-hidden(for="share-permalink") Share URL
.input-group .input-group
input.form-control.permalink(type="text" placeholder="Loading" readonly size="1024") input.form-control.permalink#share-permalink(type="text" placeholder="Loading" readonly size="1024" aria-describedby="share-permalink-help")
.input-group-append button.btn.btn-outline-secondary.clippy(type="button" data-clipboard-target="#sharelinkdialog .permalink" title="Copy to clipboard" aria-label="Copy to clipboard")
button.btn.btn-outline-secondary.clippy(type="button" data-clipboard-target="#sharelinkdialog .permalink" title="Copy to clipboard") span.fa.fa-clipboard
span.fa.fa-clipboard small.form-text.text-muted#share-permalink-help Use this URL to share your code with others
#embedsettings.form-group(role="group") #embedsettings.mb-3(role="group")
.checkbox .form-check
label input.form-check-input.readOnly(type="checkbox" id="share-checkbox-readonly")
input.readOnly(type="checkbox") label.form-check-label(for="share-checkbox-readonly") Read Only
| Read Only .form-check
.checkbox input.form-check-input.hideEditorToolbars(type="checkbox" id="share-checkbox-hideEditorToolbars")
label label.form-check-label(for="share-checkbox-hideEditorToolbars") Hide Editor Toolbars
input.hideEditorToolbars(type="checkbox")
| Hide Editor Toolbars
if sharingEnabled if sharingEnabled
.socialsharing.mt-2 .socialsharing.mt-2

View File

@@ -3,9 +3,7 @@
.modal-content .modal-content
.modal-header .modal-header
h5.modal-title Load Site Template h5.modal-title Load Site Template
button.close(type="button" data-dismiss="modal" aria-hidden="true" aria-label="Close") button.btn-close(type="button" data-bs-dismiss="modal" aria-hidden="true" aria-label="Close")
span(aria-hidden="true")
| &times;
.modal-body .modal-body
.container .container
.row(id="site-template-modal-row") .row(id="site-template-modal-row")

View File

@@ -3,10 +3,8 @@
.modal-content .modal-content
.modal-header .modal-header
h5.modal-title Timing h5.modal-title Timing
button.close(type="button" data-dismiss="modal" aria-hidden="true" aria-label="Close") button.btn-close(type="button" data-bs-dismiss="modal" aria-hidden="true" aria-label="Close")
span(aria-hidden="true")
| &times;
.modal-body .modal-body
#chart #chart
.modal-footer .modal-footer
button.btn.btn-outline-primary(type="button" data-dismiss="modal") Close button.btn.btn-outline-primary(type="button" data-bs-dismiss="modal") Close

View File

@@ -3,10 +3,8 @@
.modal-content .modal-content
.modal-header .modal-header
h5.modal-title Well, do you or not? h5.modal-title Well, do you or not?
button.close(type="button" data-dismiss="modal" aria-hidden="true" aria-label="Close") button.btn-close(type="button" data-bs-dismiss="modal" aria-hidden="true" aria-label="Close")
span(aria-hidden="true")
| &times;
.modal-body .modal-body
.modal-footer .modal-footer
button.btn.btn-link.no(type="button" data-dismiss="modal") No button.btn.btn-link.no(type="button" data-bs-dismiss="modal") No
button.btn.btn-link.yes(type="button" data-dismiss="modal") Yes button.btn.btn-link.yes(type="button" data-bs-dismiss="modal") Yes

View File

@@ -3,7 +3,7 @@
.btn-group.btn-group-sm(role="group") .btn-group.btn-group-sm(role="group")
select.function-selector select.function-selector
.btn-group.btn-group-sm(role="group" aria-label="CFG Export") .btn-group.btn-group-sm(role="group" aria-label="CFG Export")
button.btn.btn-sm.btn-light.dropdown-toggle(type="button" title="LLVM Opt Pass Options" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Set output options") button.btn.btn-sm.btn-light.dropdown-toggle(type="button" title="LLVM Opt Pass Options" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Set output options")
span.fas.fa-arrow-down span.fas.fa-arrow-down
span.hideable Export span.hideable Export
.dropdown-menu .dropdown-menu

View File

@@ -8,7 +8,7 @@ mixin optionButton(bind, isActive, text, title)
.top-bar.btn-toolbar.bg-light(role="toolbar") .top-bar.btn-toolbar.bg-light(role="toolbar")
include ../../font-size include ../../font-size
.btn-group.btn-group-sm.options(role="group") .btn-group.btn-group-sm.options(role="group")
button.btn.btn-sm.btn-light.dropdown-toggle(type="button" title="ClangIR Options" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Set output options") button.btn.btn-sm.btn-light.dropdown-toggle(type="button" title="ClangIR Options" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Set output options")
span.fas.fa-anchor span.fas.fa-anchor
span.hideable Options span.hideable Options
.dropdown-menu .dropdown-menu

View File

@@ -10,7 +10,7 @@ mixin newPaneButton(classId, text, title, label, icon)
button.btn.btn-sm.btn-light.load-save(title="Load or save text" aria-label="Load or save text") button.btn.btn-sm.btn-light.load-save(title="Load or save text" aria-label="Load or save text")
span.fa.fa-save span.fa.fa-save
span.hideable Save/Load span.hideable Save/Load
button.dropdown-toggle.btn.btn-sm.btn-light.add-pane(type="button" title="Add a new pane" aria-label="Add a new pane" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" data-cy="new-editor-dropdown-btn") button.dropdown-toggle.btn.btn-sm.btn-light.add-pane(type="button" title="Add a new pane" aria-label="Add a new pane" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" data-cy="new-editor-dropdown-btn")
span.fa.fa-plus span.fa.fa-plus
span.hideable Add new... span.hideable Add new...
.dropdown-menu(data-cy="new-editor-pane-dropdown") .dropdown-menu(data-cy="new-editor-pane-dropdown")
@@ -18,7 +18,7 @@ mixin newPaneButton(classId, text, title, label, icon)
+newPaneButton("add-executor", "Execution Only", "Add a new executor for this source", "New executor", "fas fa-microchip") +newPaneButton("add-executor", "Execution Only", "Add a new executor for this source", "New executor", "fas fa-microchip")
+newPaneButton("conformance", "Conformance View", "Add a new conformance view", "New conformance view", "fa fa-list") +newPaneButton("conformance", "Conformance View", "Add a new conformance view", "New conformance view", "fa fa-list")
+newPaneButton("add-editor", "Source Editor", "Add a new source editor", "New source editor", "fa fa-code") +newPaneButton("add-editor", "Source Editor", "Add a new source editor", "New source editor", "fa fa-code")
button#vim-flag.btn.btn-sm.btn-light(title="Toggle Vim Keybindings") button.vim-flag.btn.btn-sm.btn-light(title="Toggle Vim Keybindings")
span.fab.fa-vimeo-v span.fab.fa-vimeo-v
span.hideable Vim span.hideable Vim
if thirdPartyIntegrationEnabled if thirdPartyIntegrationEnabled
@@ -35,10 +35,10 @@ mixin newPaneButton(classId, text, title, label, icon)
button.btn.btn-sm.btn-outline-info.ctrlSNothing(disabled=true style="display: none") button.btn.btn-sm.btn-outline-info.ctrlSNothing(disabled=true style="display: none")
span.fas.fa-smile span.fas.fa-smile
.btn-group.btn-group-sm.ml-auto(role="group" aria-label="Editor language") .btn-group.btn-group-sm.ms-auto(role="group" aria-label="Editor language")
button.btn.btn-sm.language-info button.btn.btn-sm.language-info
span.fas.fa-info span.fas.fa-info
select.change-language(title="Change this editor's (and associated panels) language" placeholder="Language" disabled=embedded && readOnly) select.change-language(title="Change this editor's (and associated panels) language" placeholder="Language" disabled=embedded && readOnly)
div.currentCursorPosition div.currentCursorPosition
div#v-status div.v-status
.monaco-placeholder .monaco-placeholder

View File

@@ -10,4 +10,4 @@
button.btn.btn-sm.btn-light.select-all(type="button" title="Select all lines") button.btn.btn-sm.btn-light.select-all(type="button" title="Select all lines")
span.fa.fa-align-justify(style="border: 1px dotted white;border-radius: 0.125em;padding: 2px") span.fa.fa-align-justify(style="border: 1px dotted white;border-radius: 0.125em;padding: 2px")
span.hideable Select all span.hideable Select all
pre.content.output-content pre.content.output-content(aria-live="polite" role="log" aria-label="Compiler output")

View File

@@ -7,34 +7,30 @@ mixin newPaneButton(classId, text, title, icon)
.top-bar.btn-toolbar.bg-light(role="toolbar") .top-bar.btn-toolbar.bg-light(role="toolbar")
.btn-group.btn-group-sm(role="group" aria-label="Compiler picker") .btn-group.btn-group-sm(role="group" aria-label="Compiler picker")
.input-group .input-group
.input-group-prepend select.compiler-picker
.input-group button.btn.btn-sm.btn-light.input-group-text.picker-popout-button(data-trigger="click" style="cursor: pointer;" role="button" title="Compiler picker popout")
select.compiler-picker span
.input-group-append i.fa-solid.fa-arrow-up-right-from-square
button.btn.btn-sm.btn-light.input-group-text.picker-popout-button(data-trigger="click" style="cursor: pointer;" role="button" title="Compiler picker popout") button.btn.btn-sm.btn-light.input-group-text.prepend-options(data-trigger="click" style="cursor: pointer;" role="button" title="All compilation options")
span span.status-icon
i.fa-solid.fa-arrow-up-right-from-square input.options.form-control(type="text" placeholder="Compiler options..." size="256" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" aria-label="Compiler options")
.input-group-append .populararguments(title="Popular arguments")
button.btn.btn-sm.btn-light.input-group-text.prepend-options(data-trigger="click" style="cursor: pointer;" role="button" title="All compilation options") button.btn.btn-sm.btn-light.btn-outline-secondary.dropdown-toggle.dropdown-toggle-split.popular-arguments-btn(type="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false")
span.status-icon span.visually-hidden Popular arguments
input.options.form-control(type="text" placeholder="Compiler options..." size="256" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false") div.dropdown-menu.dropdown-menu-end
.input-group-append.populararguments(title="Popular arguments")
button.btn.btn-sm.btn-light.btn-outline-secondary.dropdown-toggle.dropdown-toggle-split.popular-arguments-btn(type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false")
span.sr-only Popular arguments
div.dropdown-menu.dropdown-menu-right
button.dropdown-item.btn.btn-light.btn-sm button.dropdown-item.btn.btn-light.btn-sm
.argmenuitem .argmenuitem
span.argtitle Detailed Compiler Flags span.argtitle Detailed Compiler Flags
span.argdescription Open a new window to edit verbose compiler flags span.argdescription Open a new window to edit verbose compiler flags
include ../../font-size include ../../font-size
.btn-group.btn-group-sm.filters(role="group") .btn-group.btn-group-sm.filters(role="group")
button.btn.btn-sm.btn-light.dropdown-toggle(type="button" title="Compiler output options" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Change how the compiler's output is generated") button.btn.btn-sm.btn-light.dropdown-toggle(type="button" title="Compiler output options" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Change how the compiler's output is generated")
span.fas.fa-cog span.fas.fa-cog
span.hideable Output... span.hideable Output...
.dropdown-menu .dropdown-menu
include ../../options-output include ../../options-output
.btn-group.btn-group-sm.filters(role="group") .btn-group.btn-group-sm.filters(role="group")
button.btn.btn-sm.btn-light.dropdown-toggle(type="button" title="Compiler output filters" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Change how the compiler output is filtered") button.btn.btn-sm.btn-light.dropdown-toggle(type="button" title="Compiler output filters" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Change how the compiler output is filtered")
span.fas.fa-filter span.fas.fa-filter
span.hideable Filter... span.hideable Filter...
.dropdown-menu .dropdown-menu
@@ -48,10 +44,10 @@ mixin newPaneButton(classId, text, title, icon)
span.fas.fa-wrench span.fas.fa-wrench
span.dp-text.hideable Overrides span.dp-text.hideable Overrides
.btn-group.btn-group-sm(role="group") .btn-group.btn-group-sm(role="group")
button.btn.btn-sm.btn-light.dropdown-toggle.add-pane(type="button" title="Add a new pane" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Add new element for this compiler" data-cy="new-compiler-dropdown-btn") button.btn.btn-sm.btn-light.dropdown-toggle.add-pane(type="button" title="Add a new pane" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Add new element for this compiler" data-cy="new-compiler-dropdown-btn")
span.fas.fa-plus span.fas.fa-plus
span.hideable Add new... span.hideable Add new...
.dropdown-menu.dropdown-menu-right.new-pane-dropdown(data-cy="new-compiler-pane-dropdown") .dropdown-menu.dropdown-menu-end.new-pane-dropdown(data-cy="new-compiler-pane-dropdown")
+newPaneButton("add-compiler", "Clone Compiler", "Clone this compiler window (click or drag)", "far fa-clone") +newPaneButton("add-compiler", "Clone Compiler", "Clone this compiler window (click or drag)", "far fa-clone")
+newPaneButton("create-executor", "Executor From This", "Create executor from this compiler", "fas fa-microchip") +newPaneButton("create-executor", "Executor From This", "Create executor from this compiler", "fas fa-microchip")
+newPaneButton("view-optimization", "Opt Remarks", "Show optimization remarks", "fas fa-weight") +newPaneButton("view-optimization", "Opt Remarks", "Show optimization remarks", "fas fa-weight")
@@ -73,22 +69,21 @@ mixin newPaneButton(classId, text, title, icon)
+newPaneButton("view-gnatdebug", "GNAT Debug Expanded Code", "Show GNAT debug expanded code", "fas fa-tree") +newPaneButton("view-gnatdebug", "GNAT Debug Expanded Code", "Show GNAT debug expanded code", "fas fa-tree")
+newPaneButton("view-cfg", "Control Flow Graph", "Show assembly control flow graphs", "fas fa-exchange-alt") +newPaneButton("view-cfg", "Control Flow Graph", "Show assembly control flow graphs", "fas fa-exchange-alt")
.btn-group.btn-group-sm(role="group") .btn-group.btn-group-sm(role="group")
button.btn.btn-sm.btn-light.dropdown-toggle.add-tool(type="button" title="Add tool" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Add tooling to this editor and compiler") button.btn.btn-sm.btn-light.dropdown-toggle.add-tool(type="button" title="Add tool" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Add tooling to this editor and compiler")
span.fas.fa-screwdriver span.fas.fa-screwdriver
span.hideable Add tool... span.hideable Add tool...
.dropdown-menu.dropdown-menu-right.new-tool-dropdown .dropdown-menu.dropdown-menu-end.new-tool-dropdown
.monaco-placeholder .monaco-placeholder
.bottom-bar.bg-light .bottom-bar.bg-light
if !embedded if !embedded
.btn-group.btn-group-sm .btn-group.btn-group-sm
.input-group .input-group
.input-group-prepend button.btn.btn-sm.btn-light.clear-cache(title="Clear cache & recompile" aria-label="Clear cache and recompile")
button.btn.btn-sm.btn-light.clear-cache(title="Clear cache & recompile") span.fas.fa-redo
span.fas.fa-redo button.btn.btn-sm.btn-light.output-btn(data-cy="new-output-pane-btn" aria-label="Show compiler output")
button.btn.btn-sm.btn-light.output-btn(data-cy="new-output-pane-btn")
span.fas.fa-receipt.status-text span.fas.fa-receipt.status-text
| &nbsp;Output | &nbsp;Output
span.output-count span.output-count(aria-live="polite")
| &nbsp;( | &nbsp;(
span.text-count span.text-count
| 0 | 0
@@ -96,11 +91,11 @@ mixin newPaneButton(classId, text, title, icon)
span.err-count span.err-count
| 0 | 0
| ) | )
span.short-compiler-name.mr-1 span.short-compiler-name.me-1(aria-hidden="true")
button.btn.btn-sm.btn-light.full-compiler-name(data-trigger="click" style="cursor: pointer;" role="button") button.btn.btn-sm.btn-light.full-compiler-name(data-trigger="click" style="cursor: pointer;" role="button" aria-label="Show compiler information")
span.fas.fa-info span.fas.fa-info
span.compile-info.mr-1(title="Compilation info") span.compile-info.me-1(title="Compilation info" aria-label="Compilation information")
button.btn.btn-sm.btn-light.full-timing-info(data-trigger="click" style="cursor: pointer;" role="button") button.btn.btn-sm.btn-light.full-timing-info(data-trigger="click" style="cursor: pointer;" role="button" aria-label="Show full timing information")
span.fas.fa-chart-bar span.fas.fa-chart-bar
button.btn.btn-sm.btn-light.compiler-license(data-trigger="click" style="cursor: pointer;" role="button") button.btn.btn-sm.btn-light.compiler-license(data-trigger="click" style="cursor: pointer;" role="button" aria-label="View compiler license")
span Compiler License span Compiler License

View File

@@ -2,5 +2,5 @@
.top-bar.btn-toolbar.bg-light(role="toolbar") .top-bar.btn-toolbar.bg-light(role="toolbar")
include ../../font-size include ../../font-size
.btn-group.btn-group-sm(role="group") .btn-group.btn-group-sm(role="group")
select.change-device(placeholder="Select a device...") select.form-select.change-device(placeholder="Select a device...")
.monaco-placeholder .monaco-placeholder

View File

@@ -1,18 +1,16 @@
#diff #diff
.top-bar.btn-toolbar.bg-light(role="toolbar") .top-bar.btn-toolbar.bg-light.py-1.d-flex.align-items-center(role="toolbar")
include ../../font-size include ../../font-size
.btn-group.btn-group-sm .btn-group.btn-group-sm.flex-grow-1.me-2
.input-group.input-group-sm.mb-auto .input-group.input-group-sm.w-100
.input-group-prepend label.input-group-text
label.input-group-text | Left:&nbsp;
| Left:&nbsp;
select.diff-picker.lhs(placeholder="Select compiler...") select.diff-picker.lhs(placeholder="Select compiler...")
select.difftype-picker.lhsdifftype(placeholder="...") select.difftype-picker.lhsdifftype(placeholder="...")
.btn-group.btn-group-sm.ml-4 .btn-group.btn-group-sm.flex-grow-1
.input-group.input-group-sm.mb-auto .input-group.input-group-sm.w-100
.input-group-prepend label.input-group-text
label.input-group-text | Right:&nbsp;
| Right:&nbsp;
select.diff-picker.rhs(placeholder="Select compiler...") select.diff-picker.rhs(placeholder="Select compiler...")
select.difftype-picker.rhsdifftype(placeholder="...") select.difftype-picker.rhsdifftype(placeholder="...")
.monaco-placeholder .monaco-placeholder

View File

@@ -30,40 +30,37 @@
.top-bar.btn-toolbar.bg-light.panel-compilation(role="toolbar") .top-bar.btn-toolbar.bg-light.panel-compilation(role="toolbar")
.btn-group.btn-group-sm(role="group" aria-label="Compiler picker") .btn-group.btn-group-sm(role="group" aria-label="Compiler picker")
.input-group .input-group
.input-group-prepend select.compiler-picker
.input-group button.btn.btn-sm.btn-light.input-group-text.picker-popout-button(data-trigger="click" style="cursor: pointer;" role="button" title="Compiler picker popout")
select.compiler-picker span
.input-group-append i.fa-solid.fa-arrow-up-right-from-square
button.btn.btn-sm.btn-light.input-group-text.picker-popout-button(data-trigger="click" style="cursor: pointer;" role="button" title="Compiler picker popout") button.btn.btn-sm.btn-light.input-group-text.prepend-options(data-trigger="click" style="cursor: pointer;" role="button" title="All compilation options")
span span.btn.btn-sm.btn-light.status-icon
i.fa-solid.fa-arrow-up-right-from-square label.visually-hidden(for="compilation-options") Compiler options for execution
.input-group-append input.compilation-options.form-control#compilation-options(type="text" placeholder="Compiler options..." size="256" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" aria-label="Compiler options for execution")
button.btn.btn-sm.btn-light.input-group-text.prepend-options(data-trigger="click" style="cursor: pointer;" role="button" title="All compilation options")
span.btn.btn-sm.btn-light.status-icon
input.compilation-options.form-control(type="text" placeholder="Compiler options..." size="256" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false")
// TODO: Maybe enable this in the future? // TODO: Maybe enable this in the future?
//.input-group-append.populararguments(title="Popular arguments") //.populararguments(title="Popular arguments")
button.btn.btn-sm.btn-light.btn-outline-secondary.dropdown-toggle.dropdown-toggle-split.popular-arguments-btn(type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false") button.btn.btn-sm.btn-light.btn-outline-secondary.dropdown-toggle.dropdown-toggle-split.popular-arguments-btn(type="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false")
span.sr-only Popular arguments span.visually-hidden Popular arguments
div.dropdown-menu.dropdown-menu-right div.dropdown-menu.dropdown-menu-end
button.dropdown-item.btn.btn-light.btn-sm button.dropdown-item.btn.btn-light.btn-sm
.argmenuitem .argmenuitem
span.argtitle Detailed Compiler Flags span.argtitle Detailed Compiler Flags
span.argdescription Open a new window to edit verbose compiler flags span.argdescription Open a new window to edit verbose compiler flags
.top-bar.btn-toolbar.bg-light.panel-args.d-none(role="toolbar") .top-bar.btn-toolbar.bg-light.panel-args.d-none(role="toolbar")
input.execution-arguments.form-control(type="text" placeholder="Execution arguments..." size="256" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false") input.execution-arguments.form-control(type="text" placeholder="Execution arguments..." size="256" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" aria-label="Execution arguments")
.top-bar.btn-toolbar.bg-light.panel-stdin.d-none(role="toolbar") .top-bar.btn-toolbar.bg-light.panel-stdin.d-none(role="toolbar")
textarea.execution-stdin.form-control(placeholder="Execution stdin..." cols="1024" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" aria-multiline="false" style="resize: vertical") textarea.execution-stdin.form-control(placeholder="Execution stdin..." cols="1024" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" aria-multiline="true" style="resize: vertical" aria-label="Standard input for program execution")
pre.content pre.content
.execution-status .execution-status(aria-live="polite" role="status" aria-label="Execution status")
.compiler-output .compiler-output(aria-live="polite" role="log" aria-label="Compiler output")
.execution-output .execution-output(aria-live="polite" role="log" aria-label="Program execution output")
.bottom-bar.bg-light .bottom-bar.bg-light
button.btn.btn-sm.btn-light.rerun(title="Rerun") button.btn.btn-sm.btn-light.rerun(title="Rerun" aria-label="Rerun execution")
span.fas.fa-circle-play span.fas.fa-circle-play
button.btn.btn-sm.btn-light.clear-cache(title="Clear cache & recompile") button.btn.btn-sm.btn-light.clear-cache(title="Clear cache & recompile" aria-label="Clear cache and recompile")
span.fas.fa-redo span.fas.fa-redo
span.short-compiler-name span.short-compiler-name(aria-hidden="true")
button.btn.btn-sm.btn-light.fas.fa-info.full-compiler-name(data-trigger="click" style="cursor: pointer;" role="button") button.btn.btn-sm.btn-light.fas.fa-info.full-compiler-name(data-trigger="click" style="cursor: pointer;" role="button" aria-label="Show compiler info")
span.compile-time(title="Compilation time (Result size)") span.compile-time(title="Compilation time (Result size)" aria-label="Compilation time and result size")
button.btn.btn-sm.btn-light.fas.fa-chart-bar.full-timing-info(data-trigger="click" style="cursor: pointer;" role="button") button.btn.btn-sm.btn-light.fas.fa-chart-bar.full-timing-info(data-trigger="click" style="cursor: pointer;" role="button" aria-label="Show full timing information")

View File

@@ -10,7 +10,7 @@ mixin optionButton(bind, isActive, text, title)
.btn-group.btn-group-sm(role="group") .btn-group.btn-group-sm(role="group")
select.gccdump-pass-picker(placeholder="Select a pass...") select.gccdump-pass-picker(placeholder="Select a pass...")
.btn-group.btn-group-sm.dump-filters .btn-group.btn-group-sm.dump-filters
button.btn.btn-sm.btn-light.dropdown-toggle(type="button" title="Dump passes" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Available dump passes") button.btn.btn-sm.btn-light.dropdown-toggle(type="button" title="Dump passes" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Available dump passes")
span.fas.fa-compass span.fas.fa-compass
span.hideable Passes span.hideable Passes
.dropdown-menu .dropdown-menu
@@ -18,7 +18,7 @@ mixin optionButton(bind, isActive, text, title)
+optionButton("rtlDump", true, "RTL Pass", "Show RTL passes") +optionButton("rtlDump", true, "RTL Pass", "Show RTL passes")
+optionButton("ipaDump", true, "IPA Pass", "Show IPA passes") +optionButton("ipaDump", true, "IPA Pass", "Show IPA passes")
.btn-group.btn-group-sm.dump-filters .btn-group.btn-group-sm.dump-filters
button.btn.btn-sm.btn-light.dropdown-toggle(type="button" title="Dump pass options" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Control the details of the dump") button.btn.btn-sm.btn-light.dropdown-toggle(type="button" title="Dump pass options" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Control the details of the dump")
span.fas.fa-microchip span.fas.fa-microchip
span.hideable Options span.hideable Options
.dropdown-menu .dropdown-menu

View File

@@ -13,14 +13,14 @@ mixin optionButton(bind, isActive, text, title)
span Wrap lines span Wrap lines
input.d-none(type="checkbox" checked=false) input.d-none(type="checkbox" checked=false)
.btn-group.btn-group-sm.options(role="group") .btn-group.btn-group-sm.options(role="group")
button.btn.btn-sm.btn-light.dropdown-toggle(type="button" title="LLVM Opt Pass Options" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Set output options") button.btn.btn-sm.btn-light.dropdown-toggle(type="button" title="LLVM Opt Pass Options" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Set output options")
span.fas.fa-anchor span.fas.fa-anchor
span.hideable Options span.hideable Options
.dropdown-menu .dropdown-menu
+optionButton("demangle-symbols", true, "Demangle Symbols", "Demangle symbols") +optionButton("demangle-symbols", true, "Demangle Symbols", "Demangle symbols")
+optionButton("-fno-discard-value-names", true, "-fno-discard-value-names", "Keep value names instead of LLVM value numbers") +optionButton("-fno-discard-value-names", true, "-fno-discard-value-names", "Keep value names instead of LLVM value numbers")
.btn-group.btn-group-sm.filters(role="group") .btn-group.btn-group-sm.filters(role="group")
button.btn.btn-sm.btn-light.dropdown-toggle(type="button" title="LLVM Opt Pass Filters" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Set output filters") button.btn.btn-sm.btn-light.dropdown-toggle(type="button" title="LLVM Opt Pass Filters" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Set output filters")
span.fas.fa-filter span.fas.fa-filter
span.hideable Filters span.hideable Filters
.dropdown-menu .dropdown-menu

View File

@@ -8,7 +8,7 @@ mixin optionButton(bind, isActive, text, title)
.top-bar.btn-toolbar.bg-light(role="toolbar") .top-bar.btn-toolbar.bg-light(role="toolbar")
include ../../font-size include ../../font-size
.btn-group.btn-group-sm.options(role="group") .btn-group.btn-group-sm.options(role="group")
button.btn.btn-sm.btn-light.dropdown-toggle(type="button" title="Opt Pass Options" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Set output options") button.btn.btn-sm.btn-light.dropdown-toggle(type="button" title="Opt Pass Options" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Set output options")
span.fas.fa-anchor span.fas.fa-anchor
span.hideable Options span.hideable Options
.dropdown-menu .dropdown-menu
@@ -16,7 +16,7 @@ mixin optionButton(bind, isActive, text, title)
+optionButton("demangle-symbols", true, "Demangle Symbols", "Demangle symbols") +optionButton("demangle-symbols", true, "Demangle Symbols", "Demangle symbols")
+optionButton("-fno-discard-value-names", true, "-fno-discard-value-names", "Keep value names instead of LLVM value numbers") +optionButton("-fno-discard-value-names", true, "-fno-discard-value-names", "Keep value names instead of LLVM value numbers")
.btn-group.btn-group-sm.filters(role="group") .btn-group.btn-group-sm.filters(role="group")
button.btn.btn-sm.btn-light.dropdown-toggle(type="button" title="Opt Pass Filters" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Set output filters") button.btn.btn-sm.btn-light.dropdown-toggle(type="button" title="Opt Pass Filters" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Set output filters")
span.fas.fa-filter span.fas.fa-filter
span.hideable Filters span.hideable Filters
.dropdown-menu .dropdown-menu
@@ -26,9 +26,8 @@ mixin optionButton(bind, isActive, text, title)
//- +optionButton("library-functions", true, "Filter Library Functions", "Filter library functions") //- +optionButton("library-functions", true, "Filter Library Functions", "Filter library functions")
.btn-group.btn-group-sm .btn-group.btn-group-sm
.input-group.input-group-sm.mb-auto .input-group.input-group-sm.mb-auto
.input-group-prepend label.input-group-text.opt-group-name
label.input-group-text.opt-group-name | Function:&nbsp;
| Function:&nbsp;
select.opt-group-picker.group-selector(placeholder="Select group") select.opt-group-picker.group-selector(placeholder="Select group")
div.opt-pipeline-body div.opt-pipeline-body
.passes-column(style="width: 250px;") Passes: .passes-column(style="width: 250px;") Passes:

View File

@@ -14,7 +14,7 @@ mixin optionButton(bind, isActive, text, title)
input.d-none(type="checkbox" checked=false) input.d-none(type="checkbox" checked=false)
// TODO: Options - display inlining context, color token (from col-num), remove duplicates // TODO: Options - display inlining context, color token (from col-num), remove duplicates
.btn-group.btn-group-sm.filters(role="group") .btn-group.btn-group-sm.filters(role="group")
button.btn.btn-sm.btn-light.dropdown-toggle(type="button" title="Opt-Remarks Filters" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Set output filters") button.btn.btn-sm.btn-light.dropdown-toggle(type="button" title="Opt-Remarks Filters" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" aria-label="Set output filters")
span.fas.fa-filter span.fas.fa-filter
span.hideable Filters span.hideable Filters
.dropdown-menu .dropdown-menu

View File

@@ -1,7 +1,7 @@
#tree #tree
.top-bar.btn-toolbar.bg-light.mainbar(role="toolbar") .top-bar.btn-toolbar.bg-light.mainbar(role="toolbar")
.btn-group.btn-group-sm.menu(role="group" aria-label="Menu") .btn-group.btn-group-sm.menu(role="group" aria-label="Menu")
button.dropdown-toggle.btn.btn-sm.btn-light.file-menu(type="button" title="File" aria-label="File" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false") button.dropdown-toggle.btn.btn-sm.btn-light.file-menu(type="button" title="File" aria-label="File" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false")
span.fa.fa-save span.fa.fa-save
span.hideable Project span.hideable Project
.dropdown-menu .dropdown-menu
@@ -10,7 +10,7 @@
span.dropdown-icon.fa.fa-save span.dropdown-icon.fa.fa-save
| Save | Save
.btn-group.btn-group-sm.options(role="group" aria-label="Tree settings") .btn-group.btn-group-sm.options(role="group" aria-label="Tree settings")
button.dropdown-toggle.btn.btn-sm.btn-light.add-pane(type="button" title="Add a new pane" aria-label="Add a new pane" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false") button.dropdown-toggle.btn.btn-sm.btn-light.add-pane(type="button" title="Add a new pane" aria-label="Add a new pane" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false")
span.fa.fa-plus span.fa.fa-plus
span.hideable Add new... span.hideable Add new...
.dropdown-menu .dropdown-menu
@@ -27,19 +27,19 @@
button.btn.btn-sm.btn-light.cmake-project(type="button" title="CMake project" data-bind="isCMakeProject" aria-pressed="false" aria-label="CMake project") button.btn.btn-sm.btn-light.cmake-project(type="button" title="CMake project" data-bind="isCMakeProject" aria-pressed="false" aria-label="CMake project")
span CMake span CMake
input.d-none(type="checkbox" checked=false) input.d-none(type="checkbox" checked=false)
.btn-group.btn-group-sm.ml-auto(role="group" aria-label="Language") .btn-group.btn-group-sm.ms-auto(role="group" aria-label="Language")
select.change-language(title="Change the language" placeholder="Language" disabled=embedded && readOnly) select.change-language(title="Change the language" placeholder="Language" disabled=embedded && readOnly)
.top-bar.btn-toolbar.bg-light.panel-args.d-none(role="toolbar") .top-bar.btn-toolbar.bg-light.panel-args.d-none(role="toolbar")
input.cmake-arguments.form-control(type="text" placeholder="CMake arguments..." size="256" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false") input.cmake-arguments.form-control(type="text" placeholder="CMake arguments..." size="256" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false")
.top-bar.btn-toolbar.bg-light.panel-outputfile.d-none(role="toolbar") .top-bar.btn-toolbar.bg-light.panel-outputfile.d-none(role="toolbar")
input.cmake-customOutputFilename.form-control(type="text" placeholder="output.s" size="256" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false") input.cmake-customOutputFilename.form-control(type="text" placeholder="output.s" size="256" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false")
.tree.v-scroll .tree.v-scroll
ul.list-group ul.list-group.list-group-flush
li.root.list-group-item.far-fa-folder li.root.list-group-item.far-fa-folder
.group-header Included files .group-header Included files
ul.list-group.named-editors ul.list-group.list-group-flush.named-editors
li.root.list-group-item.far-fa-folder li.root.list-group-item.far-fa-folder
.group-header Excluded files .group-header Excluded files
ul.list-group.unnamed-editors ul.list-group.list-group-flush.unnamed-editors
li.drophere.row.align-items-center(style="display: none;") li.drophere.row.align-items-center(style="display: none;")
span.col-lg.text-center Drop files here span.col-lg.text-center Drop files here

View File

@@ -1,19 +1,14 @@
#compiler-selector #compiler-selector
.form-row .row
.input-group .input-group
.input-group-prepend select.compiler-picker
.input-group button.btn.btn-sm.btn-light.input-group-text.picker-popout-button(data-trigger="click" style="cursor: pointer;" role="button" title="Compiler picker popout")
select.compiler-picker span
.input-group-append i.fa-solid.fa-arrow-up-right-from-square
button.btn.btn-sm.btn-light.input-group-text.picker-popout-button(data-trigger="click" style="cursor: pointer;" role="button" title="Compiler picker popout") button.btn.btn-sm.btn-light.input-group-text.prepend-options(tabindex="0" data-trigger="focus" style="cursor: pointer;" role="button" title="All compilation options")
span span.btn.btn-sm.btn-light.status-icon
i.fa-solid.fa-arrow-up-right-from-square
.input-group-append
button.btn.btn-sm.btn-light.input-group-text.prepend-options(tabindex="0" data-trigger="focus" style="cursor: pointer;" role="button" title="All compilation options")
span.btn.btn-sm.btn-light.status-icon
input.conformance-options.form-control(type="text" placeholder="Compiler options..." size="256" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false") input.conformance-options.form-control(type="text" placeholder="Compiler options..." size="256" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false")
.input-group-append button.btn.btn-sm.fas.fa-receipt.d-none.compiler-out.compiler-selector-icon(disabled=true style="cursor: help;" title="This compiler has generated some output. Open its associated pane to read it.")
button.btn.btn-sm.fas.fa-receipt.d-none.compiler-out.compiler-selector-icon(disabled=true style="cursor: help;" title="This compiler has generated some output. Open its associated pane to read it.") button.btn.btn-sm.fas.fa-times.close-compiler.compiler-selector-icon(aria-label="Close" title="Close")
button.btn.btn-sm.fas.fa-times.close.compiler-selector-icon(aria-label="Close" title="Close") button.btn.btn-sm.fas.fa-share.extract-compiler.compiler-selector-icon(aria-label="Pop compiler" title="Show compiler")
button.btn.btn-sm.fas.fa-share.close.extract-compiler.compiler-selector-icon(aria-label="Pop compiler" title="Show compiler") button.btn.btn-sm.fas.fa-copy.copy-compiler.compiler-selector-icon(aria-label="Copy compiler" title="Copy compiler")
button.btn.btn-sm.fas.fa-copy.close.copy-compiler.compiler-selector-icon(aria-label="Copy compiler" title="Copy compiler")

View File

@@ -4,7 +4,7 @@
span span
b.lib-name &nbsp; b.lib-name &nbsp;
span.lib-version span.lib-version
select.custom-select.custom-select-sm.lib-version-select select.form-select.form-select-sm.lib-version-select
option - option -
.card-body .card-body
p.lib-description &nbsp; p.lib-description &nbsp;

View File

@@ -1,5 +1,4 @@
#libs-entry #libs-entry
.input-group.input-group-sm .input-group.input-group-sm
.input-group-prepend label.input-group-text
label.input-group-text select.form-select.form-select-sm
select.custom-select.custom-select-sm

View File

@@ -3,7 +3,7 @@
.card-header .card-header
span.override-name span.override-name
span.override span.override
select.custom-select.custom-select-sm select.form-select.form-select-sm
.card-body .card-body
p.override-description p.override-description
span.override-fav span.override-fav

View File

@@ -13,4 +13,4 @@
.runtime-tool-option .runtime-tool-option
span.tool-option-name span.tool-option-name
span.tool-option-select span.tool-option-select
select.tool-option-select.custom-select-sm select.tool-option-select.form-select-sm

View File

@@ -1,5 +1,5 @@
#tree-editor-tpl #tree-editor-tpl
li.list-group-item.tree-editor-file.input-group-append li.list-group-item.tree-editor-file
span.filename someresource.txt span.filename someresource.txt
button.btn.delete-file.fa.fa-trash(title="Remove this file" aria-label="Delete") button.btn.delete-file.fa.fa-trash(title="Remove this file" aria-label="Delete")
button.btn.rename-file.fa.fa-tag(title="Rename this file" aria-label="Rename") button.btn.rename-file.fa.fa-tag(title="Rename this file" aria-label="Rename")