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"?>
<project version="4">
<component name="ProjectTasksOptions" suppressed-tasks="Less;Babel;Pug/Jade" />
<component name="ProjectTasksOptions" suppressed-tasks="Less;Babel;Pug/Jade;SCSS" />
</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.
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
[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);
await page.goto(url);
//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 page.click("#simplecook .btn.btn-primary.btn-sm.cook-do-consent");
await page.evaluate(() => {

25
package-lock.json generated
View File

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

View File

@@ -31,7 +31,7 @@
"@sentry/node": "^7.120.3",
"@types/semver": "^7.7.0",
"big-integer": "^1.6.52",
"bootstrap": "^4.6.2",
"bootstrap": "^5.3.5",
"buffer": "^6.0.3",
"chart.js": "^4.4.9",
"clipboard": "^2.0.11",
@@ -57,7 +57,7 @@
"nopt": "^8.1.0",
"p-queue": "^8.1.0",
"path-browserify": "^1.0.1",
"popper.js": "^1.16.1",
"@popperjs/core": "^2.11.8",
"profanities": "^3.0.1",
"prom-client": "^15.1.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();
import 'whatwg-fetch';
import 'popper.js';
import '@popperjs/core';
import 'bootstrap';
import $ from 'jquery';
@@ -62,6 +62,7 @@ import {ComponentConfig, EmptyCompilerState, StateWithId, StateWithLanguage} fro
import {CompilerExplorerOptions} from './global.js';
import * as utils from '../shared/common-utils.js';
import * as BootstrapUtils from './bootstrap-utils.js';
import {ParseFiltersAndOutputOptions} from './features/filters.interfaces.js';
import {localStorage, sessionThenLocalStorage} from './local.js';
import {Printerinator} from './print-view.js';
@@ -79,7 +80,7 @@ if (!window.PRODUCTION && !options.embedded) {
//css
require('bootstrap/dist/css/bootstrap.min.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/explorer.scss');
@@ -225,7 +226,7 @@ function setupButtons(options: CompilerExplorerOptions, hub: Hub) {
window.location.reload();
});
$('#history').modal();
BootstrapUtils.showModal('#history');
});
$('#ui-apply-default-font-scale').on('click', () => {
@@ -530,7 +531,7 @@ function initShortlinkInfoButton() {
buttonText.html('');
const button = $('.shortlinkInfo');
button.popover({
BootstrapUtils.initPopover(button, {
html: true,
title: 'Link created',
content: formatISODate(dt, true),
@@ -677,11 +678,13 @@ function start() {
setupButtons(options, hub);
}
const addDropdown = $('#addDropdown');
function setupAdd<C>(thing: JQuery, func: () => ComponentConfig<C>) {
(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', () => {

View File

@@ -30,7 +30,7 @@ function ensureShownMessage(message: string, motdNode: JQuery) {
motdNode.find('.content').html(message);
motdNode.removeClass('d-none');
motdNode
.find('.close')
.find('.btn-close')
.on('click', () => {
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.
// See webpack.config.esm.ts -> entry for more details.
import $ from 'jquery';
import '@popperjs/core';
import 'bootstrap';
import 'popper.js';
import {Toggles} from './widgets/toggles.js';
import './styles/noscript.scss';

View File

@@ -26,6 +26,7 @@ import * as fileSaver from 'file-saver';
import $ from 'jquery';
import * as monaco from 'monaco-editor';
import _ from 'underscore';
import * as BootstrapUtils from '../bootstrap-utils.js';
import {Pane} from './pane.js';
import {Container} from 'golden-layout';
@@ -281,7 +282,9 @@ export class Cfg extends Pane<CfgState> {
if (this.tooltipOpen) {
if (!e.target.classList.contains('fold') && $(e.target).parents('.popover.in').length === 0) {
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>`;
}
div.innerHTML = lines.join('<br/>');
for (const fold of div.getElementsByClassName('fold')) {
$(fold)
.popover({
content: unwrap(fold.getAttribute('data-extra')),
html: true,
placement: 'top',
template:
'<div class="popover cfg-fold-popover" role="tooltip">' +
'<div class="arrow"></div>' +
'<h3 class="popover-header"></h3>' +
'<div class="popover-body"></div>' +
'</div>',
})
.on('show.bs.popover', () => {
this.tooltipOpen = true;
})
.on('hide.bs.popover', () => {
this.tooltipOpen = false;
});
for (const foldElement of div.getElementsByClassName('fold')) {
const fold = foldElement as HTMLElement;
BootstrapUtils.initPopover(fold, {
content: unwrap(fold.getAttribute('data-extra')),
html: true,
placement: 'top',
template:
'<div class="popover cfg-fold-popover" role="tooltip">' +
'<div class="arrow"></div>' +
'<h3 class="popover-header"></h3>' +
'<div class="popover-body"></div>' +
'</div>',
});
BootstrapUtils.setElementEventHandler(fold, 'show.bs.popover', () => {
this.tooltipOpen = true;
});
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.
// 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
// This function sets this.state.selectedFunction if the input is non-null and valid
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.svg.innerHTML = '';
this.estimatedPNGSize.innerHTML = '';
@@ -669,7 +678,9 @@ export class Cfg extends Pane<CfgState> {
const topBarHeight = utils.updateAndCalcTopBarHeight(this.domRoot, this.topBar, this.hideable);
this.graphContainer.style.width = `${unwrap(this.domRoot.width())}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 {ResultLine} from '../../types/resultline/resultline.interfaces.js';
import {getAssemblyDocumentation} from '../api/api.js';
import * as BootstrapUtils from '../bootstrap-utils.js';
import * as codeLensHandler from '../codelens-handler.js';
import * as colour from '../colour.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 hidePaneAdder = () => {
newPaneDropdown.dropdown('hide');
BootstrapUtils.hideDropdown(newPaneDropdown);
};
// 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)
// @ts-ignore
._dragListener.on('dragStart', () => popularArgumentsMenu.dropdown('hide'));
._dragListener.on('dragStart', () => BootstrapUtils.hideDropdown(popularArgumentsMenu));
this.flagsButton.on('click', () => {
const insertPoint =
@@ -1868,7 +1869,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
dismissTime: 10000,
onBeforeShow: elem => {
elem.find('#miracle_emulink').on('click', () => {
dialog.modal();
BootstrapUtils.showModal(dialog);
const miracleMenuFrame = dialog.find('#miracleemuframe')[0];
assert(miracleMenuFrame instanceof HTMLIFrameElement);
@@ -1896,7 +1897,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
dismissTime: 10000,
onBeforeShow: elem => {
elem.find('#jsspeccy_emulink').on('click', () => {
dialog.modal();
BootstrapUtils.showModal(dialog);
const speccyemuframe = dialog.find('#speccyemuframe')[0];
assert(speccyemuframe instanceof HTMLIFrameElement);
@@ -1923,7 +1924,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
dismissTime: 10000,
onBeforeShow: elem => {
elem.find('#emulink').on('click', () => {
dialog.modal();
BootstrapUtils.showModal(dialog);
const jsbeebemuframe = dialog.find('#jsbeebemuframe')[0];
assert(jsbeebemuframe instanceof HTMLIFrameElement);
@@ -1950,7 +1951,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
dismissTime: 10000,
onBeforeShow: elem => {
elem.find('#emulink').on('click', () => {
dialog.modal();
BootstrapUtils.showModal(dialog);
const jsnesemuframe = dialog.find('#jsnesemuframe')[0];
assert(jsnesemuframe instanceof HTMLIFrameElement);
@@ -2683,7 +2684,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co
initToolButtons(): void {
this.toolsMenu = this.domRoot.find('.new-tool-dropdown');
const hideToolDropdown = () => {
this.toolsMenu?.dropdown('hide');
if (this.toolsMenu) BootstrapUtils.hideDropdown(this.toolsMenu);
};
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 &&
target.closest('.popover').length === 0
)
this.prependOptions.popover('hide');
BootstrapUtils.hidePopover(this.prependOptions);
if (
!target.is(this.fullCompilerName) &&
this.fullCompilerName.has(target as unknown as Element).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.
$(document).on('keyup.editable', e => {
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 &&
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 {
const infoLine =
'<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:
warnings.map(w => `<div class="compiler-arg-warning">${w}</div>`).join('\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 {CompilerInfo} from '../../types/compiler.interfaces.js';
import {unwrapString} from '../assert.js';
import * as BootstrapUtils from '../bootstrap-utils.js';
import {CompilationStatus} from '../compiler-service.interfaces.js';
import {CompilerService} from '../compiler-service.js';
import * as Components from '../components.js';
@@ -100,7 +101,7 @@ export class Conformance extends Pane<ConformanceViewState> {
// Dismiss the popover on escape.
$(document).on('keyup.editable', e => {
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 &&
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.selectorList = this.domRoot.find('.compiler-list');
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.libsButton = this.topBar.find('.show-libs');
this.hideable = this.domRoot.find('.hideable');
@@ -225,15 +226,11 @@ export class Conformance extends Pane<ConformanceViewState> {
.on('change', onOptionsChange)
.on('keyup', onOptionsChange);
newSelector
.find('.close')
.not('.extract-compiler')
.not('.copy-compiler')
.on('click', () => {
this.removeCompilerPicker(newCompilerEntry);
});
newSelector.find('.close-compiler').on('click', () => {
this.removeCompilerPicker(newCompilerEntry);
});
newSelector.find('.close.copy-compiler').on('click', () => {
newSelector.find('.copy-compiler').on('click', () => {
const config: AddCompilerPickerConfig = {
compilerId: newCompilerEntry.picker?.lastCompilerId ?? '',
options: newCompilerEntry.optionsField?.val() || '',
@@ -301,15 +298,19 @@ export class Conformance extends Pane<ConformanceViewState> {
): void {}
setCompilationOptionsPopover(element: JQuery<HTMLElement> | null, content: string): void {
element?.popover('dispose');
element?.popover({
content: content || 'No options in use',
template:
'<div class="popover' +
(content ? ' compiler-options-popover' : '') +
'" role="tooltip"><div class="arrow"></div>' +
'<h3 class="popover-header"></h3><div class="popover-body"></div></div>',
});
if (element) {
const existingPopover = BootstrapUtils.getPopoverInstance(element);
if (existingPopover) existingPopover.dispose();
BootstrapUtils.initPopover(element, {
content: content || 'No options in use',
template:
'<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 {

View File

@@ -482,10 +482,7 @@ export class Diff extends MonacoPane<monaco.editor.IStandaloneDiffEditor, DiffSt
) {
if (!compiler) return;
options = options || '';
let 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) + '...';
const name = compiler.name + ' ' + options;
this.compilers[id] = {
id: id,
name: name,

View File

@@ -29,6 +29,7 @@ import * as monaco from 'monaco-editor';
import * as monacoVim from 'monaco-vim';
import TomSelect from 'tom-select';
import _ from 'underscore';
import * as BootstrapUtils from '../bootstrap-utils.js';
import * as colour from '../colour.js';
import * as Components from '../components.js';
import * as monacoConfig from '../monaco-config.js';
@@ -472,7 +473,7 @@ export class Editor extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Edit
}
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);
this.vimMode = vimMode;
this.vimFlag.prop('class', 'btn btn-info');
@@ -481,7 +482,7 @@ export class Editor extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Edit
disableVim(): void {
this.vimMode.dispose();
this.domRoot.find('#v-status').html('');
this.domRoot.find('.v-status').html('');
this.vimFlag.prop('class', 'btn btn-light');
(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.conformanceViewerButton = this.domRoot.find('.btn.conformance');
const addEditorButton = this.domRoot.find('.btn.add-editor');
const toggleVimButton = this.domRoot.find('#vim-flag');
this.vimFlag = this.domRoot.find('#vim-flag');
const toggleVimButton = this.domRoot.find('.vim-flag');
this.vimFlag = this.domRoot.find('.vim-flag');
toggleVimButton.on('click', () => {
if ((this.editor as any).vimInUse) {
this.disableVim();
@@ -541,7 +542,7 @@ export class Editor extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Edit
});
this.languageInfoButton = this.domRoot.find('.language-info');
this.languageInfoButton.popover({});
BootstrapUtils.initPopover(this.languageInfoButton);
this.languageBtn = this.domRoot.find('.change-language');
const changeLanguageButton = this.languageBtn[0];
assert(changeLanguageButton instanceof HTMLSelectElement);
@@ -598,7 +599,10 @@ export class Editor extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Edit
.createDragSource(dragSource, dragConfig)
// @ts-expect-error: createDragSource returns not void
._dragListener.on('dragStart', () => {
paneAdderDropdown.dropdown('toggle');
const dropdown = BootstrapUtils.getDropdownInstance(paneAdderDropdown);
if (dropdown) {
dropdown.toggle();
}
});
dragSource.on('click', () => {
@@ -1921,7 +1925,7 @@ export class Editor extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Edit
): string {
let result =
'<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="' +
(data.logoData ? data.logoData : '') +
'" 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 {}
updateLanguageTooltip() {
this.languageInfoButton.popover('dispose');
// Dispose existing popover instance
const existingPopover = BootstrapUtils.getPopoverInstance(this.languageInfoButton);
if (existingPopover) existingPopover.dispose();
if (this.currentLanguage?.tooltip) {
this.languageInfoButton.popover({
BootstrapUtils.initPopover(this.languageInfoButton, {
title: 'More info about this language',
content: this.currentLanguage.tooltip,
container: 'body',

View File

@@ -38,6 +38,7 @@ import {Language} from '../../types/languages.interfaces.js';
import {ResultLine} from '../../types/resultline/resultline.interfaces.js';
import {Artifact, ArtifactType} from '../../types/tool.interfaces.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 {CompilerService} from '../compiler-service.js';
import {ICompilerShared} from '../compiler-shared.interfaces.js';
@@ -799,15 +800,19 @@ export class Executor extends Pane<ExecutorState> {
!target.is(this.prependOptions) &&
this.prependOptions.has(target as any).length === 0 &&
target.closest('.popover').length === 0
)
this.prependOptions.popover('hide');
) {
const popover = BootstrapUtils.getPopoverInstance(this.prependOptions);
if (popover) popover.hide();
}
if (
!target.is(this.fullCompilerName) &&
this.fullCompilerName.has(target as any).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);
@@ -963,7 +968,8 @@ export class Executor extends Pane<ExecutorState> {
// Dismiss the popover on escape.
$(document).on('keyup.editable', e => {
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 target = $(e.target);
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) {
this.prependOptions.popover('dispose');
this.prependOptions.popover({
// Dispose of existing popover
const existingPopover = BootstrapUtils.getPopoverInstance(this.prependOptions);
if (existingPopover) existingPopover.dispose();
// Initialize new popover
BootstrapUtils.initPopover(this.prependOptions, {
content: content || 'No options in use',
template:
'<div class="popover' +

View File

@@ -29,6 +29,7 @@ import TomSelect from 'tom-select';
import _ from 'underscore';
import {escapeHTML} from '../../shared/common-utils.js';
import {assert, unwrap, unwrapString} from '../assert.js';
import * as BootstrapUtils from '../bootstrap-utils.js';
import * as Components from '../components.js';
import {EventHub} from '../event-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(
'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');
}
});
$('#true-dark .content .close').on('click', e => {
$('#true-dark .content .dark-close').on('click', e => {
local.localStorage.set(localKey, 'hidden');
toggleButton();
toggleOverlay();

View File

@@ -22,18 +22,20 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import {Modal, Tooltip} from 'bootstrap';
import ClipboardJS from 'clipboard';
import GoldenLayout from 'golden-layout';
import $ from 'jquery';
import _ from 'underscore';
import {unwrap} from './assert.js';
import * as BootstrapUtils from './bootstrap-utils.js';
import {sessionThenLocalStorage} from './local.js';
import {options} from './options.js';
import * as url from './url.js';
import ClickEvent = JQuery.ClickEvent;
import TriggeredEvent = JQuery.TriggeredEvent;
import {SentryCapture} from './sentry.js';
import {Settings, SiteSettings} from './settings.js';
import ClickEvent = JQuery.ClickEvent;
const cloneDeep = require('lodash.clonedeep');
@@ -87,24 +89,31 @@ export class Sharing {
private layout: GoldenLayout;
private lastState: any;
private share: JQuery;
private shareShort: JQuery;
private shareFull: JQuery;
private shareEmbed: JQuery;
private readonly share: JQuery;
private readonly shareTooltipTarget: JQuery;
private readonly shareShort: JQuery;
private readonly shareFull: JQuery;
private readonly shareEmbed: JQuery;
private settings: SiteSettings;
private clippyButton: ClipboardJS | null;
private readonly shareLinkDialog: HTMLElement;
constructor(layout: any) {
this.layout = layout;
this.lastState = null;
this.shareLinkDialog = unwrap(document.getElementById('sharelinkdialog'), 'Share modal element not found');
this.share = $('#share');
this.shareTooltipTarget = $('#share-tooltip-target');
this.shareShort = $('#shareShort');
this.shareFull = $('#shareFull');
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.clippyButton = null;
@@ -122,9 +131,9 @@ export class Sharing {
});
this.layout.on('stateChanged', this.onStateChanged.bind(this));
$('#sharelinkdialog')
.on('show.bs.modal', this.onOpenModalPane.bind(this))
.on('hidden.bs.modal', this.onCloseModalPane.bind(this));
BootstrapUtils.initModal(this.shareLinkDialog);
this.shareLinkDialog.addEventListener('show.bs.modal', this.onOpenModalPane.bind(this));
this.shareLinkDialog.addEventListener('hidden.bs.modal', this.onCloseModalPane.bind(this));
this.layout.eventHub.on('settingsChange', (newSettings: SiteSettings) => {
this.settings = newSettings;
@@ -176,11 +185,16 @@ export class Sharing {
}
}
private onOpenModalPane(event: TriggeredEvent<HTMLElement, undefined, HTMLElement, HTMLElement>): void {
// @ts-ignore The property is added by bootstrap
const button = $(event.relatedTarget);
const currentBind = Sharing.bindToLinkType(button.data('bind'));
const modal = $(event.currentTarget);
private onOpenModalPane(event: Event): void {
const modalEvent = event as Modal.Event;
if (!modalEvent.relatedTarget) {
throw new Error('No relatedTarget found in modal event');
}
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 permalink = modal.find('.permalink');
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.
// 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()) {
event.stopPropagation();
this.copyLinkTypeToClipboard(type);
// As we prevented bubbling, the dropdown won't close by itself. We need to trigger it manually
this.share.dropdown('hide');
event.stopPropagation();
// As we prevented bubbling, the dropdown won't close by itself.
BootstrapUtils.hideDropdown(this.share);
}
return false;
}
private getLinkOfType(type: LinkType): Promise<string> {
@@ -278,7 +293,7 @@ export class Sharing {
return new Promise<string>((resolve, reject) => {
Sharing.getLinks(config, type, (error: any, newUrl: string, extra: string, updateState: boolean) => {
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');
reject();
} else {
@@ -295,7 +310,7 @@ export class Sharing {
const config = this.layout.toConfig();
Sharing.getLinks(config, type, (error: any, newUrl: string, extra: string, updateState: boolean) => {
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');
} else {
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 {
where.tooltip('dispose');
where.tooltip({
placement: 'bottom',
trigger: 'manual',
title: message,
});
where.tooltip('show');
// Manual triggering of tooltips does not hide them automatically. This timeout ensures they do
setTimeout(() => where.tooltip('hide'), 1500);
// First dispose any existing tooltip
const tooltipEl = where[0];
if (!tooltipEl) return;
const existingTooltip = Tooltip.getInstance(tooltipEl);
if (existingTooltip) {
existingTooltip.dispose();
}
// 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 {
@@ -336,7 +368,7 @@ export class Sharing {
if (Sharing.isNavigatorClipboardAvailable()) {
navigator.clipboard
.writeText(link)
.then(() => this.displayTooltip(this.share, 'Link copied to clipboard'))
.then(() => this.displayTooltip(this.shareTooltipTarget, 'Link copied to clipboard'))
.catch(() => this.openShareModalForType(type));
} else {
this.openShareModalForType(type);
@@ -397,7 +429,7 @@ export class Sharing {
): string {
const embedUrl = Sharing.getEmbeddedUrl(config, root, isReadOnly, extraOptions);
// 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 {
@@ -455,7 +487,7 @@ export class Sharing {
const newElement = baseTemplate.children('a.share-item').clone();
if (service.logoClass) {
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) {

View File

@@ -36,6 +36,11 @@ body {
padding: 0;
}
// Fix for Bootstrap 5 navbar
.navbar .container-fluid {
padding-left: 0;
}
.navbar-brand img.logo-overlay {
position: absolute;
top: 0;
@@ -75,13 +80,23 @@ body {
.compiler-picker {
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 {
text-align: center;
border-left: none !important;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-left: none !important; /* Fallback for older browsers */
border-inline-start: none !important; /* Logical property for RTL support */
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 {
@@ -294,6 +309,12 @@ pre.content.wrap * {
}
}
#site-template-loader {
.modal-dialog {
min-width: 800px; /* ensure template modal has adequate width */
}
}
.modal-body {
min-height: 200px;
overflow-y: auto; /* make body scrollable -> keep Header & Footer onscreen, if possible */
@@ -377,7 +398,7 @@ pre.content.wrap * {
max-width: 100% !important;
}
.toast-header .close {
.toast-header .btn-close {
float: left;
margin-right: 5px;
}
@@ -387,17 +408,25 @@ pre.content.wrap * {
}
.font-size-list {
min-width: 43px !important;
max-height: 70% !important;
overflow-y: scroll;
min-width: 60px !important;
max-height: 70vh !important;
overflow-y: auto;
width: auto;
// For my fellow Firefox users
scrollbar-width: thin;
padding: 0.25rem 0;
}
.font-size-list button {
margin-left: 0 !important;
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 {
@@ -893,17 +922,18 @@ div.populararguments div.dropdown-menu {
}
.corporate .ces {
font-size: 165%;
height: 9em;
.btn {
--bs-btn-font-size: 24px;
}
height: 20em;
}
.legendary .ces {
font-size: 165%;
height: 7em;
}
.legendary .ces button {
padding: 1em;
.btn {
--bs-btn-font-size: 24px;
padding: 1em;
}
height: 20em;
}
.ces-logo {
@@ -1115,7 +1145,7 @@ div.populararguments div.dropdown-menu {
margin-right: 2pt;
}
span.badge.badge-pill {
span.badge.rounded-pill {
margin-left: 2pt;
}
@@ -1185,11 +1215,42 @@ html[data-theme='one-dark'] {
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 {
padding-top: 0;
}
.conformance-wrapper .compiler-list .form-row {
.conformance-wrapper .compiler-list .row {
padding-top: 3px;
}
@@ -1229,8 +1290,10 @@ html[data-theme='one-dark'] {
.prepend-options,
.picker-popout-button {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-top-right-radius: 0; /* Fallback for older browsers */
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 {
@@ -1257,7 +1320,9 @@ html[data-theme='one-dark'] {
}
.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 {
@@ -1315,9 +1380,14 @@ html[data-theme='one-dark'] {
> div {
padding: 0;
flex-shrink: 0;
&.site-templates-list-col {
min-width: 150px;
width: 20%;
}
&.site-template-preview-col {
flex-grow: 1;
flex-shrink: 1;
min-width: 400px;
img {
width: 1000px;
aspect-ratio: 1000 / 800;
@@ -1579,11 +1649,11 @@ html[data-theme='one-dark'] {
}
}
.close {
.dark-close {
position: absolute;
bottom: 0px;
bottom: 0;
right: -12px;
//background: #e787e7;
padding: 0;
background: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, #1a1a1a 48%, #1a1a1a 100%);
width: 24px;
height: 24px;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -30,6 +30,7 @@ import {
EnvVarOverrides,
} from '../../types/compilation/compiler-overrides.interfaces.js';
import {assert, unwrap} from '../assert.js';
import * as BootstrapUtils from '../bootstrap-utils.js';
import {CompilerInfo} from '../compiler.interfaces.js';
import {localStorage} from '../local.js';
import {options} from '../options.js';
@@ -160,8 +161,8 @@ export class CompilerOverridesWidget {
btn.addClass('active');
} else if (state instanceof IncompatibleState) {
btn.prop('disabled', true);
btn.prop('data-toggle', 'tooltip');
btn.prop('data-placement', 'top');
btn.prop('data-bs-toggle', 'tooltip');
btn.prop('data-bs-placement', 'top');
btn.prop('title', state.reason);
}
div.data('ov-name', fave.name);
@@ -416,9 +417,8 @@ export class CompilerOverridesWidget {
const lastOverrides = JSON.stringify(this.configured);
const popup = this.popupDomRoot.modal();
// 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();
const newOverrides = JSON.stringify(this.configured);
@@ -428,5 +428,7 @@ export class CompilerOverridesWidget {
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 {CompilerInfo} from '../../types/compiler.interfaces.js';
import {unwrap, unwrapString} from '../assert.js';
import * as BootstrapUtils from '../bootstrap-utils.js';
import {CompilerService} from '../compiler-service.js';
import {highlight} from '../highlight.js';
import {CompilerPicker} from './compiler-picker.js';
@@ -64,7 +65,7 @@ export class CompilerPickerPopup {
this.categoryFilters = [];
this.searchBar.val('');
this.modal.on('shown.bs.modal', () => {
BootstrapUtils.setElementEventHandler(this.modal, 'shown.bs.modal', () => {
this.searchBar[0].focus();
});
}
@@ -186,7 +187,7 @@ export class CompilerPickerPopup {
`
<div class="compiler d-flex" data-value="${compiler.id}">
<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>
</div>
</div>
@@ -264,10 +265,10 @@ export class CompilerPickerPopup {
show() {
this.searchBar.trigger('input');
this.fillCompilers();
this.modal.modal({});
BootstrapUtils.showModal(this.modal);
}
hide() {
this.modal.modal('hide');
BootstrapUtils.hideModal(this.modal);
}
}

View File

@@ -135,7 +135,7 @@ export class CompilerPicker {
'<div class="d-flex"><div>' +
escapeHtml(data.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>' +
@@ -143,6 +143,7 @@ export class CompilerPicker {
'</div>'
);
},
item: (data, escapeHtml) => `<div title="${escapeHtml(data.name)}">${escapeHtml(data.name)}</div>`,
},
});

View File

@@ -24,6 +24,7 @@
import $ from 'jquery';
import {escapeHTML} from '../../shared/common-utils.js';
import * as BootstrapUtils from '../bootstrap-utils.js';
import {options} from '../options.js';
export type CompilerVersionInfo = {version: string; fullVersion?: string};
@@ -78,14 +79,19 @@ function reallySetCompilerVersionPopover(
.on('click', () => {
versionContent.toggle();
hiddenVersionText.toggle();
pane.fullCompilerName.popover('update');
const popover = BootstrapUtils.getPopoverInstance(pane.fullCompilerName);
if (popover) popover.update();
});
hiddenSection.append(hiddenVersionText).append(clickToExpandContent);
bodyContent.append(hiddenSection);
}
pane.fullCompilerName.popover('dispose');
pane.fullCompilerName.popover({
// Dispose of existing popover
const existingPopover = BootstrapUtils.getPopoverInstance(pane.fullCompilerName);
if (existingPopover) existingPopover.dispose();
// Initialize new popover
BootstrapUtils.initPopover(pane.fullCompilerName, {
html: true,
title: notification
? ($.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++) {
const item = $('<button></button>');
item.attr('data-value', i)
.addClass('dropdown-item btn btn-sm btn-light')
.text(i)
.appendTo(elem)
.on('click', onClickEvent);
item.attr('data-value', i).addClass('dropdown-item').text(i).appendTo(elem).on('click', onClickEvent);
if (obj.scale === i) {
item.addClass('active');

View File

@@ -25,9 +25,10 @@
import $ from 'jquery';
import {editor} from 'monaco-editor';
import {pluck} from 'underscore';
import {unwrap} from '../assert.js';
import * as BootstrapUtils from '../bootstrap-utils.js';
import {EditorSource, HistoryEntry, sortedList} from '../history.js';
import ITextModel = editor.ITextModel;
import {unwrap} from '../assert.js';
type Entry = {dt: number; name: string; load: () => void};
@@ -72,7 +73,9 @@ export class HistoryWidget {
name: `${dt.replace(/\s\(.*\)/, '')} (${languages})`,
load: () => {
this.onLoad(data);
this.modal?.modal('hide');
if (this.modal) {
BootstrapUtils.hideModal(this.modal);
}
},
};
}),
@@ -140,7 +143,8 @@ export class HistoryWidget {
this.onLoadCallback = onLoad;
// 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());
unwrap(this.modal).modal();
const modalElement = unwrap(this.modal)[0];
modalElement.addEventListener('shown.bs.modal', () => this.resizeLayout());
BootstrapUtils.showModal(modalElement);
}
}

View File

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

View File

@@ -29,6 +29,7 @@ import {escapeHTML} from '../../shared/common-utils.js';
import {Language} from '../../types/languages.interfaces.js';
import {SourceApiEntry} from '../../types/source.interfaces.js';
import {unwrap, unwrapString} from '../assert.js';
import * as BootstrapUtils from '../bootstrap-utils.js';
import {HistorySource} from '../history.js';
import {localStorage} from '../local.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,
response => this.onLoad(response.file),
);
this.modal?.modal('hide');
if (this.modal) {
BootstrapUtils.hideModal(this.modal);
}
}
private static populate(root: JQuery, list: PopulateItem[]) {
@@ -143,7 +146,9 @@ export class LoadSave {
name: name,
load: () => {
this.onLoad(data);
this.modal?.modal('hide');
if (this.modal) {
BootstrapUtils.hideModal(this.modal);
}
},
delete: () => {
this.alertSystem.ask(
@@ -183,7 +188,9 @@ export class LoadSave {
name: dt.replace(/\s\(.*\)/, ''),
load: () => {
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);
}
this.modal?.modal('hide');
if (this.modal) {
BootstrapUtils.hideModal(this.modal);
}
}
public run(onLoad, editorText, currentLanguage: Language) {
@@ -224,7 +233,11 @@ export class LoadSave {
this.populateLocalHistory();
this.onLoadCallback = onLoad;
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() {
@@ -238,7 +251,9 @@ export class LoadSave {
LoadSave.setLocalFile(name, this.editorText);
};
if (name in LoadSave.getLocalFiles()) {
this.modal?.modal('hide');
if (this.modal) {
BootstrapUtils.hideModal(this.modal);
}
this.alertSystem.ask(
'Replace current?',
`Do you want to replace the existing saved file '${escapeHTML(name)}'?`,
@@ -246,7 +261,9 @@ export class LoadSave {
);
} else {
doneCallback();
this.modal?.modal('hide');
if (this.modal) {
BootstrapUtils.hideModal(this.modal);
}
}
}

View File

@@ -32,6 +32,7 @@ import {
RuntimeToolType,
} from '../../types/execution/execution.interfaces.js';
import {assert} from '../assert.js';
import * as BootstrapUtils from '../bootstrap-utils.js';
import {CompilerInfo} from '../compiler.interfaces.js';
import {localStorage} from '../local.js';
import {options} from '../options.js';
@@ -392,9 +393,8 @@ export class RuntimeToolsWidget {
const lastOverrides = JSON.stringify(this.configured);
const popup = this.popupDomRoot.modal();
// 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();
const newOverrides = JSON.stringify(this.configured);
@@ -404,5 +404,7 @@ export class RuntimeToolsWidget {
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 {SiteTemplateResponse, UserSiteTemplate} from '../../types/features/site-templates.interfaces.js';
import {assert, unwrap, unwrapString} from '../assert.js';
import * as BootstrapUtils from '../bootstrap-utils.js';
import {localStorage} from '../local.js';
import {Settings} from '../settings.js';
import * as url from '../url.js';
@@ -174,7 +175,7 @@ class SiteTemplatesWidget {
this.populated = true;
}
show() {
this.modal.modal('show');
BootstrapUtils.showModal(this.modal);
if (!this.populated) {
this.populate();
}

View File

@@ -24,11 +24,12 @@
import {Chart, ChartData, defaults} from 'chart.js';
import $ from 'jquery';
import {Settings} from '../settings.js';
import 'chart.js/auto';
import {isString} from '../../shared/common-utils.js';
import {CompilationResult} from '../../types/compilation/compilation.interfaces.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};
@@ -188,7 +189,7 @@ function displayData(data: Data) {
},
},
});
modal.modal('show');
BootstrapUtils.showModal(modal);
}
export function displayCompilationTiming(compileResult: CompilationResult | null, totalTime: number) {

View File

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

View File

@@ -1,4 +1,4 @@
.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]
.dropdown-menu.font-size-list(aria-labelledby="fs-button")

View File

@@ -2,111 +2,116 @@ extends _layout.pug
block prepend content
nav.navbar.navbar-godbolt.navbar-expand-md.navbar-light.bg-light
include logo.pug
button.navbar-toggler(type="button" data-toggle="collapse" data-target="#navbarContent" aria-controls="navbarContent" aria-expanded="false" aria-label="Toggle navigation")
span.navbar-toggler-icon
.collapse.navbar-collapse#navbarContent
ul.navbar-nav.navbar-left.mr-auto
li.nav-item.dropdown
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
.container-fluid
include logo.pug
button.navbar-toggler(type="button" data-bs-toggle="collapse" data-bs-target="#navbarContent" aria-controls="navbarContent" aria-expanded="false" aria-label="Toggle navigation")
span.navbar-toggler-icon
.collapse.navbar-collapse#navbarContent
ul.navbar-nav.me-auto.mb-2.mb-md-0
li.nav-item.dropdown
button.btn.btn-light.nav-link.dropdown-toggle#policiesDropdown(role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false")
| Policies
span#policyBellNotification.d-none.badge.badge-pill.badge-primary
span.fa.fa-bell
div.dropdown-menu.dropdown-menu-right(aria-labelledby="policiesDropdown")
include menu-policies.pug
li.nav-item.dropdown
button.btn.btn-light.nav-link.dropdown-toggle#otherDropdown(role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false") Other
div.dropdown-menu.dropdown-menu-right(aria-labelledby="otherDropdown")
include menu-other.pug
button.btn.btn-light.nav-link.dropdown-toggle#addDropdown(role="button" data-bs-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-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
include popups/_all
#real-dark
#true-dark
.content Try the real dark theme 🔦
.close
i.fa-solid.fa-xmark
button.btn.dark-close.fas.fa-times(type="button" aria-label="Close")

View File

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

View File

@@ -6,7 +6,7 @@ block content
input#filterAnsi(name='filterAnsi' value='true' type='hidden')
.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
| Output
@@ -14,7 +14,7 @@ block content
include ../options-output.pug
.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
| Filters

View File

@@ -1,7 +1,7 @@
block footer
nav.bg-light
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")
include ../menu-other.pug

View File

@@ -1,11 +1,11 @@
.header
nav.navbar.navbar-godbolt.navbar-expand-md.navbar-light.bg-light
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
| Thanks for using Compiler Explorer
.navbar-nav.navbar-right
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
include ../sponsor-icons.pug

View File

@@ -1,7 +1,7 @@
block header
nav.bg-light
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")
each language in languages

View File

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

View File

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

View File

@@ -3,9 +3,7 @@
.modal-content
.modal-header
h5.modal-title History
button.close(type="button" data-dismiss="modal" aria-hidden="true" aria-label="Close")
span(aria-hidden="true")
| &times;
button.btn-close(type="button" data-bs-dismiss="modal" aria-hidden="true" aria-label="Close")
.modal-body
.card
.card-body
@@ -18,4 +16,4 @@
div.src-content#load-history-src-display(style="height:600px;overflow-y:scroll;flex:2")
div.monaco-placeholder
.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-header
h5.modal-title Libraries
button.close(type="button" data-dismiss="modal" aria-hidden="true" aria-label="Close")
span(aria-hidden="true")
| &times;
button.btn-close(type="button" data-bs-dismiss="modal" aria-hidden="true" aria-label="Close")
.modal-body
.card
.card-body
@@ -13,8 +11,9 @@
.row
.col-lg
div(style='text-align: center')
input.lib-search-input(value="" placeholder="Search text")
button.btn.btn-primary.lib-search-button(type="button") Search
.input-group.mb-3
input.form-control.lib-search-input(value="" placeholder="Search text")
button.btn.btn-primary.lib-search-button(type="button") Search
hr
.row
.col-lg.libs-how-to-use

View File

@@ -3,17 +3,15 @@
.modal-content(style="max-width: unset")
.modal-header
h5.modal-title Load and save editor text
button.close(type="button" data-dismiss="modal" aria-hidden="true" aria-label="Close")
span(aria-hidden="true")
| &times;
button.btn-close(type="button" data-bs-dismiss="modal" aria-label="Close")
.modal-body
.card
.card-header
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(href="#load-browser-local" role="tab" data-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-file-system" role="tab" data-toggle="tab") File system
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-bs-toggle="tab") Browser-local storage
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-bs-toggle="tab") File system
.card-body.tab-content
#load-examples.tab-pane.active(role="tabpanel")
h4.card-title Load from examples:
@@ -23,26 +21,26 @@
h4.card-title Load from browser-local storage:
ul.local-storage.small-v-scrollable
li.template
a.mr-3(href="javascript:;")
button.overwrite.btn.btn-sm.btn-light.mr-1
span.fa.fa-edit.mr-1
a.me-3(href="javascript:;")
button.overwrite.btn.btn-sm.btn-light.me-1
span.fa.fa-edit.me-1
| Overwrite
button.delete.btn.btn-sm.btn-light
span.fa.fa-trash.mr-1
span.fa.fa-trash.me-1
| Delete
.input-group
input.save-name(type="text" size="20" placeholder="Set name of state")
.input-group-append
button.btn.btn-light.save-button(type="button") Save to browser-local storage
.input-group.mb-3
input.form-control.save-name(type="text" placeholder="Set name of state")
button.btn.btn-light.save-button(type="button") Save to browser-local storage
#load-browser-local-history.tab-pane(role="tabpanel")
h4.card-title Load from browser-local history:
ul.local-history.small-v-scrollable
li.template: a(href="javascript:;")
#load-file-system.tab-pane(role="tabpanel")
h4.card-title Load/save to your system
label.btn.btn-outline-secondary.btn-file(role="button") Load from a local file
input.local-file(type="file")
label.btn.btn-outline-secondary.btn-file.save-btn(role="button") Save to file
input.save-file(type="button")
.mb-3
label.form-label Load from a local file
input.form-control.local-file(type="file")
.mb-3
button.btn.btn-outline-secondary.save-btn(type="button") Save to file
.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-header
h5.modal-title Compiler overrides
button.close(type="button" data-dismiss="modal" aria-hidden="true" aria-label="Close")
span(aria-hidden="true")
| &times;
button.btn-close(type="button" data-bs-dismiss="modal" aria-hidden="true" aria-label="Close")
.modal-body
.card
.card-body
@@ -25,8 +23,8 @@
span.override-name Compiler environment variables
.card-body
span.description One environment variable per line, KEY=VALUE, that will be set during compilation.
span.custom-override
textarea.envvars(cols="30")
span
textarea.form-control.envvars(cols="30", rows="3")
.possible-overrides.items
.overrides-favorites-col.col-md
h6 Favorites

View File

@@ -3,9 +3,7 @@
.modal-content
.modal-header
h5.modal-title Rename Pane
button.close(type="button" data-dismiss="modal" aria-hidden="true" aria-label="Close")
span(aria-hidden="true")
| &times;
button.btn-close(type="button" data-bs-dismiss="modal" aria-hidden="true" aria-label="Close")
.modal-body
.card
.card-body
@@ -13,5 +11,5 @@
.input-group
input#renamepaneinput.form-control.panename(type="text" placeholder="New pane name" size="1024")
.modal-footer
button.btn.btn-outline-secondary(type="button" data-dismiss="modal") Close
button#renamepanesubmit.btn.btn-outline-primary(type="submit" data-dismiss="modal" value="Save Changes") Save Changes
button.btn.btn-outline-secondary(type="button" data-bs-dismiss="modal") Close
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-header
h5.modal-title Runtime tools
button.close(type="button" data-dismiss="modal" aria-hidden="true" aria-label="Close")
span(aria-hidden="true")
| &times;
button.btn-close(type="button" data-bs-dismiss="modal" aria-hidden="true" aria-label="Close")
.modal-body
.card
.card-body
@@ -23,8 +21,8 @@
span.runtimetool-name Runtime environment variables
.card-body
span.description One environment variable per line, KEY=VALUE.
span.custom-runtimetool
textarea.envvars(cols="30")
span
textarea.form-control.envvars(cols="30", rows="3")
.possible-runtimetools.items
.runtimetools-favorites-col.col-md
h6 Favorites

View File

@@ -1,18 +1,17 @@
mixin checkbox(cls, text)
.checkbox
label
input(class=cls type="checkbox")
!= text
.form-check
input.form-check-input(class=cls type="checkbox" id="settings-checkbox-"+cls)
label.form-check-label(for="settings-checkbox-"+cls)!= text
mixin select(cls, text)
div
label!= text
select(class=cls)
.mb-3
label.form-label(for="settings-select-"+cls)!= text
select.form-select(class=cls id="settings-select-"+cls)
mixin input(cls, type, text, style)
div
label!= text
input(class=cls type=type style=style)
.mb-3
label.form-label(for="settings-input-"+cls)!= text
input.form-control(class=cls type=type style=style id="settings-input-"+cls)
#settings.modal.fade.gl_keep(tabindex="-1" role="dialog")
@@ -21,8 +20,7 @@ mixin input(cls, type, text, style)
.modal-header
h5.modal-title
| Compiler Explorer Settings
button.close(type="button" data-dismiss="modal" aria-label="Close")
span(aria-hidden="true") &times;
button.btn-close(type="button" data-bs-dismiss="modal" aria-label="Close")
.modal-body
.card
.card-header
@@ -30,53 +28,54 @@ mixin input(cls, type, text, style)
| preserved as part of shared URLs, and are persisted locally using browser
| local storage.
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(href="#site-behaviour" role="tab" data-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="#editor" role="tab" data-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.active(href="#colouring" role="tab" data-bs-toggle="tab") Colouring
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-bs-toggle="tab") Keybindings
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-bs-toggle="tab") Compilation
.card-body.tab-content
#colouring.tab-pane.active.form-group(role="group")
#colouring.tab-pane.active(role="group")
+select("theme", "Site theme")
+checkbox("colourise", "Colourise lines to show how the source maps to the output")
+checkbox("alwaysEnableAllSchemes", "Make all colour schemes available regardless of theme")
+select("colourScheme", "Line highlighting colour scheme")
#site-behaviour.tab-pane.form-group(role="group")
label
| Default language
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)")
select.defaultLanguage
#site-behaviour.tab-pane(role="group")
.mb-3
label.form-label(for="settings-select-defaultLanguage") Default language
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)")
select.form-select.defaultLanguage(id="settings-select-defaultLanguage")
+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("newEditorLastLang", "Use last selected language when opening new Editors")
+checkbox("enableCommunityAds", "Show community events")
#keybindings.tab-pane.form-group(role="group")
#keybindings.tab-pane(role="group")
+checkbox("useVim", "Vim editor mode")
.the-save-option-to-auto-share
label
kbd Ctrl
| +
kbd S
| &nbsp;behaviour
select.enableCtrlS
.checkbox.the-save-option-to-tree-save
label
input.enableCtrlStree(type="checkbox")
mb-3
label.form-label(for="settings-checkbox-enableCtrlS")
kbd Ctrl
| +
kbd S
| &nbsp;behaviour
select.form-select.enableCtrlS(id="settings-checkbox-enableCtrlS")
.form-check.the-save-option-to-tree-save
input.form-check-input.enableCtrlStree(type="checkbox" id="settings-checkbox-enableCtrlStree")
label.form-check-label(for="settings-checkbox-enableCtrlStree")
| Make
kbd Ctrl
| +
kbd S
| &nbsp;include and save the file to a Tree if that's added to the UI
.checkbox.the-popup-dialog-box-option
label
input.enableSharingPopover(type="checkbox")
.form-check.the-popup-dialog-box-option
input.form-check-input.enableSharingPopover(type="checkbox" id="settings-checkbox-enableSharingPopover")
label.form-check-label(for="settings-checkbox-enableSharingPopover")
| Pop up a dialog box when
kbd Ctrl
| +
kbd S
| &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%")
+select("defaultFontScale", "Default font scale")
+checkbox("editorsFLigatures", "Enable font ligatures")
@@ -97,19 +96,20 @@ mixin input(cls, type, text, style)
+checkbox("wordWrap", "Enable Word Wrapping")
+checkbox("enableCodeLens", "Enable CodeLens features (requires refresh to take effect)")
+checkbox("colouriseBrackets", "Colourise matching bracket pairs")
#compilation.tab-pane.form-group(role="group")
#compilation.tab-pane(role="group")
div
+checkbox("compileOnChange", "Compile automatically when source changes")
+checkbox("autoDelayBeforeCompile", "Use automatic delay before compiling")
div
label Delay before compiling:&nbsp;
span.delay-current-value &nbsp;
.slider-input
b 0.25s
input.delay(type="range")
b 3s
mb-3
label.form-label(for="settings-input-delay") Delay before compiling:&nbsp;
span.delay-current-value &nbsp;
.slider-input(id="settings-input-delay")
b 0.25s
input.delay(type="range")
b 3s
+checkbox("formatOnCompile", "Enable formatting on compilation (for supported languages)")
+checkbox("executorCompileOnChange", "Compile executor automatically when arguments change")
+checkbox("shakeStatusIconOnWarnings", "Shake the status icon on argument warnings")
.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-header
h5.modal-title Share
button.close(type="button" data-dismiss="modal" aria-hidden="true" aria-label="Close")
span(aria-hidden="true")
| &times;
button.btn-close(type="button" data-bs-dismiss="modal" aria-hidden="true" aria-label="Close")
.modal-body
.card
.card-body
.container
label.form-label.visually-hidden(for="share-permalink") Share URL
.input-group
input.form-control.permalink(type="text" placeholder="Loading" readonly size="1024")
.input-group-append
button.btn.btn-outline-secondary.clippy(type="button" data-clipboard-target="#sharelinkdialog .permalink" title="Copy to clipboard")
span.fa.fa-clipboard
#embedsettings.form-group(role="group")
.checkbox
label
input.readOnly(type="checkbox")
| Read Only
.checkbox
label
input.hideEditorToolbars(type="checkbox")
| Hide Editor Toolbars
input.form-control.permalink#share-permalink(type="text" placeholder="Loading" readonly size="1024" aria-describedby="share-permalink-help")
button.btn.btn-outline-secondary.clippy(type="button" data-clipboard-target="#sharelinkdialog .permalink" title="Copy to clipboard" aria-label="Copy to clipboard")
span.fa.fa-clipboard
small.form-text.text-muted#share-permalink-help Use this URL to share your code with others
#embedsettings.mb-3(role="group")
.form-check
input.form-check-input.readOnly(type="checkbox" id="share-checkbox-readonly")
label.form-check-label(for="share-checkbox-readonly") Read Only
.form-check
input.form-check-input.hideEditorToolbars(type="checkbox" id="share-checkbox-hideEditorToolbars")
label.form-check-label(for="share-checkbox-hideEditorToolbars") Hide Editor Toolbars
if sharingEnabled
.socialsharing.mt-2

View File

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

View File

@@ -3,10 +3,8 @@
.modal-content
.modal-header
h5.modal-title Timing
button.close(type="button" data-dismiss="modal" aria-hidden="true" aria-label="Close")
span(aria-hidden="true")
| &times;
button.btn-close(type="button" data-bs-dismiss="modal" aria-hidden="true" aria-label="Close")
.modal-body
#chart
.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-header
h5.modal-title Well, do you or not?
button.close(type="button" data-dismiss="modal" aria-hidden="true" aria-label="Close")
span(aria-hidden="true")
| &times;
button.btn-close(type="button" data-bs-dismiss="modal" aria-hidden="true" aria-label="Close")
.modal-body
.modal-footer
button.btn.btn-link.no(type="button" data-dismiss="modal") No
button.btn.btn-link.yes(type="button" data-dismiss="modal") Yes
button.btn.btn-link.no(type="button" data-bs-dismiss="modal") No
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")
select.function-selector
.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.hideable Export
.dropdown-menu

View File

@@ -8,7 +8,7 @@ mixin optionButton(bind, isActive, text, title)
.top-bar.btn-toolbar.bg-light(role="toolbar")
include ../../font-size
.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.hideable Options
.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")
span.fa.fa-save
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.hideable Add new...
.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("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")
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.hideable Vim
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")
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
span.fas.fa-info
select.change-language(title="Change this editor's (and associated panels) language" placeholder="Language" disabled=embedded && readOnly)
div.currentCursorPosition
div#v-status
div.v-status
.monaco-placeholder

View File

@@ -10,4 +10,4 @@
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.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")
.btn-group.btn-group-sm(role="group" aria-label="Compiler picker")
.input-group
.input-group-prepend
.input-group
select.compiler-picker
.input-group-append
button.btn.btn-sm.btn-light.input-group-text.picker-popout-button(data-trigger="click" style="cursor: pointer;" role="button" title="Compiler picker popout")
span
i.fa-solid.fa-arrow-up-right-from-square
.input-group-append
button.btn.btn-sm.btn-light.input-group-text.prepend-options(data-trigger="click" style="cursor: pointer;" role="button" title="All compilation options")
span.status-icon
input.options.form-control(type="text" placeholder="Compiler options..." size="256" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false")
.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
select.compiler-picker
button.btn.btn-sm.btn-light.input-group-text.picker-popout-button(data-trigger="click" style="cursor: pointer;" role="button" title="Compiler picker popout")
span
i.fa-solid.fa-arrow-up-right-from-square
button.btn.btn-sm.btn-light.input-group-text.prepend-options(data-trigger="click" style="cursor: pointer;" role="button" title="All compilation options")
span.status-icon
input.options.form-control(type="text" placeholder="Compiler options..." size="256" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" aria-label="Compiler options")
.populararguments(title="Popular arguments")
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.visually-hidden Popular arguments
div.dropdown-menu.dropdown-menu-end
button.dropdown-item.btn.btn-light.btn-sm
.argmenuitem
span.argtitle Detailed Compiler Flags
span.argdescription Open a new window to edit verbose compiler flags
include ../../font-size
.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.hideable Output...
.dropdown-menu
include ../../options-output
.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.hideable Filter...
.dropdown-menu
@@ -48,10 +44,10 @@ mixin newPaneButton(classId, text, title, icon)
span.fas.fa-wrench
span.dp-text.hideable Overrides
.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.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("create-executor", "Executor From This", "Create executor from this compiler", "fas fa-microchip")
+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-cfg", "Control Flow Graph", "Show assembly control flow graphs", "fas fa-exchange-alt")
.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.hideable Add tool...
.dropdown-menu.dropdown-menu-right.new-tool-dropdown
.dropdown-menu.dropdown-menu-end.new-tool-dropdown
.monaco-placeholder
.bottom-bar.bg-light
if !embedded
.btn-group.btn-group-sm
.input-group
.input-group-prepend
button.btn.btn-sm.btn-light.clear-cache(title="Clear cache & recompile")
span.fas.fa-redo
button.btn.btn-sm.btn-light.output-btn(data-cy="new-output-pane-btn")
button.btn.btn-sm.btn-light.clear-cache(title="Clear cache & recompile" aria-label="Clear cache and recompile")
span.fas.fa-redo
button.btn.btn-sm.btn-light.output-btn(data-cy="new-output-pane-btn" aria-label="Show compiler output")
span.fas.fa-receipt.status-text
| &nbsp;Output
span.output-count
span.output-count(aria-live="polite")
| &nbsp;(
span.text-count
| 0
@@ -96,11 +91,11 @@ mixin newPaneButton(classId, text, title, icon)
span.err-count
| 0
| )
span.short-compiler-name.mr-1
button.btn.btn-sm.btn-light.full-compiler-name(data-trigger="click" style="cursor: pointer;" role="button")
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" aria-label="Show compiler information")
span.fas.fa-info
span.compile-info.mr-1(title="Compilation info")
button.btn.btn-sm.btn-light.full-timing-info(data-trigger="click" style="cursor: pointer;" role="button")
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" aria-label="Show full timing information")
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

View File

@@ -2,5 +2,5 @@
.top-bar.btn-toolbar.bg-light(role="toolbar")
include ../../font-size
.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

View File

@@ -1,18 +1,16 @@
#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
.btn-group.btn-group-sm
.input-group.input-group-sm.mb-auto
.input-group-prepend
label.input-group-text
| Left:&nbsp;
.btn-group.btn-group-sm.flex-grow-1.me-2
.input-group.input-group-sm.w-100
label.input-group-text
| Left:&nbsp;
select.diff-picker.lhs(placeholder="Select compiler...")
select.difftype-picker.lhsdifftype(placeholder="...")
.btn-group.btn-group-sm.ml-4
.input-group.input-group-sm.mb-auto
.input-group-prepend
label.input-group-text
| Right:&nbsp;
.btn-group.btn-group-sm.flex-grow-1
.input-group.input-group-sm.w-100
label.input-group-text
| Right:&nbsp;
select.diff-picker.rhs(placeholder="Select compiler...")
select.difftype-picker.rhsdifftype(placeholder="...")
.monaco-placeholder

View File

@@ -30,40 +30,37 @@
.top-bar.btn-toolbar.bg-light.panel-compilation(role="toolbar")
.btn-group.btn-group-sm(role="group" aria-label="Compiler picker")
.input-group
.input-group-prepend
.input-group
select.compiler-picker
.input-group-append
button.btn.btn-sm.btn-light.input-group-text.picker-popout-button(data-trigger="click" style="cursor: pointer;" role="button" title="Compiler picker popout")
span
i.fa-solid.fa-arrow-up-right-from-square
.input-group-append
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")
select.compiler-picker
button.btn.btn-sm.btn-light.input-group-text.picker-popout-button(data-trigger="click" style="cursor: pointer;" role="button" title="Compiler picker popout")
span
i.fa-solid.fa-arrow-up-right-from-square
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
label.visually-hidden(for="compilation-options") Compiler options for execution
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")
// TODO: Maybe enable this in the future?
//.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
//.populararguments(title="Popular arguments")
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.visually-hidden Popular arguments
div.dropdown-menu.dropdown-menu-end
button.dropdown-item.btn.btn-light.btn-sm
.argmenuitem
span.argtitle Detailed 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")
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")
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
.execution-status
.compiler-output
.execution-output
.execution-status(aria-live="polite" role="status" aria-label="Execution status")
.compiler-output(aria-live="polite" role="log" aria-label="Compiler output")
.execution-output(aria-live="polite" role="log" aria-label="Program execution output")
.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
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.short-compiler-name
button.btn.btn-sm.btn-light.fas.fa-info.full-compiler-name(data-trigger="click" style="cursor: pointer;" role="button")
span.compile-time(title="Compilation time (Result size)")
button.btn.btn-sm.btn-light.fas.fa-chart-bar.full-timing-info(data-trigger="click" style="cursor: pointer;" role="button")
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" aria-label="Show compiler info")
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" 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")
select.gccdump-pass-picker(placeholder="Select a pass...")
.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.hideable Passes
.dropdown-menu
@@ -18,7 +18,7 @@ mixin optionButton(bind, isActive, text, title)
+optionButton("rtlDump", true, "RTL Pass", "Show RTL passes")
+optionButton("ipaDump", true, "IPA Pass", "Show IPA passes")
.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.hideable Options
.dropdown-menu

View File

@@ -13,14 +13,14 @@ mixin optionButton(bind, isActive, text, title)
span Wrap lines
input.d-none(type="checkbox" checked=false)
.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.hideable Options
.dropdown-menu
+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")
.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.hideable Filters
.dropdown-menu

View File

@@ -8,7 +8,7 @@ mixin optionButton(bind, isActive, text, title)
.top-bar.btn-toolbar.bg-light(role="toolbar")
include ../../font-size
.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.hideable Options
.dropdown-menu
@@ -16,7 +16,7 @@ mixin optionButton(bind, isActive, text, title)
+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")
.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.hideable Filters
.dropdown-menu
@@ -26,9 +26,8 @@ mixin optionButton(bind, isActive, text, title)
//- +optionButton("library-functions", true, "Filter Library Functions", "Filter library functions")
.btn-group.btn-group-sm
.input-group.input-group-sm.mb-auto
.input-group-prepend
label.input-group-text.opt-group-name
| Function:&nbsp;
label.input-group-text.opt-group-name
| Function:&nbsp;
select.opt-group-picker.group-selector(placeholder="Select group")
div.opt-pipeline-body
.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)
// TODO: Options - display inlining context, color token (from col-num), remove duplicates
.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.hideable Filters
.dropdown-menu

View File

@@ -1,7 +1,7 @@
#tree
.top-bar.btn-toolbar.bg-light.mainbar(role="toolbar")
.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.hideable Project
.dropdown-menu
@@ -10,7 +10,7 @@
span.dropdown-icon.fa.fa-save
| Save
.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.hideable Add new...
.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")
span CMake
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)
.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")
.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")
.tree.v-scroll
ul.list-group
ul.list-group.list-group-flush
li.root.list-group-item.far-fa-folder
.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
.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;")
span.col-lg.text-center Drop files here

View File

@@ -1,19 +1,14 @@
#compiler-selector
.form-row
.row
.input-group
.input-group-prepend
.input-group
select.compiler-picker
.input-group-append
button.btn.btn-sm.btn-light.input-group-text.picker-popout-button(data-trigger="click" style="cursor: pointer;" role="button" title="Compiler picker popout")
span
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
select.compiler-picker
button.btn.btn-sm.btn-light.input-group-text.picker-popout-button(data-trigger="click" style="cursor: pointer;" role="button" title="Compiler picker popout")
span
i.fa-solid.fa-arrow-up-right-from-square
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-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-times.close.compiler-selector-icon(aria-label="Close" title="Close")
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.close.copy-compiler.compiler-selector-icon(aria-label="Copy compiler" title="Copy compiler")
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-share.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")

View File

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

View File

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

View File

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

View File

@@ -13,4 +13,4 @@
.runtime-tool-option
span.tool-option-name
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
li.list-group-item.tree-editor-file.input-group-append
li.list-group-item.tree-editor-file
span.filename someresource.txt
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")