{"id":13402821,"url":"https://github.com/1j01/os-gui","last_synced_at":"2025-04-09T11:11:30.903Z","repository":{"id":43749784,"uuid":"126927087","full_name":"1j01/os-gui","owner":"1j01","description":"Retro OS GUI JS/CSS library","archived":false,"fork":false,"pushed_at":"2024-05-22T02:11:37.000Z","size":2090,"stargazers_count":159,"open_issues_count":11,"forks_count":17,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-05-22T20:09:45.171Z","etag":null,"topics":["gui","menu","menubar","menus","os","ui","ui-components","windowing","windows","windows-95","windows-98"],"latest_commit_sha":null,"homepage":"https://os-gui.js.org","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/1j01.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-03-27T04:06:19.000Z","updated_at":"2024-05-29T19:34:18.667Z","dependencies_parsed_at":"2024-05-29T19:34:10.079Z","dependency_job_id":"8cdad3c4-b017-4d49-8587-50e47a3e6bb6","html_url":"https://github.com/1j01/os-gui","commit_stats":{"total_commits":482,"total_committers":1,"mean_commits":482.0,"dds":0.0,"last_synced_commit":"bb4df0f0c26969c089858118130975cd137cdac8"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1j01%2Fos-gui","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1j01%2Fos-gui/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1j01%2Fos-gui/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1j01%2Fos-gui/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/1j01","download_url":"https://codeload.github.com/1j01/os-gui/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248027411,"owners_count":21035594,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["gui","menu","menubar","menus","os","ui","ui-components","windowing","windows","windows-95","windows-98"],"created_at":"2024-07-30T19:01:21.313Z","updated_at":"2025-04-09T11:11:30.885Z","avatar_url":"https://github.com/1j01.png","language":"JavaScript","funding_links":[],"categories":["JavaScript","\u003cimg src=\"media/icons8-windows-11-48.png\" alt=\"logo\" width=\"36\"/\u003e  WINDOWS"],"sub_categories":[],"readme":"# \u003ca href=\"https://os-gui.js.org\"\u003e\u003cimg alt=\"os-gui.js\" src=\"images/os-gui-logo.svg\" height=\"200\"\u003e\u003c/a\u003e\n\nA library for imitating operating system graphical user interfaces on the web\n\nSpecifically, Windows 98 — for now at least; it could be expanded in the future.\n\nThis library powers [98.js.org](https://98.js.org), a web-based version of Windows 98, including Paint, Notepad, Sound Recorder, and more.\n\nSee the [homepage](https://os-gui.js.org/) for more information.\n\n\n## Features\n\n- Menu bars, with support for checkbox and radio items, disabled states, submenus, keyboard shortcuts, and more\n\n- App windows which you can drag around, maximize, minimize, close, and resize\n\n- Dialog and tool window variants\n\n- Flying titlebar animation that guides your eyes during maximize/minimize/restore\n\n- Focus containment: if you Tab or Shift+Tab within a window, it wraps around to the first/last control.\n\n- Button styles, including lightweight buttons, disabled buttons, and default action buttons\n\n- Scrollbar styles, webkit-specific (in the future there could be a custom scrollbar based on a nonintrusive scrollbar library, or styles *supporting* a library, where you're expected to use the library directly)\n  - Procedurally rendered arrows, allowing for different scrollbar sizes\n  - Inversion effect when clicking on scrollbar track\n\n- Themeable with Windows `.theme` \u0026 `.themepack` files **at runtime**!\n\n## Demo\n\nSee [demos online here](https://os-gui.js.org)\n\n### See also\n\n- [98.js](https://github.com/1j01/98), my web desktop\n- [padraigfl/packard-belle](https://github.com/padraigfl/packard-belle/)\n- [arturbien/React95](https://github.com/arturbien/React95)\n- [React95/React95](https://github.com/React95/React95)\n\n\n## Requirements\n\nThis library currently requires [jQuery](https://jquery.com/) for the windowing implementation.\nMenu bars do **not** require jQuery.\n\n(Eventually I want to have no dependencies. So far I've removed jQuery from the menu code...)\n\n\n## Setup\n\nThe library is not (yet) provided as a single convenient file.\n\nYou can either 1. download the repository as a ZIP file, 2. clone the repository, or 3. install the library as an [npm package](https://www.npmjs.com/package/os-gui).\n\nYou have to include scripts for the components you want to use (`MenuBar.js` or `$Window.js`),\nalong with stylesheets for layout, a theme, and a color scheme.\n\nMake sure to use the compiled CSS files, not the source files.\n\u003c!-- If you're not installing with `npm`, you'll have to build the library yourself. See [Development](#development) below. --\u003e\n\nIn `\u003chead\u003e`:\n```html\n\u003clink href=\"os-gui/layout.css\" rel=\"stylesheet\" type=\"text/css\"\u003e\n\u003clink href=\"os-gui/windows-98.css\" rel=\"stylesheet\" type=\"text/css\"\u003e\n\u003clink href=\"os-gui/windows-default.css\" rel=\"stylesheet\" type=\"text/css\"\u003e\n```\n\nIn `\u003chead\u003e` or `\u003cbody\u003e`:\n```html\n\u003cscript src=\"os-gui/MenuBar.js\"\u003e\u003c/script\u003e\n\n\u003cscript src=\"lib/jquery.js\"\u003e\u003c/script\u003e \u003c!-- required by $Window.js --\u003e\n\u003cscript src=\"os-gui/$Window.js\"\u003e\u003c/script\u003e\n```\n\n\n## API\n\n**Note**: The API will likely change a lot, but I maintain a [Changelog](CHANGELOG.md).\n\n### Panel \u0026 Inset Styles\n\n- `.inset-deep` creates a 2px inset border\n- `.outset-deep` creates a 2px inset border (like a button or window or menu popup)\n- `.inset-shallow` creates a 1px inset border\n- `.outset-shallow` creates a 1px outset border\n\n### Button styles\n\nButton styles are applied to `button` elements globally.\n(And if you ever want to reset it, note that you have to get rid of the pseudo element `::after` as well. @TODO: scope CSS)\n\n#### Toggle Buttons\nTo make a toggle button, add the `.toggle` class to the button.\nMake it show as pressed with the `.selected` class. (@TODO: rename this `.pressed`)\n\nYou should use the styles together with semantic `aria-pressed`, `aria-haspopup`, and/or `aria-expanded` attributes as appropriate.\n\n#### Default Buttons\nYou can show button is the default action by adding `.default` to the button.\nNote that in Windows 98, this style moves from button to button depending on the focus.\nA rule of thumb is that it should be on the button that will trigger with Enter. \n\n#### Lightweight Buttons\nYou can make a lightweight button by adding `.lightweight` to the button.\nLightweight buttons are subtle and have no border until hover.\n\n#### Disabled Button States\nYou can disable a button by adding the standard `disabled` attribute to the button.\n\n#### Pressed Button States\nYou can show a button as being pressed by adding the `.pressing` class to the button.  \nThis is useful for buttons that are triggered by a keystroke.\n\n### Scrollbar styles\n\nScrollbar styles are applied globally, but they have a `-webkit-` prefix, so they'll only work in \"webkit-based\" browsers, generally, like Chrome, Safari, and Opera.\n\n(Can be overridden with `::-webkit-scrollbar` and related selectors (but not easily reset to the browser default, unless `-webkit-appearance: scrollbar` works... @TODO: scope CSS)\n\n### Selection styles\n\nSelection styles are applied globally.\n\n(Can be overridden with `::selection` (but not easily reset to the browser default... unless with `unset`? @TODO: scope CSS)\n\n### `MenuBar(menus)`\n\nCreates a menu bar component.\n\n`menus` should be an object holding arrays of [menu item specifications](#menu-item-specification), keyed by menu button name.\n\nReturns an object with property `element`, which you should then append to the DOM where you want it.\n\nSee examples in the [demo code](./demo/demo.js).\n\n#### `element`\n\nThe DOM element that represents the menu bar.\n\n#### `closeMenus()`\n\nCloses any menus that are open.\n\n#### `setKeyboardScope(...eventTargets)`\n\nHotkeys like \u003ckbd\u003eAlt\u003c/kbd\u003e will be handled at the level of the given element(s) or event target(s).\n\nBy default, the scope is `window` (global), for the case of a single-page application where the menu bar is at the top.\nIf you are putting the menu bar in a window, you should call this with the window's element:\n\n```js\nmenu_bar.setKeyboardScope($window.element);\n```\nor better yet,\n```js\n$window.setMenuBar(menu_bar);\n```\nwhich takes care of the keyboard scope for you, while attaching the menu bar to the window.\n\nNote that some keyboard behavior is always handled if the menu bar has focus.\n\nNote also for iframes, you may need to call this with `$window[0], iframe.contentWindow` currently, but this should be changed in the future (keyboard events should be proxied).\n\n#### Event: `info`\n\nCan be used to implement a status bar.\nA description is provided as `event.detail.description` when rolling over menu items that specify a `description`. For example:\n\n```js\nmenubar.element.addEventListener(\"info\", (event)=\u003e {\n\tstatusBar.textContent = event.detail?.description || \"\";\n});\n```\n\n#### Event: `default-info`\n\nSignals that a status bar should be reset to blank or a default message.\n\n```js\nmenubar.element.addEventListener(\"default-info\", (event)=\u003e {\n\tstatusBar.textContent = \"\";\n\n\t// or:\n\tstatusBar.textContent = \"For Help, click Help Topics on the Help Menu.\";\n\t// like in MS Paint (and JS Paint)\n\n\t// or:\n\tstatusBar.textContent = \"For Help, press F1.\";\n\t// like WordPad\n\n\t// or perhaps even:\n\tstatusBar.innerHTML = \"For Help, \u003ca href='docs'\u003eclick here\u003c/a\u003e\";\n\t// Note that a link is not a common pattern, and it could only work for the default text;\n\t// for menu item descriptions the message in the status bar is transient, so\n\t// you wouldn't be able to reach it to click on it.\n});\n```\n\n### Menu item specification\n\nMenu item specifications are either `MENU_DIVIDER` (a constant indicating a horizontal rule), or a radio group specification, or an object with the following properties:\n\n* `label`: a label for the item; ampersands define [access keys](#access-keys) (to use a literal ampersand, use `\u0026\u0026`)\n* `shortcutLabel` (optional): a keyboard shortcut to show for the item, like \"Ctrl+A\" (Note: you need to listen for the shortcut yourself, unlike access keys)\n* `ariaKeyShortcuts` (optional): [`aria-keyshortcuts`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-keyshortcuts) for the item, like \"Control+A Meta+A\", for screen readers. \"Ctrl\" is not valid (you must spell it out), and it's best to provide an alternative for macOS, usually with the equivalent Command key, using \"Meta\" (and `event.metaKey`).\n* `action` (optional): a function to execute when the item is clicked (can only specify either `action` or `checkbox`)\n* `checkbox` (optional): an object specifying that this item should behave as a checkbox.\n\t* Property `check` of this object should be a function that *checks* if the item should be checked or not, returning `true` for checked and `false` for unchecked. What a cutesy name.\n\t* Property `toggle` should be a function that toggles the state of the option, however you're storing it; called when clicked.\n* `enabled` (optional): can be `false` to unconditionally disable the item, or a function that determines whether the item should be enabled, returning `true` to enable the item, `false` to disable.\n* `submenu` (optional): an array of menu item specifications to create a submenu\n* `description` (optional): for implementing a status bar; an [`info` event](#event-info) is emitted when rolling over the item with this description\n* `value` (optional): for radio items, the value of the item; can be any type, but `===` is used to determine whether the item is checked.\n\nA radio group specification is an object with the following properties:\n\n* `radioItems`: an array of menu item specifications to create a radio button group. Unlike `submenu`, the items are included directly in this menu. It is recommended to separate the radio group from other menu items with a `MENU_DIVIDER`.\n* `getValue`: a function that should return the value of the selected radio item.\n* `setValue`: a function that should change the state to the given value, in an application-specific way.\n* `ariaLabel` (optional): a string to use as the `aria-label` for the radio group (for screen reader accessibility)\n\n### Access Keys\n\nMenus can be navigated with contextual hotkeys known as **access keys**.\n\nPlace an ampersand before a letter in a menu button or menu item's label to make it an access key.\nMicrosoft has [documentation on access keys](https://docs.microsoft.com/windows/apps/design/input/access-keys),\nincluding guidelines for choosing access keys.\nGenerally the first letter is a good choice.\n\nIf a menu item doesn't define an access key, the first letter of the label can be used to access it.\n\nFor menu buttons, you need to hold Alt when pressing the button's access key, but for menu items in menu popups you must press the key directly, as Alt will close the menus.\n\nIf there are multiple menu items with the same access key, it will cycle between them without activating them.\nYou should try to make the access keys unique, including between defined access keys and the first letters of menu items without defined access keys.\n(This behavior is observed in Windows 98, in Explorer's Favorites menu, where you can make bookmarks with the first letter matching the access keys of other menu items.)\n\n\u003c!-- @TODO: this section is an awkward mix of explaining what access keys are, how they work, and how to implement them; should restructure --\u003e\n\nThere is an `AccessKeys` object exported by `MenuBar.js`, with functions for dealing with access keys:\n\n#### `AccessKeys.escape(label)`\n\nEscapes ampersands in the given label, so that they are not interpreted as access keys.\n\nThis is useful for dynamic menus, like a history menu that uses page titles as labels. You don't want ampersands to be spuriously interpreted as access keys, or double ampersands to be interpreted as a single ampersand.\n\n#### `AccessKeys.unescape(label)`\n\nUn-escapes all double ampersands in the label.\n\nFor rendering, use [`toHTML`](#accesskeys-tohtml-label) or [`toFragment`](#accesskeys-tofragment-label) instead.\n\n\u003c!-- #### `AccessKeys.indexOf(label)`\n\nReturns the index of the ampersand that defines an access key, or -1 if not present.\n\nInternal use only. --\u003e\n\n#### `AccessKeys.has(label)`\n\nReturns true if the label has an access key.\n\n#### `AccessKeys.get(label)`\n\nReturns the access key for the given label, or null if none.\n\n`MenuBar` handles access keys automatically, but if you're including access keys for other UI elements, you need to handle them yourself, and you can use this rather than hard-coding the access key, so it doesn't need to be changed in two places.\n\n#### `AccessKeys.remove(label)`\n\nRemoves the access key indicator (`\u0026`) from the label, and un-escapes any double ampersands.\nAlso removes a parenthetical access key indicator, like \" (\u0026N)\", as a special case.\n\n#### `AccessKeys.toText(label)`\n\nRemoves the access key indicator (`\u0026`) from the label, and un-escapes any double ampersands.\nThis is like [`toHTML`](#accesskeys-tohtml-label) but for plain text.\n\n**Note**: while often access keys are part of a word, like \"\u0026New\",\nin translations they are often indicated separately, like \"새로 만들기 (\u0026N)\",\nsince the access key stays the same, but the letter is no longer part of the word (or even the alphabet).\nThis function doesn't remove strings like \" (\u0026N)\", it will remove only the \"\u0026\" and leave \"새로 만들기 (N)\".\nIf you want that behavior, use `AccessKeys.remove(label)`.\n\n#### `AccessKeys.toHTML(label)`\n\nReturns HTML (with proper escaping) with the access key as a `\u003cspan class='menu-hotkey'\u003e` element.\n\n**Security note**: It is safe to use the result of this function in HTML element content, as it escapes the label. It is not safe to use in an attribute value, but this is not the intended usage.\n\n**Layout note**: you may want to surround the result with `\u003cspan style=\"white-space: pre\"\u003e` to prevent whitespace from collapsing if the access key is next to a space.\n\n#### `AccessKeys.toFragment(label)`\n\nReturns a [`DocumentFragment`](https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment) with the access key as a `\u003cspan class='menu-hotkey'\u003e` element.\n\n**Layout note**: you may want to surround the result with `\u003cspan style=\"white-space: pre\"\u003e` to prevent whitespace from collapsing if the access key is next to a space.\n\n\n### `$Window(options)`\n\nCreates a window component that can be dragged around and such, brought to the front when clicked, and closed.\nDifferent types of windows can be created with different options.\nNote that focus wraps within a window's content.\n\nReturns a jQuery object with additional methods and properties (see below, after options).\n\nThe DOM node can be accessed with `$window.element`, and the `$Window` object can be accessed from the DOM node with with `element.$window`.\n\n\u003ctable\u003e\u003ctr\u003e\u003ctd\u003e\n\n`options.title`: Sets the initial window caption.\n\n`options.icons`: Specifies the icon of the window at different sizes. Pass an object with keys that are sizes in pixels (or \"any\"), and values that are the URL of an image, or an object with `srcset` if you want support different pixel densities, or a DOM node if you want full control (e.g. to use an `\u003csvg\u003e` or a font icon or an emoji).\n\n`options.toolWindow`: If `true`, the window will be a tool window, which means it will not have a minimize or maximize button, and it will be shown as always focused by default. It will also have a smaller close button in the default styles.\n\n`options.parentWindow`: If specified, the window will be a child of this window. For tool windows, the focus state will be shared with the parent window.\n\n`options.maximizeButton`: If set to `false`, the window will not have a maximize button. You cannot enable this if `toolWindow` is `true`.\n\n`options.minimizeButton`: If set to `false`, the window will not have a minimize button. You cannot enable this if `toolWindow` is `true`.\n\n`options.closeButton`: If set to `false`, the window will not have a close button.\n\n`options.resizable`: If set to `true`, the window can be resized by the edges and corners.\n\n`options.outerWidth`: Specifies the initial width of the window, including borders.\n\n`options.outerHeight`: Specifies the initial height of the window, including title bar, menu bar, and borders.\n\n`options.innerWidth`: Specifies the initial width of the window contents, excluding borders.\n\n`options.innerHeight`: Specifies the initial height of the window contents, excluding title bar, menu bar, and borders\n\n`options.minOuterWidth`: The minimum outer width of the window (when resizing), in pixels.\n\n`options.minOuterHeight`: The minimum outer height of the window (when resizing), in pixels.\n\n`options.minInnerWidth`: The minimum width of the window contents (when resizing), in pixels.\n\n`options.minInnerHeight`: The minimum height of the window contents (when resizing), in pixels.\n\n`options.constrainRect(rect, x_axis, y_axis)`: A function that can be used to constrain the window to a particular rectangle.\nTakes and returns a rectangle object with `x`, `y`, `width`, and `height` properties.\n`x_axis` and `y_axis` define what is being dragged: `-1` for left and top, `1` for right and bottom, and `0` for middle.\nNote that the window will always be constrained to not move past the minimum width and height.\n\n`options.iframes`: Contains options for controlling iframe integration.\nBy default OS-GUI will try to enhance iframes with logic to:\n- [x] Show the window as focused when the iframe has focus (this even works for nested iframes!)\n- [x] Restore focus to controls in the iframe when refocusing the window (e.g. clicking the titlebar) (this even works for nested iframes!)\n- [ ] Propagate theme to iframes (i.e. when you drag a Windows `.theme` file, apply it to iframes too)\n\t- [x] Theme is propagated to iframes when using `applyCSSProperties(cssProperties, {element, recurseIntoIframes: true})`\n\t- [ ] @TODO: apply theme for new iframes, not just existing ones (needs a place to store the current theme, or a way to listen for changes to CSS properties in the DOM so it can dynamically inherit them across the frame boundary, supporting stylesheets as well as inline styles)\n- [ ] @TODO: proxy mouse and keyboard events to and from the iframe, to allow for:\n\t- [ ] Outer window to capture and prevent keyboard events\n\t\t- Handle menu Alt+(access key) hotkeys when focus is in the iframe\n\t\t- An obvious use case is a browser app that loads arbitrary interactive content, but reserves some keyboard shortcuts for its own use. That said, if you're implementing a browser inside a browser, you can't reserve any of the keyboard shortcuts that the real browser reserves! (Maybe an electron version of 98.js.org would be able to though.)\n\t- [ ] Iframe to handle shortcuts when menus are focused\n\t- [ ] Fixing issues where dragging inside the iframe (without needing pointer capture):\n\t\t- [ ] Let the iframe to handle mouseup/pointerup events outside itself, so it knows when to end dragging.\n\t\t- [ ] Let the iframe to handle mousemove/pointermove events outside itself, so it works when dragging outside the iframe (it's ugly if it stops at the border).\n\t\t- [ ] At the start of a drag when the iframe was not previously focused, the gesture should be uninterrupted. (Does this need event proxying? Or just less nosy focus-handling (don't call focus() where not needed)? I think it currently focuses each parent browsing context before restoring focus inside the iframe, and it should really just figure out if it can focus an inner control, and IF NOT focus an outer one. And it shouldn't `focus()` what's already focused.)\n\t\t- [ ] When dragging over elements outside the iframe, such as an overlapped window (even with an iframe inside it), the interacted iframe should be able to handle the drag. (It just needs a `.pointer-is-down iframe { pointer-events: none; }` and an override on the interacted iframe. I've done it in 98.js.org, easy. In addition to mousemove/pointermove proxying.)\n\n`options.iframes.ignoreCrossOrigin`: Set to true to silence cross-origin warnings for iframes within the window. Focus integration can't fully work with cross-origin iframes. There will be cases where the window is not shown as focused when clicking into the iframe, and focus can't be restored to controls within the iframe.\n\n\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\nReturns a jQuery object with additional methods and properties:\n\n#### `title(text)`\n\nSets the title, or if `text` isn't passed, returns the current title of the window.\n\n#### `close(force=false)`\n\nCloses the window.\n\nIf `force` is `true`, the \"close\" event will not be emitted, and the window will be closed immediately.\n\n#### `focus()`\n\nTries to focus something within the window, in this order of priority:\n- The last focused control within the window\n- A control with `class=\"default\"`\n- If it's a tool window, the parent window\n- and otherwise the window itself (specifically `$window.$content`)\n\n#### `blur()`\n\nRemoves focus from the window. If focus is outside the window, it is left unchanged.\n\n#### `minimize()`\n\nMinimizes the window. If `$window.task.$task` is defined it will use that as a target for minimizing, otherwise the window will minimize to the bottom of the screen.\n\nCurrent behavior is that it *toggles* minimization. This may change in the future.\n\n#### `maximize()`\n\nMaximizes the window. While maximized, the window will use `position: fixed`, so it will not scroll with the page.\n\nCurrent behavior is that it *toggles* maximization. This may change in the future. Also, if minimized, it will restore instead of maximizing. Basically, it does what the maximize button does, rather than simply what the method name suggests.\n\n#### `unminimize()`\n\nPRIVATE: don't use this. Use `restore()` instead.\n\nRestores the window from minimized state.\n\n#### `restore()`\n\nRestores the window from minimized or maximized state. If the window is not minimized or maximized, this method does nothing.\n\n#### `center()`\n\nCenters the window in the page.\nYou should call this after the contents of the window is fully rendered, or you've set a fixed size for the window.\n\nIf you have images in the window, wait for them to load before showing and centering the window, or define a fixed size for the images.\n\n#### `applyBounds()`\n\nFits the window within the page if it's partially offscreen.\n(Doesn't resize the window if it's too large; it'll go off the right and bottom of the screen.)\n\n#### `bringTitleBarInBounds()`\n\nRepositions the window so that the title bar is within the bounds of the page, so it can be dragged.\n\n#### `bringToFront()`\n\nBrings the window to the front by setting its `z-index` to larger than any `z-index` yet used by the windowing system.\n\n#### `setDimensions({ innerWidth, innerHeight, outerWidth, outerHeight })`\n\nSets the size of the window. Pass `{ innerWidth, innerHeight }` to specify the size in terms of the window content, or `{ outerWidth, outerHeight }` to specify the size including the window frame.\n\n*(This may be expanded in the future to allow setting the position as well...)*\n\n#### `setIcons(icons)`\n\nChanges the icon(s) of the window. `icons` is in the same format as `options.icons`.\n\n#### `setTitlebarIconSize(size)`\n\nSets the size of the window's title bar icon, picking the closest size that's available.\n\n#### `getTitlebarIconSize()`\n\nReturns the size of the window's title bar icon.\n\n#### `getIconAtSize(size)`\n\nPicks the closest icon size that's available, and returns a unique DOM node (i.e. cloned),\nor `null` if no icons are defined.\n\nThis can be used for representing the window in the taskbar.\n\n#### `setMenuBar(menuBar)`\n\nAppends the menu bar to the window, and sets the keyboard scope for the menu bar's hotkeys to the window.\n\nCan be called with `null` to remove the menu bar.\n\n#### `setMinimizeTarget(minimizeTargetElement)`\n\nThe minimize target (taskbar button) represents the window when minimized, and is used for animating minimize and restore.\nIf `minimizeTargetElement` is `null`, the window will minimize to the bottom of the screen (the default).\n\n#### `$Button(text, action)`\n\nCreates a button in the window's content area.\nIt automatically closes the window when clicked. There's no (good) way to prevent this, as it's intended only for dialogs.\n\nIf you need any other behavior, just create a `\u003cbutton\u003e` and add it to the window's content area.\n\nReturns a jQuery object.\n\n#### `addChildWindow($window)`\n\nPRIVATE: don't use this.\n\nDefines a window as a child. For tool windows, the focus state will be shared with the parent window.\n\nThis is used internally when you set `options.parentWindow` when creating a window.\n\n#### `onFocus(listener)`\n\nCalls the listener when the window is (visually?) focused.\n\nReturns a function to remove the listener.\n\n#### `onBlur(listener)`\n\nCalls the listener when the window (visually?) loses focus.\n\nReturns a function to remove the listener.\n\n#### `onClosed(listener)`\n\nCalls the listener when the window is closed (after the close event is emitted, and if it wasn't prevented).\n\nReturns a function to remove the listener.\n\n#### `onBeforeClose(listener)`\n\nCalls the listener before the window is closed. If the listener calls `event.preventDefault()`, the window will not be closed.\n\nReturns a function to remove the listener.\n\nThis event is useful for confirming with the user before closing a window, for example.  \n`$window.close(true)` can then be used to bypass this event (and the confirmation) when the window should really be closed.\n\nIf you're not going to prevent closing the window, you should probably use the `closed` event instead, since, hypothetically, another listener could prevent closing *after* your listener, leading to premature cleanup.\n\n#### `onBeforeDrag(listener)`\n\nCalls the listener before the window is dragged by the titlebar. If the listener calls `event.preventDefault()`, the drag will be prevented.\n\nReturns a function to remove the listener.\n\nThis event allows overriding the drag behavior of the Colors and Tools windows in JS Paint.\n\n#### `onTitleChange(listener)`\n\nCalls the listener when the window's title changes.\n\nReturns a function to remove the listener.\n\nThis event can be used to update a taskbar button's label.\n\n#### `onIconChange(listener)`\n\nCalls the listener when the window's icon changes.\n\nReturns a function to remove the listener.\n\nThis event can be used to update a taskbar button's icon.\nUse `$window.getIconAtSize(size)` to get an appropriate icon.\n\n#### `closed`\n\nWhether the window has been closed.\n\n#### `icons`\n\nThe icons of the window at different sizes, as set by `options.icons` or `setIcons()`.\n\n#### `elements`\n\nAn object containing references to the window's elements.\n\n##### `content` (HTMLElement)\n\nThe window's content area.\n\n##### `titlebar` (HTMLElement)\n\nThe window's titlebar, including the title, window buttons, and possibly an icon.\n\n##### ` _title_area` (HTMLElement)\n\nA wrapper element around the title.\n\nPRIVATE: Don't use this. Use `elements.titlebar` or `elements.title` instead, if possible.\n\n##### `title` (HTMLElement)\n\nThe window's title.\n\n##### `closeButton` (HTMLButtonElement)\n\nThe window's close button.\n\n##### `minimizeButton` (HTMLButtonElement)\n\nThe window's minimize button.\n\n##### `maximizeButton` (HTMLButtonElement)\n\nThe window's maximize button.\n\n\n#### `$content`\n\n*jQuery object.*  \nWhere you can append contents to the window.\n\n#### `$titlebar`\n\n*jQuery object.*  \nThe titlebar of the window, including the title, window buttons, and possibly an icon.\n\n#### `$title_area`\n\nPRIVATE: Don't use this. Use `$title` or `$titlebar` instead, if possible.\n\n*jQuery object.*  \nWrapper around the title.\n\n#### `$title`\n\n*jQuery object.*  \nThe title portion of the titlebar.\n\n#### `$x`\n\n*jQuery object.*  \nThe close button.\n\n#### `$minimize`\n\n*jQuery object.*  \nThe minimize button.\n\n#### `$maximize`\n\n*jQuery object.*  \nThe maximize button.\n\n#### `$icon`\n\nPRIVATE: Don't use this.\n\n*jQuery object.*  \nThe titlebar icon.\n\n#### `element`\n\nThe DOM element that represents the window.\n\n#### Event: `close`\n\nDEPRECATED: Use the `onBeforeClose` method instead.\n\nCan be used to prevent closing a window, with `event.preventDefault()`.\nSince there could be multiple listeners, and another listener could prevent closing, if you want to detect when the window is actually closed, use the `closed` event.\n\n#### Event: `closed`\n\nDEPRECATED: Use the `onClosed` method instead.\n\nThis event is emitted when the window is closed. It cannot be prevented.\n\n#### Event: `window-drag-start`\n\nDEPRECATED: Use the `onBeforeDrag` method instead.\n\nCan be used to prevent dragging a window, with `event.preventDefault()`.\n\n#### Event: `title-change`\n\nDEPRECATED: Use the `onTitleChange` method instead.\n\nCan be used to update a taskbar button's label.\n\n#### Event: `icon-change`\n\nDEPRECATED: Use the `onIconChange` method instead.\n\nCan be used to update a taskbar button's icon.\nUse `$window.getIconAtSize(size)` to get an appropriate icon.\n\n### Positioning Windows\n\nOther than `center()`, there is no API specifically for positioning windows.\n\nYou can use `$($window.element).css({ top: \"500px\", left: \"500px\" })` to set the position of the window with [jQuery's `css()` method](https://api.jquery.com/css/), or else use:\n```js\n$window.element.style.top = \"500px\";\n$window.element.style.left = \"500px\";\n```\n\nYou can also set `position` to `fixed` or `absolute` to position the window relative to the viewport or the nearest positioned ancestor, respectively.\n\nIf you want to position a window relative to another window, you can use `$otherWindow.element.getBoundingClientRect()` to get the bounding rectangle of the other window, and then use that to position the window. This is a [built-in DOM API](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect). For example:\n```js\nconst otherRect = $otherWindow.element.getBoundingClientRect();\n$window.element.top = `${otherRect.top}px`;\n$window.element.left = `${otherRect.right + 10}px`;\n```\n\n#### Notes:\n- Stylesheets can't be used (without `!important`) to position the window, because the library uses inline styles to position the window, which take precedence.\n- If either window has dynamic content, such as images, you should wait for the content to load before measuring and positioning windows. Alternatively, you can make the layout fixed, by specifying sizes for all images/similar, or a fixed size for the window.\n- I may extend `setDimensions()` in the future to allow positioning the window in addition to sizing it, or add a `setPosition()` method.\n- You can pass `options.constrainRect` to dynamically constrain the window position and size during dragging and resizing.\n\n\n### Theming\n\n`parse-theme.js` contains functions for parsing and applying themes.\n\n\n#### `parseThemeFileString(themeString)`\n\nParses an INI file string into CSS properties.\n\nAutomatically renders dynamic theme graphics, and includes them in the CSS properties.\n\n#### `applyCSSProperties(cssProperties, {element=document.documentElement, recurseIntoIframes=false})`\n\n`cssProperties` is an object with CSS properties and values. It can also be a `CSSStyleDeclaration` object.\n\n`element` is the element to apply the properties to.\n\nIf `recurseIntoIframes` is true, then the properties will be applied to all `\u003ciframe\u003e` elements within the element as well.\nThis only works with same-origin iframes.\n\n#### `renderThemeGraphics(cssProperties)`\n\nCan be used to update theme graphics (scrollbar icons, etc.) for a specific section of the page. Used by the demo to show variations.\n\nReturns CSS properties representing the rendered theme graphics.\n\n```js\nelement.style.setProperty('--scrollbar-size', '30px');\napplyCSSProperties(renderThemeGraphics(getComputedStyle(element)), { element });\n```\n\n#### `makeThemeCSSFile(cssProperties)`\n\nExports a CSS file for a theme. Assumes that the theme graphics are already rendered.\nIncludes a \"generated file\" comment.\n\n#### `makeBlackToInsetFilter()`\n\nInitializes an SVG filter that can be used to make icons appear disabled.\nIt may not work with all icons, since it uses the black parts of the image to form a shape.\n\nUsage from CSS:\n```css\nbutton:disabled .icon {\n\tfilter: saturate(0%) opacity(50%); /* fallback until SVG filter is initialized */\n\tfilter: url(\"#os-gui-black-to-inset-filter\");\n}\n```\n\n## License\n\nLicensed under the [MIT License](https://opensource.org/licenses/MIT), see [LICENSE](LICENSE) for details.\n\n## Development\n\nInstall [Node.js](https://nodejs.org/) if you don't already have it.\n\nClone the repository, then in the project directory run `npm i` to install the dependencies.\nAlso run `npm i` when pulling in changes from the repository, in case there are changes to the dependencies.\n\nRun `npm start` to open a development server. It will open a demo page in your default browser. Changes to the library will be automatically recompiled, and the page will automatically reload.\n\nRun `npm run lint` to run type checking and spell checking (and any other linting tasks).\n\nRun `npm test` to run the tests.\nThis also saves coverage reports to the `coverage` directory,\nbut note that it only records code covered by unit tests,\ni.e. code imported directly into the tests, not code loaded in the page context,\nas this requires further setup for instrumentation.\n\nIt's a good idea to close the server when updating or installing dependencies; otherwise you may run into EPERM issues.\n\nThe styles are written with [PostCSS](https://postcss.org/), for mixins and other transforms.  \nRecommended: install a PostCSS language plugin for your editor, like [PostCSS Language Support](https://marketplace.visualstudio.com/items?itemName=csstools.postcss) for VS Code.\n\nCurrently there's some CSS that has to manually be regenerated in-browser and copied into theme-specific CSS files.  \nIn the future this could be done with a custom PostCSS syntax parser for .theme/.themepack files, and maybe SVG instead of any raster graphics to avoid needing `node-canvas` (native dependencies are a pain). Or maybe UPNG.js and plain pixel manipulation.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F1j01%2Fos-gui","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F1j01%2Fos-gui","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F1j01%2Fos-gui/lists"}