[Rust] Alert not displayed when minor version is outdated

**Describe the bug**
```
[dependencies]
uuid = "1.4"
```
Last version is 1.10.0, but no alert is displayed (green mark is shown).

**To Reproduce**
See above.

**Expected behavior**
Expected behavior: alert shown (red icon) for minor version (X.Y) number mismatch.

**Screenshots**
Minor version mismatch isn't detected as shown on screenshot below:
<image>

**Desktop (please complete the following information):**
Windows 10

**Additional context**
Visual Studio Code v1.91.1 (current)
Dependi v0.7.5 (current)

diff --git a/vscode/src/core/parsers/TomlParser.ts b/vscode/src/core/parsers/TomlParser.ts
index d586def..bde3ed2 100644
--- a/vscode/src/core/parsers/TomlParser.ts
+++ b/vscode/src/core/parsers/TomlParser.ts
@@ -138,9 +138,7 @@ export class TomlParser implements Parser {
     const commentIndex = line.indexOf("#");
     item.key = clearText(line.substring(0, eqIndex));
     item.key = item.key.replace(".version", "");
-    item.value = clearText(
-      line.substring(eqIndex + 1, commentIndex > -1 ? commentIndex : line.length)
-    );
+    item.value = line.substring(eqIndex + 1, commentIndex > -1 ? commentIndex : line.length).trim().replace(/^"|"$|'/g, '');

     if (isBoolean(item.value) || item.value.includes("path")) {
       return undefined;
@@ -206,7 +204,7 @@ function parseVersionValue(line: string, item: Item) {
   if (isBoolean(found)) {
     return;
   }
-  item.value = clearText(found);
+  item.value = found;
   item.start = foundAt;
   item.end = item.start + item.value.length;
 }


`TWMConfig["twMergeConfig"]` does not allow partial configurations

<!--

If your issue doesn't follow the templates provided, it will be closed without
comment – no exceptions.

For feature requests, please create a "Discussion" with the category "Ideas":
https://github.com/jrgarciadev/tailwind-variants/discussions/new

-->

**Describe the bug**

Since the use of `TWMConfig["twMergeConfig"]` is to be passed to `extendTailwindMerge` that takes configs partially, the type of `TWMConfig["twMergeConfig"]` should be also partial.

```js
const twMerge = !isEmptyObject(config.twMergeConfig)
      ? extendTailwindMerge(config.twMergeConfig)
      : twMergeBase;
```
https://github.com/nextui-org/tailwind-variants/blob/main/src/index.js#LL29C24-L29C24

```js
export function extendTailwindMerge(
    configExtension: Partial<Config> | CreateConfigSubsequent,
    ...createConfig: CreateConfigSubsequent[]
) {
    return typeof configExtension === 'function'
        ? createTailwindMerge(getDefaultConfig, configExtension, ...createConfig)
        : createTailwindMerge(
              () => mergeConfigs(getDefaultConfig(), configExtension),
              ...createConfig,
          )
}
```
https://github.com/dcastil/tailwind-merge/blob/main/src/lib/extend-tailwind-merge.ts#LL8C1-L18C2

```ts
export type TWMConfig = {
  /**
   * Whether to merge the class names with `tailwind-merge` library.
   * It's avoid to have duplicate tailwind classes. (Recommended)
   * @see https://github.com/dcastil/tailwind-merge/blob/v1.8.1/README.md
   * @default true
   */
  twMerge?: boolean;
  /**
   * The config object for `tailwind-merge` library.
   * @see https://github.com/dcastil/tailwind-merge/blob/v1.8.1/docs/configuration.md
   */
  twMergeConfig?: TwMergeConfig;
};
```
https://github.com/nextui-org/tailwind-variants/blob/main/src/config.d.ts#L20

**To Reproduce**
Steps to reproduce the behavior:

1. Install the package
2. Pass `tv` an empty configuration object as shown below:
```js
tv(options, {
    ...config,
    twMerge: true,
    twMergeConfig: {},
  });
```
3. Now you reproduced the type error.

**Expected behavior**

`TWMConfig["twMergeConfig"]` should be typed partially.

```ts
export type TWMConfig = {
  /**
   * Whether to merge the class names with `tailwind-merge` library.
   * It's avoid to have duplicate tailwind classes. (Recommended)
   * @see https://github.com/dcastil/tailwind-merge/blob/v1.8.1/README.md
   * @default true
   */
  twMerge?: boolean;
  /**
   * The config object for `tailwind-merge` library.
   * @see https://github.com/dcastil/tailwind-merge/blob/v1.8.1/docs/configuration.md
   */
  twMergeConfig?: Partial<TwMergeConfig>;
};
```

**Screenshots**
If applicable, add screenshots to help explain your problem.
<image>

**Desktop (please complete the following information):**

All desktop environments

**Smartphone (please complete the following information):**

All smartphone environments

**Additional context**
None

diff --git a/src/index.js b/src/index.js
index 0d95543..abe2875 100644
--- a/src/index.js
+++ b/src/index.js
@@ -20,7 +20,7 @@ export const cnBase = (...classes) => voidEmpty(classes.flat(Infinity).filter(Bo

 export const cn =
   (...classes) =>
-  (config = defaultConfig) => {
+  (config) => {
     if (!config.twMerge) {
       return cnBase(classes);
     }
@@ -46,7 +46,7 @@ const joinObjects = (obj1, obj2) => {
   return mergedObj;
 };

-export const tv = (options, config = defaultConfig) => {
+export const tv = (options, configProp) => {
   const {
     slots: slotProps = {},
     variants: variantsProps = {},
@@ -55,6 +55,8 @@ export const tv = (options, config = defaultConfig) => {
     defaultVariants: defaultVariantsProps = {},
   } = options;

+  const config = Object.assign({}, defaultConfig, configProp);
+
   const base = cnBase(options?.extend?.base, options?.base);
   const variants = mergeObjects(variantsProps, options?.extend?.variants);
   const defaultVariants = Object.assign({}, options?.extend?.defaultVariants, defaultVariantsProps);


Produced image/snap doesn't show replies, RT's and likes... instead shows "Read 10.1k replies"

## Bug description
<!--
  What did you do? (Provide code in next section)

  What did you expect to happen?

  What happened instead?

  Do you have an error stack-trace?
-->
  What did you do? (Provide code in next section)
Took a snap of a tweet of one of the built-in examples. @jack's tweet #20.

  What did you expect to happen?
For the snap to appear as in the READ.ME on the git page.

  What happened instead?
The program executes correctly on the CLI.
But, in the produced snap, instead of there showing an <hr> separator and then the replies, rt's and likes.. there's a button that says Read XX replies.

  Do you have an error stack-trace?
Don't know what this is, but I'll get it if it can be explained to me :)

## Reproduction
<!--
  Provide one of the following:
  1. A code-snippet that reproduces the issue
  2. A reproduction repo that reproduces the issue
  3. A PR with a failing test-case

  Remove irrelevant code to make it easier for others to read and debug.

  -- Why?
  The goal is to maximize communication efficiency.

  When an issue is immediately reproducible, others can start debugging instead of following-up with questions.
-->
  Provide one of the following:
  1. snap-tweet https://twitter.com/jack/status/20
  2. ??
  3. Sharing snap instead:

<image>
FYI I am not logged in to twitter on the browser, but I doubt that has any effect.

## Environment

- snap-tweet version: snap-tweet/1.2.1 win32-x64 node-v16.14.0
- Operating System: Microsoft Windows 10 Home 10.0.19044 N/A Build 19044
- Node version: v16.14.0
- Package manager (npm/yarn/pnpm) and version: npm 8.5.0

diff --git a/src/tweet-camera.ts b/src/tweet-camera.ts
index cd7c690..c587006 100644
--- a/src/tweet-camera.ts
+++ b/src/tweet-camera.ts
@@ -4,7 +4,6 @@ import CDP from 'chrome-remote-interface';
 import exitHook from 'exit-hook';
 import {
 	querySelector,
-	xpath,
 	waitForNetworkIdle,
 	hideNode,
 	screenshotNode,
@@ -157,16 +156,31 @@ class TweetCamera {
 		const { root } = await client.DOM.getDocument();
 		const tweetContainerNodeId = await querySelector(client.DOM, root.nodeId, '#app > div > div > div:last-child');

-		const [tweetLinkNodeId] = await xpath(client.DOM, '//a[starts-with(@href, "https://twitter.com/intent/tweet")]/..');
-
 		await Promise.all([
 			// "Copy link to Tweet" button
-			hideNode(client.DOM, await querySelector(client.DOM, tweetContainerNodeId, '[role="button"]')),
+			hideNode(
+				client.DOM,
+				await querySelector(client.DOM, tweetContainerNodeId, '[role="button"]'),
+			),

 			// Info button - can't use aria-label because of i18n
-			hideNode(client.DOM, await querySelector(client.DOM, tweetContainerNodeId, 'a[href$="twitter-for-websites-ads-info-and-privacy"]')),
-
-			client.DOM.removeNode({ nodeId: tweetLinkNodeId }),
+			hideNode(
+				client.DOM,
+				await querySelector(
+					client.DOM,
+					tweetContainerNodeId,
+					'a[href$="twitter-for-websites-ads-info-and-privacy"]',
+				),
+			),
+
+			// Remove the "Read 10K replies" button
+			client.DOM.removeNode({
+				nodeId: await querySelector(
+					client.DOM,
+					tweetContainerNodeId,
+					'.css-1dbjc4n.r-kzbkwu.r-1h8ys4a',
+				),
+			}),

 			// Unset max-width to fill window width
 			client.DOM.setAttributeValue({


<!--

If your issue doesn't follow the templates provided, it will be closed without
comment – no exceptions.

For feature requests, please create a "Discussion" with the category "Ideas":
https://github.com/jrgarciadev/tailwind-variants/discussions/new

-->

**Describe the bug**
When using the VariantProps generic all of the returned types are `any` instead of being literals.

**To Reproduce**
1. Create a basic `tv` function in Typescript that includes variants.
```ts
const circle = tv({
  variants: {
    size: {
      md: ["w-[580px] h-[580px]"],
      lg: ["w-[1280px] h-[1280px]"],
    },
    contrast: {
      low: ["bg-gray-100", "dark:bg-purple-900"],
      high: ["bg-gray-200", "dark:bg-purple-800"],
    },
  },
  defaultVariants: {
    size: "md",
    contrast: "high",
  },
})
```
2. Import and use the VariantProps generic
```ts
type CircleType = VariantProps<typeof circle>
```
3. Check intellisense on either type, or component, and see `any`
<image>
<image>

**Expected behavior**
variant props to be literals. ie: `size: 'md' | 'lg'`

**Screenshots**
Screenshots are added above in description

**Desktop (please complete the following information):**

- OS: Mac 12.6.6
- Browser: Not applicable
- Typescript Version: 5.1

**Smartphone (please complete the following information):**

- Device: n/a
- OS: n/a
- Browser: n/a
- Version: n/a

**Additional context**
The actual `tv` function isn't strongly typed either. I can add whatever I want to `defaultVariants` whether its a variant or not.

diff --git a/src/index.d.ts b/src/index.d.ts
index 1cd9ec4..62f918d 100644
--- a/src/index.d.ts
+++ b/src/index.d.ts
@@ -267,7 +267,8 @@ export declare const tv: TV;

 export declare const defaultConfig: TVConfig;

-export type VariantProps<Component extends (...args: any) => any> = Omit<
-  OmitUndefined<Parameters<Component>[0]>,
-  "class" | "className"
->;
+export type VariantProps<T> = T extends {variants: infer V}
+  ? V extends TVVariantsDefault<any, undefined>
+    ? {[K in TVVariantKeys<V, any>[number]]?: keyof V[K]}
+    : never
+  : never;


**Version (please complete the following information):**
 - SRO version: Vietnam
 - RSBot version: 2.5.1

**Describe the bug**
South Alexandria town loop doesn't work if you wear low durability item, and you have "back to town if durability low" ticked on

Bot will think it's outside town on its way to storage and use recall scroll

**To reproduce the behavior:**
Steps to reproduce the behavior:
1. Set south Alexandria as your recall point
2. Activate "back to town if durability low" in protection tab
3. Fight monsters till your durability is low
4. Bot will automatically recall to town and activate town loop
5. On the way to storage NPC bot will think it's outside town and recall again

**(Optional) Screenshots**
It starts to recall around here <image>


## Description of the issue
There occured a couple of warnings in the [building routine](https://github.com/OpenEnergyPlatform/ontology/actions/runs/9172004175) of the latest release.

![grafik](https://github.com/OpenEnergyPlatform/ontology/assets/38690039/7a6be180-abb6-4490-a379-0f89d4b52042)

ToDo: check if there is a need to update the routines.

## Ideas of solution

If you already have ideas for the solution describe them here

## Workflow checklist

- [ ] I am aware of the [workflow](https://github.com/OpenEnergyPlatform/ontology/blob/dev/CONTRIBUTING.md) for this repository

diff --git a/.github/workflows/automated-testing.yml b/.github/workflows/automated-testing.yml
index 9e10fed78..d29d1a2c4 100644
--- a/.github/workflows/automated-testing.yml
+++ b/.github/workflows/automated-testing.yml
@@ -4,8 +4,8 @@ jobs:
   build:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v2
-      - uses: actions/setup-java@v2
+      - uses: actions/checkout@v4
+      - uses: actions/setup-java@v4
         with:
           distribution: 'adopt'
           java-version: '11'
@@ -19,7 +19,7 @@ jobs:
       - name: verify
         run: |
           java -jar build/robot.jar verify --input build/oeo/$(cat VERSION)/oeo-full.owl --queries tests/verify/*
-      - uses: actions/upload-artifact@master
+      - uses: actions/upload-artifact@v4
         with:
           name: build-artifacts
           path: build/oeo
@@ -27,16 +27,16 @@ jobs:
     runs-on: ubuntu-latest
     needs: build
     steps:
-      - uses: actions/checkout@v2
+      - uses: actions/checkout@v4
       - uses: actions/setup-python@v4
         with:
           python-version: '3.7.17'
           architecture: x64
-      - uses: actions/setup-java@v2
+      - uses: actions/setup-java@v4
         with:
           distribution: 'adopt'
           java-version: '11'
-      - uses: actions/download-artifact@master
+      - uses: actions/download-artifact@v4
         with:
           name: build-artifacts
           path: build/oeo
@@ -68,7 +68,7 @@ jobs:
           exit 1
       - name: Upload Ontology
         if: always()
-        uses: actions/upload-artifact@v3
+        uses: actions/upload-artifact@v4
         with:
           name: build-files
           path: |
@@ -82,7 +82,7 @@ jobs:
           # bash tests/competency_questions/run_questions.sh "java -jar build/hermit.jar" $(pwd)/build/oeo/$(cat VERSION)/oeo-full.owl false
       - name: Upload Artifacts
         if: always()
-        uses: actions/upload-artifact@v3
+        uses: actions/upload-artifact@v4
         with:
           name: test-report
           path: build/report.json
@@ -94,7 +94,7 @@ jobs:
           BRANCH_NAME="${PATHS[1]}_${PATHS[2]}"
           echo "BRANCH=$(echo ${BRANCH_NAME})" >> $GITHUB_ENV
       - name: Coverage Badge
-        uses: schneegans/dynamic-badges-action@v1.0.0
+        uses: schneegans/dynamic-badges-action@v1.7.0
         with:
           auth: ${{ secrets.GIST_SECRET }}
           gistID: 6d00affa9fbc89c79684d62091d96551


if a `uBit.sleep()` is used followed by `target_wait_us()` the delays do not match

I expect the 4ms resolution of `uBit.sleep()` as that is the ticker configuration, however it'd expect `target_wait_us()` to not have this limitation as it spin waits using the timer peripheral.

To test via pin toggle with a logic analyser we can first confirm that `target_wait_us()` works fine on its own:

```cpp
while (true) {
    uBit.io.P15.setDigitalValue(1);
    target_wait_us(4000);
    uBit.io.P15.setDigitalValue(0);
    target_wait_us(5000);
    uBit.io.P15.setDigitalValue(1);
    target_wait_us(6000);
    uBit.io.P15.setDigitalValue(0);
    target_wait_us(7000);
    uBit.io.P15.setDigitalValue(1);
    target_wait_us(8000);
    uBit.io.P15.setDigitalValue(0);
    target_wait_us(9000);
}
```

Ignoring the inaccuracy of `target_wait_us` (4ms delay is measured as 3.85ms), we can clearly see the delay increasing proportionaly:
<image>

Adding a `uBit.sleep(4)`, as that is minimum delay due to the 4ms ticket, with each `target_wait_us()` and the results change (measurement results added in the comments of this snippet):
```cpp
while (true) {
    uBit.io.P15.setDigitalValue(1);
    // This delay is 7.85ms -> looks "fine", similar inaccuracy as the previous test
    uBit.sleep(4);
    target_wait_us(4000);
    uBit.io.P15.setDigitalValue(0);
    // This delay is 8.95ms -> this looks good too
    uBit.sleep(4);
    target_wait_us(5000);
    uBit.io.P15.setDigitalValue(1);
    // This delay is ~13ms ❌
    uBit.sleep(4);
    target_wait_us(6000);
    uBit.io.P15.setDigitalValue(0);
    // This delay is ~13ms ❌
    uBit.sleep(4);
    target_wait_us(7000);
    uBit.io.P15.setDigitalValue(1);
    // This delay is  ~13ms ❌
    uBit.sleep(4);
    target_wait_us(8000);
    uBit.io.P15.setDigitalValue(0);
    // This delay is  ~13ms -> I guess this is finally correct
    uBit.sleep(4);
    target_wait_us(9000);
    uBit.io.P15.setDigitalValue(1);
    // This delay is ~17ms ❌
    uBit.sleep(4);
    target_wait_us(10000);

    // Stay low for 40ms to easily find the start of the next iteration
    uBit.io.P15.setDigitalValue(0);
    uBit.sleep(40);
}
```

<image>

diff --git a/model/MicroBit.h b/model/MicroBit.h
index f1106ebf..089d585e 100644
--- a/model/MicroBit.h
+++ b/model/MicroBit.h
@@ -215,8 +215,13 @@ namespace codal
              *
              * @param milliseconds the amount of time, in ms, to wait for. This number cannot be negative.
              *
-             * @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER milliseconds is less than zero.
+             * @code
+             * uBit.sleep(20); //sleep for 20ms
+             * @endcode
              *
+             * @note This operation is currently limited by the rate of the system timer, therefore
+             *       the granularity of the sleep operation is limited to 4 ms, unless the rate of
+             *       the system timer is modified.
              */
             void sleep(uint32_t milliseconds);

@@ -289,8 +294,13 @@ namespace codal
      *
      * @param milliseconds the amount of time, in ms, to wait for. This number cannot be negative.
      *
-     * @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER milliseconds is less than zero.
+     * @code
+     * uBit.sleep(20); //sleep for 20ms
+     * @endcode
      *
+     * @note This operation is currently limited by the rate of the system timer, therefore
+     *       the granularity of the sleep operation is limited to 4 ms, unless the rate of
+     *       the system timer is modified.
      */
     inline void MicroBit::sleep(uint32_t milliseconds)
     {


Hi,
I really like the sfepy docs, but it seems that the search function is broken.
Could you please look into this?
E.g. when searching https://sfepy.org/doc-devel/search.html?q=neumann&check_keywords=yes&area=default

I see a problem in the Javascript console related to jQuery:
<image>

Thank you for your help!
Regards,
Florian

diff --git a/doc/conf.py b/doc/conf.py
index 415d676a3..e5fc94e8d 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -43,10 +43,16 @@

 # Add any Sphinx extension module names here, as strings. They can be extensions
 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
-extensions = ['sphinx.ext.autosummary', 'sphinx.ext.autodoc',
-              'sphinx.ext.doctest', 'sphinx.ext.imgmath',
-              'sphinx.ext.viewcode', 'numpydoc',
-              'gen_field_table', 'gen_term_table', 'gen_solver_table',
+extensions = ['sphinx.ext.autosummary',
+              'sphinx.ext.autodoc',
+              'sphinx.ext.doctest',
+              'sphinx.ext.imgmath',
+              'sphinx.ext.viewcode',
+              'sphinxcontrib.jquery',
+              'numpydoc',
+              'gen_field_table',
+              'gen_term_table',
+              'gen_solver_table',
               'IPython.sphinxext.ipython_console_highlighting',
               'IPython.sphinxext.ipython_directive',
 ]
diff --git a/doc/developer_guide.rst b/doc/developer_guide.rst
index a76245818..f134b153e 100644
--- a/doc/developer_guide.rst
+++ b/doc/developer_guide.rst
@@ -519,7 +519,8 @@ How to Regenerate Documentation

 The following steps summarize how to regenerate this documentation.

-#. Install `sphinx`_ and `numpydoc`_. Do not forget to set the path to numpydoc
+#. Install `sphinx`_, `numpydoc`_, `sphinx-rtd-theme`_ and `IPython`_.
+   Do not forget to set the path to numpydoc
    in site_cfg.py if it is not installed in a standard location for Python
    packages on your platform. A recent :math:`\mbox{\LaTeX}` distribution is
    required, too, for example `TeX Live`_. Depending on your OS/platform, it


### Browser

Google Chrome

### Browser Version

Version 106.0.5249.103 (Official Build) (64-bit)

### Extension or Userscript?

Extension

### Extension/Userscript Version

3.0.0.6

### Video link where you see the problem

https://www.youtube.com/watch?v=AmekhRy8f7E

### What happened?

YouTube Changed their UI, and it now causes a few graphical interferences with most of the customisations in the plugin.

### How to reproduce/recreate?

1. Click on a video.
2. Scroll down.

![SharedScreenshot](https://user-images.githubusercontent.com/101042481/195048714-49badb2f-9dfb-41f3-949e-689c7f7f5491.jpg)

### Will you be available for follow-up questions to help developers diagnose & fix the issue?

Yes

diff --git a/Extensions/combined/content-style.css b/Extensions/combined/content-style.css
index d8da7db1e..4e53f87fe 100644
--- a/Extensions/combined/content-style.css
+++ b/Extensions/combined/content-style.css
@@ -30,12 +30,21 @@
 }

 .ryd-tooltip {
-  position: relative;
   display: block;
   height: 2px;
+}
+
+.ryd-tooltip-old-design {
+  position: relative;
   top: 9px;
 }

+.ryd-tooltip-new-design {
+  position: absolute;
+  bottom: -10px;
+  left: -4px;
+}
+
 .ryd-tooltip-bar-container {
   width: 100%;
   height: 2px;
@@ -44,3 +53,12 @@
   padding-bottom: 12px;
   top: -6px;
 }
+
+/* required to make the ratio bar visible in the new design */
+ytd-menu-renderer.ytd-watch-metadata {
+  overflow-y: visible !important;
+}
+
+#top-level-buttons-computed {
+  position: relative !important;
+}
diff --git a/Extensions/combined/src/bar.js b/Extensions/combined/src/bar.js
index 930f0f525..ebf9ad8fe 100644
--- a/Extensions/combined/src/bar.js
+++ b/Extensions/combined/src/bar.js
@@ -57,14 +57,12 @@ function createRateBar(likes, dislikes) {

         (
           document.getElementById(
-            isNewDesign() ? "actions-inner" : "menu-container"
+            isNewDesign() ? "top-level-buttons-computed" : "menu-container"
           ) || document.querySelector("ytm-slim-video-action-bar-renderer")
         ).insertAdjacentHTML(
           "beforeend",
           `
-

+

                 <div
                     id="ryd-bar-container"


> The influence radius is not quite right in Anno 1404.
The influence area of Base of Operations (3x3, 18 Range) is 4 tiles larger in the program than in the game
And in the program you can place 6 houses along the Base of Operations in one direction but in game you can only place 5, the sixth one will be out of range. Thats the real problem

<image>

Reported by Lywzc on Discord

diff --git a/AnnoDesigner/Models/LayoutObject.cs b/AnnoDesigner/Models/LayoutObject.cs
index b4aebd03..f3c3c46d 100644
--- a/AnnoDesigner/Models/LayoutObject.cs
+++ b/AnnoDesigner/Models/LayoutObject.cs
@@ -374,8 +374,19 @@ public double GetScreenRadius(int gridSize)
         {
             if (_screenRadius == default || _lastGridSizeForScreenRadius != gridSize)
             {
-                _screenRadius = _coordinateHelper.GridToScreen(WrappedAnnoObject.Radius, gridSize);
+                // Buildings of an odd-numbered size (3x3/3x5 etc) draw their circular influence range with an additional +0.5,
+                // this is not correct and needs to be adjusted to produce the correct radius for those buildings.
+                // See https://github.com/AnnoDesigner/anno-designer/issues/299 for an explanation of the issue.

+                // check if Object Width and Height are odd numbers or not, if both are, adjust the circle size with -0.1
+                if ((WrappedAnnoObject.Size.Width %2 != 0 ) && (WrappedAnnoObject.Size.Height %2 != 0))
+                {
+                    _screenRadius = _coordinateHelper.GridToScreen(WrappedAnnoObject.Radius - 0.1, gridSize);
+                }
+                else
+                {
+                    _screenRadius = _coordinateHelper.GridToScreen(WrappedAnnoObject.Radius, gridSize);
+                }
                 _lastGridSizeForScreenRadius = gridSize;
             }

@@ -436,7 +447,7 @@ public Rect GridInfluenceRangeRect
             {
                 if (_gridInfluenceRangeRect == default)
                 {
-                    if (WrappedAnnoObject.InfluenceRange == 0)
+                    if (WrappedAnnoObject.InfluenceRange <= 0)
                     {
                         _gridInfluenceRangeRect = new Rect(Position, default(Size));
                     }
diff --git a/Tests/AnnoDesigner.Tests/LayoutObjectTests.cs b/Tests/AnnoDesigner.Tests/LayoutObjectTests.cs
new file mode 100644
index 00000000..d0d44a03
--- /dev/null
+++ b/Tests/AnnoDesigner.Tests/LayoutObjectTests.cs
@@ -0,0 +1,129 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using AnnoDesigner.Core.Models;
+using AnnoDesigner.Helper;
+using AnnoDesigner.Models;
+using Xunit;
+
+namespace AnnoDesigner.Tests
+{
+    public class LayoutObjectTests
+    {
+        private static readonly ICoordinateHelper coordinateHelper;
+
+        static LayoutObjectTests()
+        {
+            coordinateHelper = new CoordinateHelper();
+        }
+
+        #region GridInfluenceRangeRect tests
+
+        [Theory]
+        [InlineData(0)]
+        [InlineData(-1)]
+        [InlineData(-0.001)]
+        public void GridInfluenceRangeRect_InfluenceRangeIsZeroOrNegative_ShouldReturnEmptyRect(double influenceRangeToSet)
+        {
+            // Arrange
+            var annoObject = new AnnoObject
+            {
+                InfluenceRange = influenceRangeToSet,
+                Size = new Size(10, 10),
+                Position = new Point(42, 42)
+            };
+            var layoutObject = new LayoutObject(annoObject, null, null, null);
+
+            // Act
+            var influenceRangeRect = layoutObject.GridInfluenceRangeRect;
+
+            // Assert
+            Assert.Equal(annoObject.Position, influenceRangeRect.Location);
+            Assert.Equal(default(Size), influenceRangeRect.Size);
+        }
+
+        #endregion
+
+        #region GetScreenRadius tests
+
+        [Theory]
+        [InlineData(5, 5, 99)]
+        [InlineData(3, 3, 99)]
+        [InlineData(5.5, 5.5, 99)]
+        public void GetScreenRadius_SizeHeightAndWidthAreOdd_ShouldAdjustRadius(double widthToSet, double heightToSet, double expectedRadius)
+        {
+            // Arrange
+            var annoObject = new AnnoObject
+            {
+                Size = new Size(widthToSet, heightToSet),
+                Radius = 10
+            };
+            var layoutObject = new LayoutObject(annoObject, coordinateHelper, null, null);
+
+            // Act
+            var screenRadius = layoutObject.GetScreenRadius(10);
+
+            // Assert
+            Assert.Equal(expectedRadius, screenRadius);
+        }
+
+        [Fact]
+        public void GetScreenRadius_SizeHeightIsOdd_ShouldNotAdjustRadius()
+        {
+            // Arrange
+            var annoObject = new AnnoObject
+            {
+                Size = new Size(8, 5),
+                Radius = 10
+            };
+            var layoutObject = new LayoutObject(annoObject, coordinateHelper, null, null);
+
+            // Act
+            var screenRadius = layoutObject.GetScreenRadius(10);
+
+            // Assert
+            Assert.Equal(100, screenRadius);
+        }
+
+        [Fact]
+        public void GetScreenRadius_SizeWidthIsOdd_ShouldNotAdjustRadius()
+        {
+            // Arrange
+            var annoObject = new AnnoObject
+            {
+                Size = new Size(5, 8),
+                Radius = 10
+            };
+            var layoutObject = new LayoutObject(annoObject, coordinateHelper, null, null);
+
+            // Act
+            var screenRadius = layoutObject.GetScreenRadius(10);
+
+            // Assert
+            Assert.Equal(100, screenRadius);
+        }
+
+        [Fact]
+        public void GetScreenRadius_NeitherSizeWidthNorHeightIsOdd_ShouldNotAdjustRadius()
+        {
+            // Arrange
+            var annoObject = new AnnoObject
+            {
+                Size = new Size(8, 8),
+                Radius = 10
+            };
+            var layoutObject = new LayoutObject(annoObject, coordinateHelper, null, null);
+
+            // Act
+            var screenRadius = layoutObject.GetScreenRadius(10);
+
+            // Assert
+            Assert.Equal(100, screenRadius);
+        }
+
+        #endregion
+    }
+}


Add an option for adding subfloat separators

Currently, LaTeX code generated for sub floats places them one by one without separation. This results in adjacent images, as well as captions, in the output. It would be very handy to have an option to specify separator. One could write something like `fig.subsep = "\\hfill"`, what would result with something like
``` tex
\subfloat{\includegraphics[width=0.49\textwidth]{…} }
\hfill % here, the separator
\subfloat{\includegraphics[width=0.49\textwidth]{…} }
```
Here is an extreme illustration of the problem with the `out.width` set to 30 percent.
<image>
It would also be useful to specify separators not only in between the sub figures, but also before and after the first one and the last one respectively. This would allow more flexible layouts and centering sub figures in columns. From the interface perspactive, perhaps this could be achieved by passing a vector of separators. Eg. `fig.subsep=c("\\hspace {1 cm}", "\\hfill", "\\hspace {1 cm}")`.
<!--
Please keep the below portion in your issue. Your issue will be closed if any of the boxes is not checked (i.e., replace `[ ]` by `[x]`). In certain (rare) cases, you may be exempted if you give a brief explanation (e.g., you are only making a suggestion for improvement). Thanks!
-->

---

By filing an issue to this repo, I promise that

- [x] I have fully read the issue guide at https://yihui.org/issue/.
- [x] I have provided the necessary information about my issue.
    - If I'm asking a question, I have already asked it on Stack Overflow or RStudio Community, waited for at least 24 hours, and included a link to my question there.
    - If I'm filing a bug report, I have included a minimal, self-contained, and reproducible example, and have also included `xfun::session_info('knitr')`. I have upgraded all my packages to their latest versions (e.g., R, RStudio, and R packages), and also tried the development version: `remotes::install_github('yihui/knitr')`.
    - If I have posted the same issue elsewhere, I have also mentioned it in this issue.
- [x] I have learned the Github Markdown syntax, and formatted my issue correctly.

I understand that my issue may be closed if I don't fulfill my promises.

diff --git a/NEWS.md b/NEWS.md
index abe810d1ad..c1865e1141 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,5 +1,9 @@
 # CHANGES IN knitr VERSION 1.40

+## NEW FEATURES
+
+- Per suggestion of @jakubkaczor, a new chunk option called `fig.subsep` was added (thanks, @pedropark99, #2140). This option is focused on improving LaTeX sub-figures arrangement (see LaTeX [sub-figures section in _R Markdown Cookbook_](https://bookdown.org/yihui/rmarkdown-cookbook/latex-subfigure.html)). This option is available only for PDF output and will be ignored in other output formats. With this option, you can add LaTeX commands between (and around) sub-figures.
+
 ## MINOR CHANGES

 - When the inline R code cannot be correctly parsed, the error message will show the original code in addition to the parsing error, which can make it easier to identify the code error in the source document (thanks, @AlbertLei, #2141).
diff --git a/R/hooks-latex.R b/R/hooks-latex.R
index eb8a728f06..5b7168ba22 100644
--- a/R/hooks-latex.R
+++ b/R/hooks-latex.R
@@ -156,6 +156,34 @@ hook_plot_tex = function(x, options) {
                  sprintf('height=%s', options$out.height),
                  options$out.extra), collapse = ',')

+  # If the chunk have sub-figures, check if we need to add a subfloat separator
+  # between each sub-figure.
+  if (usesub && !is.null(subsep <- options$fig.subsep)) {
+    # User can provide a single separator, or, a vector of multiple separators.
+    # Number of elements in this vector can vary from `fig.num - 1` to `fig.num + 1`.
+    n_subsep = length(subsep)
+    # If the length of `fig.subsep` does not comply with these boundaries, stop the user.
+    if (!n_subsep %in% (c(1L, -1:1 + fig.num))) stop2(
+      "'fig.subsep' should be a single character value, or a character vector ",
+      "with number of elements ranging from ", fig.num - 1, " to ", fig.num + 1,
+      ". But currently 'fig.subsep' has ", n_subsep, " elements.`"
+    )
+    sub11 = sub21 = ''  # strings from subsep to prepend sub1, or append sub2
+    # If `fig.subsep` is a single separator, add it before all plots except first
+    if (n_subsep == 1L) {
+      if (!plot1) sub11 = subsep
+    } else if (n_subsep < fig.num) {
+      # If `fig.num - 1` separators, add i-th separator before (i+1)-th plot:
+      if (!plot1) sub11 = subsep[fig.cur - 1]
+    } else {
+      sub11 = subsep[fig.cur]
+      # If is the last plot in set, add the last separator after it.
+      if (n_subsep > fig.num && plot2) sub21 = subsep[fig.cur + 1L]
+    }
+    if (sub11 != '') sub1 = paste(sub11, sub1, sep = '\n')
+    if (sub21 != '') sub2 = paste(sub2, sub21, sep = '\n')
+  }
+
   paste0(
     fig1, align1, sub1, resize1,
     if (tikz) {


picom --invert-color-include fails when the HOME env is unset

when i run picom in an environment where HOME is unset
then this does not work = the colors of chromium are not inverted

```
picom --invert-color-include 'class_g="Chromium-browser"'
```

fixed by setting HOME to an empty tempdir

picom version 10.2

<details>
<summary>
test-picom.py
</summary>

im calling picom from python, so this looks like

```py
#!/usr/bin/env python3

# required packages: tigervnc picom chromium

import os
import subprocess
import tempfile
import time

vnc_display = ":2"

args = [
    "Xvnc",
    # dont require a password
    "-SecurityTypes", "none",
    "-geometry", "1024x768", # default: 1024x768
    "-depth", "16", # default: 24
    "-FrameRate", "10", # maximum frame rate. default: 60
    "-localhost", # accept connections only from localhost
    vnc_display,
]
proc_xvnc = subprocess.Popen(
    args,
)
time.sleep(1)

args = ["vncviewer", vnc_display]
proc_vncviewer = subprocess.Popen(
    args,
)
time.sleep(1)

# minimal env: only PATH is set
custom_env = {
    "PATH": os.environ["PATH"],
    "DISPLAY": vnc_display,
    # setting HOME is required to fix "picom --invert-color-include"
    #"HOME": tempfile.mkdtemp(suffix="-temp_home"),
}

args = [
    "picom",

    # invert colors of chromium window
    "--invert-color-include", 'class_g="Chromium-browser"',

    # invert colors of all windows, also "save file" dialogs
    #"--invert-color-include", 'name ~= "."',

    # dont invert colors for the "feh" image viewer
    #"--invert-color-include", 'class_g != "feh"',

    # fix "picom --invert-color-include" with unset HOME
    "--config", "/dev/null",
]
proc_picom = subprocess.Popen(
    args,
    env=custom_env,
)
time.sleep(1)

args = ["chromium"]
proc_chromium = subprocess.Popen(
    args,
    env=custom_env,
)
time.sleep(5)

proc_vncviewer.kill()
proc_chromium.kill()
proc_picom.kill()
proc_xvnc.kill()
```

</details>
<image>
<image>
<image>

diff --git a/src/config.c b/src/config.c
index ec39aa4390..f5764c5c39 100644
--- a/src/config.c
+++ b/src/config.c
@@ -596,15 +596,17 @@ char *locate_auxiliary_file(const char *scope, const char *path, const char *inc
 	// Fall back to searching in user config directory
 	scoped_charp picom_scope = mstrjoin("/picom/", scope);
 	scoped_charp config_home = (char *)xdg_config_home();
-	char *ret = locate_auxiliary_file_at(config_home, picom_scope, path);
-	if (ret) {
-		return ret;
+	if (config_home) {
+		char *ret = locate_auxiliary_file_at(config_home, picom_scope, path);
+		if (ret) {
+			return ret;
+		}
 	}

 	// Fall back to searching in system config directory
 	auto config_dirs = xdg_config_dirs();
 	for (int i = 0; config_dirs[i]; i++) {
-		ret = locate_auxiliary_file_at(config_dirs[i], picom_scope, path);
+		char *ret = locate_auxiliary_file_at(config_dirs[i], picom_scope, path);
 		if (ret) {
 			free(config_dirs);
 			return ret;
@@ -612,7 +614,7 @@ char *locate_auxiliary_file(const char *scope, const char *path, const char *inc
 	}
 	free(config_dirs);

-	return ret;
+	return NULL;
 }

 /**
diff --git a/src/config_libconfig.c b/src/config_libconfig.c
index f2a8e07faa..ed2097eb76 100644
--- a/src/config_libconfig.c
+++ b/src/config_libconfig.c
@@ -81,17 +81,19 @@ FILE *open_config_file(const char *cpath, char **ppath) {

 	// First search for config file in user config directory
 	auto config_home = xdg_config_home();
-	auto ret = open_config_file_at(config_home, ppath);
-	free((void *)config_home);
-	if (ret) {
-		return ret;
+	if (config_home) {
+		auto ret = open_config_file_at(config_home, ppath);
+		free((void *)config_home);
+		if (ret) {
+			return ret;
+		}
 	}

 	// Fall back to legacy config file in user home directory
 	const char *home = getenv("HOME");
 	if (home && strlen(home)) {
 		auto path = mstrjoin(home, config_filename_legacy);
-		ret = fopen(path, "r");
+		auto ret = fopen(path, "r");
 		if (ret && ppath) {
 			*ppath = path;
 		} else {
@@ -105,7 +107,7 @@ FILE *open_config_file(const char *cpath, char **ppath) {
 	// Fall back to config file in system config directory
 	auto config_dirs = xdg_config_dirs();
 	for (int i = 0; config_dirs[i]; i++) {
-		ret = open_config_file_at(config_dirs[i], ppath);
+		auto ret = open_config_file_at(config_dirs[i], ppath);
 		if (ret) {
 			free(config_dirs);
 			return ret;

src/config.c:parse_window_shader_prefix(const char *src, const char **end, void *user_data)

src/config_libconfig.c:open_config_file(const char *cpath, char **ppath)


### Current behavior

The color on title bar is misaligned. It should be invisible (non colorized):
<image>

### How to reproduce it (as minimally and precisely as possible)

_No response_

### Expected behavior

The colorized title bar should be smaller.

### Screenshots

_No response_

### Workaround

_No response_

### Affected platforms

Windows

### Affected DevToys kind

DevToys (app with GUI)

### DevToys Version

2.0.1

### Relevant Assets/Logs

_No response_

diff --git a/src/app/dev/platforms/desktop/DevToys.Windows/Controls/MicaWindowWithOverlay.xaml b/src/app/dev/platforms/desktop/DevToys.Windows/Controls/MicaWindowWithOverlay.xaml
index 439302773a..8d42daba8f 100644
--- a/src/app/dev/platforms/desktop/DevToys.Windows/Controls/MicaWindowWithOverlay.xaml
+++ b/src/app/dev/platforms/desktop/DevToys.Windows/Controls/MicaWindowWithOverlay.xaml
@@ -98,6 +98,11 @@
                         <Grid Margin="-1">
                             <AdornerDecorator>
                                 <Grid Margin="{TemplateBinding MarginMaximized}">
+                                    <Border
+                                        x:Name="WindowTopBorder"
+                                        VerticalAlignment="Top"
+                                        Height="48"/>
+
                                     <ContentPresenter />

                                     <controls:OverlayControl
diff --git a/src/app/dev/platforms/desktop/DevToys.Windows/Controls/MicaWindowWithOverlay.xaml.cs b/src/app/dev/platforms/desktop/DevToys.Windows/Controls/MicaWindowWithOverlay.xaml.cs
index f05c9b95c1..0b3b4d9fa2 100644
--- a/src/app/dev/platforms/desktop/DevToys.Windows/Controls/MicaWindowWithOverlay.xaml.cs
+++ b/src/app/dev/platforms/desktop/DevToys.Windows/Controls/MicaWindowWithOverlay.xaml.cs
@@ -11,11 +11,11 @@
 using DevToys.Windows.Core;
 using DevToys.Windows.Core.Helpers;
 using DevToys.Windows.Native;
+using Microsoft.Win32;
 using Windows.Win32;
 using Windows.Win32.Foundation;
 using Windows.Win32.Graphics.Dwm;
 using Windows.Win32.UI.Controls;
-using Windows.Win32.UI.WindowsAndMessaging;
 using Application = System.Windows.Application;
 using Button = System.Windows.Controls.Button;
 using Color = System.Windows.Media.Color;
@@ -34,6 +34,7 @@ public abstract partial class MicaWindowWithOverlay : Window
     private Button? _maximizeButton;
     private Button? _minimizeButton;
     private StackPanel? _windowStateButtonsStackPanel;
+    private Border? _windowTopBorder;
     private bool _isMouseButtonDownOnDraggableTitleBarArea;
     private Point _mouseDownPositionOnDraggableTitleBarArea;

@@ -124,6 +125,7 @@ public override void OnApplyTemplate()
         _maximizeButton = (Button)Template.FindName("MaximizeButton", this);
         var draggableTitleBarArea = (Border)Template.FindName("DraggableTitleBarArea", this);
         var overlayControl = (OverlayControl)Template.FindName("TitleBar", this);
+        _windowTopBorder = (Border)Template.FindName("WindowTopBorder", this);

         _windowStateButtonsStackPanel.SizeChanged += WindowStateButtonsStackPanel_SizeChanged;
         closeButton.Click += CloseButton_Click;
@@ -483,6 +485,16 @@ private void UpdateTheme()
         dictionaries.Remove(resourceDictionaryToRemove);
         dictionaries.Add(resourceDictionary);
         UpdateLayout();
+
+        Guard.IsNotNull(_windowTopBorder);
+        if (IsAccentColorOnTitleBarEnabled())
+        {
+            _windowTopBorder.Background = SystemParameters.WindowGlassBrush;
+        }
+        else
+        {
+            _windowTopBorder.Background = null;
+        }
     }

     private nint ShowSnapLayout(nint lParam, ref bool handled)
@@ -560,4 +572,22 @@ private static void OnForbidMinimizeAndMaximizePropertyChangedCallback(Dependenc
             NativeMethods.EnableMinimizeAndMaximizeCapabilities(windowHandle);
         }
     }
+
+    public static bool IsAccentColorOnTitleBarEnabled()
+    {
+        const string key = @"HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\DWM";
+        const string name = "ColorPrevalence";
+
+        object? value = Registry.GetValue(key, name, null);
+        if (value is null)
+        {
+            // Key doesn't exist, assume default (OFF)
+            return false;
+        }
+        else
+        {
+            // If the key value is 1, the setting is ON. Otherwise, it's OFF.
+            return ((int)value == 1);
+        }
+    }
 }


[bug] api prefix and admin clash causes admin to break

Hello, I followed the settings correctly, and everything went right.

I created the plugins.js added the settings, and sent the settings via environment variables.

External API working perfectly.

But when I went to access the Content Type, I'm getting this error message:

<image>

And it doesn't load the Content Types, and the contents I have are gone:

<image>

I'm using strapi 4.1.12

And my settings:

<image>
<image>

If I disable the settings everything goes back to normal.
And I'm uploading the strapi with:

yarn strapi develop --watch-admin

I would love to use this extension, but with this problem I can't use it, did I miss some configuration?

Thanks for the help.

diff --git a/server/middleware/transform.js b/server/middleware/transform.js
index 794976f..7ac8e36 100644
--- a/server/middleware/transform.js
+++ b/server/middleware/transform.js
@@ -14,7 +14,7 @@ const transform = async (strapi, ctx, next) => {
 	}

 	// only process api requests.
-	if (isAPIRequest(ctx, settings.prefix)) {
+	if (isAPIRequest(ctx)) {
 		const { data } = ctx.body;

 		// ensure no error returned.
diff --git a/server/util/isAPIRequest.js b/server/util/isAPIRequest.js
index 82452d5..8e6b676 100644
--- a/server/util/isAPIRequest.js
+++ b/server/util/isAPIRequest.js
@@ -1,7 +1,10 @@
 'use strict';

-const isAPIRequest = (ctx, prefix = '/api/') =>
-	ctx.request && ctx.request.url && ctx.request.url.indexOf(prefix) !== -1;
+const isAPIRequest = (ctx) =>
+	ctx.state &&
+	ctx.state.route &&
+	ctx.state.route.info &&
+	ctx.state.route.info.type === 'content-api';

 module.exports = {
 	isAPIRequest,


**Describe the bug**
Quiz questions are not automatically saved when the bot is executed

**Screenshots**
<image>

**Logs**
N/A

**Error Statement**
N/A

**Desktop (please complete the following information):**
 - OS: macOS 14.4 (M1 Max)
 - Browser Version 122.0.6261.129 (Official Build) (arm64)
 - Version latest

**Expected behavior**
save Q&As to qa.csv

diff --git a/config.yaml b/config.yaml
index ef4573f..6fdb9ac 100644
--- a/config.yaml
+++ b/config.yaml
@@ -6,26 +6,24 @@ profile_path: '' #can be empty, will default to 'C:\Users\<user>\AppData\Local\G

 positions:
-- <position title 1>
-- <position title 2>
-- <position title 3>
+- Software Engineer

 locations:
-- <state>
+- Michigan
 - Remote

-salary: <yearly salary>
-rate: <hourly rate>
+salary: 60,000
+rate: 25
 # --------- Optional Parameters -------
 uploads:
-  Resume: 'C:\Users\<user>\Documents\resume.docx'
-  Cover Letter: 'C:\Users\<user>\Documents\cover letter.docx'
+  Resume: /Users/steven/Desktop/LinkedIn-Easy-Apply-Bot/cv.pdf
+  Cover Letter: /Users/steven/Desktop/LinkedIn-Easy-Apply-Bot/cl.pdf
   #Photo: # PATH TO photo

 output_filename:
-- # PATH TO OUTPUT FILE (default output.csv)
+- /Users/steven/Desktop/LinkedIn-Easy-Apply-Bot/out.csv

 # blacklist:
-# - # Company names you want to ignore
\ No newline at end of file
+# - # Company names you want to ignore
diff --git a/easyapplybot.py b/easyapplybot.py
index 52e9691..ef08ec4 100644
--- a/easyapplybot.py
+++ b/easyapplybot.py
@@ -108,15 +108,17 @@ def __init__(self,

         #initialize questions and answers file
         self.qa_file = Path("qa.csv")
+        self.answers = {}

         #if qa file does not exist, create it
         if self.qa_file.is_file():
-            self.answers = pd.read_csv(self.qa_file).to_dict()
+            df = pd.read_csv(self.qa_file)
+            for index, row in df.iterrows():
+                self.answers[row['Question']] = row['Answer']
         #if qa file does exist, load it
         else:
-            self.answers = {"Questions":"Answers"}
-            df = pd.DataFrame(self.answers, index=[0])
-            df.to_csv(self.qa_file, encoding='utf-8')
+            df = pd.DataFrame(columns=["Question", "Answer"])
+            df.to_csv(self.qa_file, index=False, encoding='utf-8')

     def get_appliedIDs(self, filename) -> list | None:
@@ -467,6 +469,10 @@ def is_present(button_locator) -> bool:
                             log.info("Please answer the questions, waiting 5 seconds...")
                             time.sleep(5)
                             elements = self.get_elements("error")
+
+                            for element in elements:
+                                self.process_questions()
+
                             if "application was sent" in self.browser.page_source:
                                 log.info("Application Submitted")
                                 submitted = True
@@ -477,8 +483,7 @@ def is_present(button_locator) -> bool:
                                 break
                         continue
                         #add explicit wait
-                    # for element in elements:
-                    #self.process_questions()
+
                     else:
                         log.info("Application not submitted")
                         time.sleep(2)
@@ -559,9 +564,9 @@ def process_questions(self):
     def ans_question(self, question): #refactor this to an ans.yaml file
         answer = None
         if "how many" in question:
-            answer = random.randint(3, 12)
+            answer = "1"
         elif "experience" in question:
-            answer = random.randint(3, 12)
+            answer = "1"
         elif "sponsor" in question:
             answer = "No"
         elif 'do you ' in question:
@@ -595,15 +600,21 @@ def ans_question(self, question): #refactor this to an ans.yaml file
             #open file and document unanswerable questions, appending to it
             answer = "user provided"
             time.sleep(15)
-            self.answers[question] = answer
+
             # df = pd.DataFrame(self.answers, index=[0])
             # df.to_csv(self.qa_file, encoding="utf-8")
         log.info("Answering question: " + question + " with answer: " + answer)
-        self.answers[question] = answer
-        df = pd.DataFrame(self.answers, index=[0])
-        df.to_csv(self.qa_file, encoding="utf-8")
-        log.debug(f"{question} : {answer}")
+
+        # Append question and answer to the CSV
+        if question not in self.answers:
+            self.answers[question] = answer
+            # Append a new question-answer pair to the CSV file
+            new_data = pd.DataFrame({"Question": [question], "Answer": [answer]})
+            new_data.to_csv(self.qa_file, mode='a', header=False, index=False, encoding='utf-8')
+            log.info(f"Appended to QA file: '{question}' with answer: '{answer}'.")
+
         return answer
+
     def load_page(self, sleep=1):
         scroll_page = 0
         while scroll_page < 4000:
diff --git a/out.csv b/out.csv
new file mode 100644
index 0000000..0c663d4
--- /dev/null
+++ b/out.csv
@@ -0,0 +1,28 @@
+2024-03-28 16:36:32,3857211698,Software Engineer,AIMQ DEVELOPMENT LLC,True,False
+2024-03-28 16:40:05,3857211698,Software Engineer,AIMQ DEVELOPMENT LLC,True,True
+2024-04-09 22:28:24,3860832233,0) Full Stack Engineer,Vital Tech Solutions,True,True
+2024-04-09 22:35:42,3832283941,0) Quantitative Developer,Jobot,True,True
+2024-04-09 22:36:15,3874275602,0) Sr Software Engineer (Warren),CIeNET Technologies,True,True
+2024-04-09 22:36:35,3827214267,0) Full Stack AI Engineer,MOTOR Information Systems,True,True
+2024-04-09 22:36:55,3854456823,0) Technical engineer or Embedded or System Engineer or Linux engineer,People Tech Group Inc,True,True
+2024-04-09 22:37:12,3783795088,0) Embedded Software Engineer,LER TechForce,True,True
+2024-04-09 22:41:05,3796905534,0) Mid Level Software Engineer,"Accessibility Services, Inc.",True,True
+2024-04-09 22:41:28,3866228500,0) Mikrotik Engineer,Mad River Enterprises,True,True
+2024-04-09 22:44:56,3880081547,0) DevOps Engineer,Camgian,True,True
+2024-04-09 22:55:36,3818300577,1) CMM Programmer,Tenneco,True,True
+2024-04-09 22:56:13,3812006594,1) Senior Software Engineer,"Revela, Inc.",True,True
+2024-04-09 22:56:30,3764169601,1) Embedded Software Engineer,LER TechForce,True,True
+2024-04-09 22:56:50,3864416985,1) Embedded Engineer,People Tech Group Inc,True,True
+2024-04-09 22:57:40,3860849293,1) Embedded Software Engineer,Pure Talent Consulting,True,True
+2024-04-09 23:03:01,3887422852,1) Dotnet Developer,Imetris Corporation,True,True
+2024-04-09 23:07:22,3860352554,1) Senior Full Stack Shopify Developer,Akantro,True,True
+2024-04-09 23:10:37,3885141730,1) Experienced Talend ETL Developer,"Retail Insights, LLC",True,True
+2024-04-09 23:16:15,3875092654,"1) IT Ops Engineer II - Primarily Remote/On-Site 2-3 days/mth - MS Windows, Linux, Powershell",Conexess Group,True,True
+2024-04-09 23:16:34,3885092790,1) Senior SAS Programmer - only W2,Intellisoft Technologies,True,False
+2024-04-09 23:19:25,3827215295,1) Platform Engineer,RE/MAX,True,True
+2024-04-09 23:19:59,3840470076,1) Software Engineer,InterEx Group,True,False
+2024-04-09 23:20:33,3864393011,1) Application Developer,SysTechCorp Inc,True,False
+2024-04-09 23:21:07,3881719303,1) Senior Cloud Developer,JRD Systems,True,False
+2024-04-09 23:23:49,3840470076,1) Software Engineer,InterEx Group,True,True
+2024-04-09 23:28:02,3885092790,1) Senior SAS Programmer - only W2,Intellisoft Technologies,True,False
+2024-04-09 23:30:16,3885092790,1) Senior SAS Programmer - only W2,Intellisoft Technologies,True,True
diff --git a/qa.csv b/qa.csv
new file mode 100644
index 0000000..7bd6244
--- /dev/null
+++ b/qa.csv
@@ -0,0 +1,52 @@
+Question,Answer
+"Have you completed the following level of education: Bachelor's Degree?
+Have you completed the following level of education: Bachelor's Degree?
+Required
+Yes
+No",user provided
+"Do you have a valid driver's license?
+Do you have a valid driver's license?
+Required
+Yes
+No",user provided
+"Do you have the following license or certification: Google Cloud Certified Professional Cloud Architect?
+Do you have the following license or certification: Google Cloud Certified Professional Cloud Architect?
+Required
+Yes
+No
+Please make a selection",user provided
+"have you completed the following level of education: bachelor's degree?
+have you completed the following level of education: bachelor's degree?
+required
+yes
+no",Yes
+how many years of work experience do you have with c++?,1
+"how many years of computer games experience do you currently have?
+enter a whole number between 0 and 99",1
+"how many years of engineering experience do you currently have?
+enter a whole number between 0 and 99",1
+"how many years of work experience do you have with unreal engine?
+enter a whole number between 0 and 99",1
+"how many years utilizing unreal engine?
+enter a decimal number larger than 0.0",1
+how many years of computer games experience do you currently have?,1
+how many years of engineering experience do you currently have?,1
+how many years of work experience do you have with unreal engine?,1
+how many years utilizing unreal engine?,1
+"how many years of work experience do you have with databases?
+enter a whole number between 0 and 99",1
+"how many years of work experience do you have with business development?
+enter a whole number between 0 and 99",1
+"are you comfortable commuting to this job's location?
+are you comfortable commuting to this job's location?
+required
+yes
+no
+please make a selection",Yes
+how many years of work experience do you have with databases?,1
+how many years of work experience do you have with business development?,1
+"are you comfortable commuting to this job's location?
+are you comfortable commuting to this job's location?
+required
+yes
+no",Yes


[Bug]: Bebel Version upgraded to 7.24.1 Promise.allSettled cannot support

### 馃捇

- [ ] Would you like to work on a fix?

### How are you using Babel?

babel-loader (webpack)

### Input code

babel.config.js
```js
module.exports = {
    env: {
        node: {
            presets: [
                [
                    '@babel/preset-env',
                    {
                        targets: {
                            node: '14'
                        },
                        modules: false
                    }
                ],
                '@vue/babel-preset-jsx',
                [
                    '@babel/preset-typescript',
                    {
                        allExtensions: true
                    }
                ]
            ],
            sourceType: 'unambiguous',
            plugins: [
                [
                    '@babel/plugin-transform-runtime',
                    {
                        corejs: {
                            version: 3,
                            proposals: true
                        }
                    }
                ],
                ['@babel/plugin-proposal-decorators', {legacy: true}],
                '@babel/plugin-proposal-class-properties',
                '@babel/plugin-syntax-dynamic-import',
                [
                    'component',
                    {
                        libraryName: '@ncfe/nc.element-ui',
                        style: false
                    },
                    '@ncfe/nc.element-ui'
                ]
            ]
        },
        web: {
            presets: [
                [
                    '@babel/preset-env',
                    {
                        targets: {
                            chrome: 44,
                            ie: 8
                        },
                        modules: false
                    }
                ],
                '@vue/babel-preset-jsx',
                [
                    '@babel/preset-typesc

diff --git a/packages/babel-plugin-polyfill-corejs3/src/babel-runtime-corejs3-paths.ts b/packages/babel-plugin-polyfill-corejs3/src/babel-runtime-corejs3-paths.ts
new file mode 100644
index 00000000..f50e5f0c
--- /dev/null
+++ b/packages/babel-plugin-polyfill-corejs3/src/babel-runtime-corejs3-paths.ts
@@ -0,0 +1,179 @@
+// This file contains the list of paths supported by @babel/runtime-corejs3.
+// It must _not_ be edited, as all new features should go through direct
+// injection of core-js-pure imports.
+
+export const stable = new Set([
+  "array",
+  "array/from",
+  "array/is-array",
+  "array/of",
+  "bind",
+  "clear-immediate",
+  "code-point-at",
+  "concat",
+  "copy-within",
+  "date/now",
+  "ends-with",
+  "entries",
+  "every",
+  "fill",
+  "filter",
+  "find",
+  "find-index",
+  "flags",
+  "flat",
+  "flat-map",
+  "for-each",
+  "includes",
+  "index-of",
+  "json/stringify",
+  "keys",
+  "last-index-of",
+  "map",
+  "map",
+  "math/acosh",
+  "math/asinh",
+  "math/atanh",
+  "math/cbrt",
+  "math/clz32",
+  "math/cosh",
+  "math/expm1",
+  "math/fround",
+  "math/hypot",
+  "math/imul",
+  "math/log10",
+  "math/log1p",
+  "math/log2",
+  "math/sign",
+  "math/sinh",
+  "math/tanh",
+  "math/trunc",
+  "number/epsilon",
+  "number/is-finite",
+  "number/is-integer",
+  "number/is-nan",
+  "number/is-safe-integer",
+  "number/max-safe-integer",
+  "number/min-safe-integer",
+  "number/parse-float",
+  "number/parse-int",
+  "object/assign",
+  "object/create",
+  "object/define-properties",
+  "object/define-property",
+  "object/entries",
+  "object/freeze",
+  "object/from-entries",
+  "object/get-own-property-descriptor",
+  "object/get-own-property-descriptors",
+  "object/get-own-property-names",
+  "object/get-own-property-symbols",
+  "object/get-prototype-of",
+  "object/is",
+  "object/is-extensible",
+  "object/is-frozen",
+  "object/is-sealed",
+  "object/keys",
+  "object/prevent-extensions",
+  "object/seal",
+  "object/set-prototype-of",
+  "object/values",
+  "pad-end",
+  "pad-start",
+  "parse-float",
+  "parse-int",
+  "promise",
+  "queue-microtask",
+  "reduce",
+  "reduce-right",
+  "reflect/apply",
+  "reflect/construct",
+  "reflect/define-property",
+  "reflect/delete-property",
+  "reflect/get",
+  "reflect/get-own-property-descriptor",
+  "reflect/get-prototype-of",
+  "reflect/has",
+  "reflect/is-extensible",
+  "reflect/own-keys",
+  "reflect/prevent-extensions",
+  "reflect/set",
+  "reflect/set-prototype-of",
+  "repeat",
+  "reverse",
+  "set",
+  "set-immediate",
+  "set-interval",
+  "set-timeout",
+  "slice",
+  "some",
+  "sort",
+  "splice",
+  "starts-with",
+  "string/from-code-point",
+  "string/raw",
+  "symbol",
+  "symbol/async-iterator",
+  "symbol/for",
+  "symbol/has-instance",
+  "symbol/is-concat-spreadable",
+  "symbol/iterator",
+  "symbol/key-for",
+  "symbol/match",
+  "symbol/replace",
+  "symbol/search",
+  "symbol/species",
+  "symbol/split",
+  "symbol/to-primitive",
+  "symbol/to-string-tag",
+  "symbol/unscopables",
+  "trim",
+  "trim-end",
+  "trim-left",
+  "trim-right",
+  "trim-start",
+  "url",
+  "url-search-params",
+  "values",
+  "weak-map",
+  "weak-set",
+]);
+
+export const proposals = new Set([
+  ...stable,
+  "aggregate-error",
+  "at",
+  "code-points",
+  "composite-key",
+  "composite-symbol",
+  "global-this",
+  "match-all",
+  "math/clamp",
+  "math/degrees",
+  "math/deg-per-rad",
+  "math/fscale",
+  "math/iaddh",
+  "math/imulh",
+  "math/isubh",
+  "math/rad-per-deg",
+  "math/radians",
+  "math/scale",
+  "math/seeded-prng",
+  "math/signbit",
+  "math/umulh",
+  "number/from-string",
+  "observable",
+  "reflect/define-metadata",
+  "reflect/delete-metadata",
+  "reflect/get-metadata",
+  "reflect/get-metadata-keys",
+  "reflect/get-own-metadata",
+  "reflect/get-own-metadata-keys",
+  "reflect/has-metadata",
+  "reflect/has-own-metadata",
+  "reflect/metadata",
+  "replace-all",
+  "symbol/dispose",
+  "symbol/observable",
+  "symbol/pattern-match",
+]);
diff --git a/packages/babel-plugin-polyfill-corejs3/src/index.ts b/packages/babel-plugin-polyfill-corejs3/src/index.ts
index 9a5d667a..e1244a18 100644
--- a/packages/babel-plugin-polyfill-corejs3/src/index.ts
+++ b/packages/babel-plugin-polyfill-corejs3/src/index.ts
@@ -11,6 +11,7 @@ import {
   DecoratorMetadataDependencies,
   type CoreJSPolyfillDescriptor,
 } from "./built-in-definitions";
+import * as BabelRuntimePaths from "./babel-runtime-corejs3-paths";
 import canSkipPolyfill from "./usage-filters";

 import type { NodePath } from "@babel/traverse";
@@ -35,7 +36,6 @@ type Options = {
   [presetEnvCompat]?: { noRuntimeName: boolean };
   [runtimeCompat]: {
     useBabelRuntime: boolean;
-    babelRuntimePath: string;
     ext: string;
   };
 };
@@ -127,6 +127,16 @@ export default defineProvider<Options>(function (
       } else if (name.startsWith("es.") && !available.has(name)) {
         useProposalBase = true;
       }
+      if (
+        useBabelRuntime &&
+        !(
+          useProposalBase
+            ? BabelRuntimePaths.proposals
+            : BabelRuntimePaths.stable
+        ).has(desc.pure)
+      ) {
+        return;
+      }
       const coreJSPureBase = getCoreJSPureBase(useProposalBase);
       return utils.injectDefaultImport(
         `${coreJSPureBase}/${desc.pure}${ext}`,
diff --git a/packages/babel-plugin-polyfill-corejs3/test/fixtures/regression/babel-runtime-missing-proposals/input.mjs b/packages/babel-plugin-polyfill-corejs3/test/fixtures/regression/babel-runtime-missing-proposals/input.mjs
new file mode 100644
index 00000000..f5e6314e
--- /dev/null
+++ b/packages/babel-plugin-polyfill-corejs3/test/fixtures/regression/babel-runtime-missing-proposals/input.mjs
@@ -0,0 +1,2 @@
+Promise.allSettled(1, 2, 3);
+Observable(1, 2, 3);
\ No newline at end of file
diff --git a/packages/babel-plugin-polyfill-corejs3/test/fixtures/regression/babel-runtime-missing-proposals/options.json b/packages/babel-plugin-polyfill-corejs3/test/fixtures/regression/babel-runtime-missing-proposals/options.json
new file mode 100644
index 00000000..934681b6
--- /dev/null
+++ b/packages/babel-plugin-polyfill-corejs3/test/fixtures/regression/babel-runtime-missing-proposals/options.json
@@ -0,0 +1,14 @@
+{
+  "plugins": [
+    [
+      "@@/polyfill-corejs3",
+      {
+        "method": "usage-pure",
+        "proposals": true,
+        "#__secret_key__@babel/runtime__compatibility": {
+          "useBabelRuntime": true
+        }
+      }
+    ]
+  ]
+}
diff --git a/packages/babel-plugin-polyfill-corejs3/test/fixtures/regression/babel-runtime-missing-proposals/output.mjs b/packages/babel-plugin-polyfill-corejs3/test/fixtures/regression/babel-runtime-missing-proposals/output.mjs
new file mode 100644
index 00000000..dd88a740
--- /dev/null
+++ b/packages/babel-plugin-polyfill-corejs3/test/fixtures/regression/babel-runtime-missing-proposals/output.mjs
@@ -0,0 +1,4 @@
+import _Promise from "@babel/runtime-corejs3/core-js/promise.js";
+import _Observable from "@babel/runtime-corejs3/core-js/observable.js";
+_Promise.allSettled(1, 2, 3);
+_Observable(1, 2, 3);
diff --git a/packages/babel-plugin-polyfill-corejs3/test/fixtures/regression/babel-runtime-missing/input.mjs b/packages/babel-plugin-polyfill-corejs3/test/fixtures/regression/babel-runtime-missing/input.mjs
new file mode 100644
index 00000000..f5e6314e
--- /dev/null
+++ b/packages/babel-plugin-polyfill-corejs3/test/fixtures/regression/babel-runtime-missing/input.mjs
@@ -0,0 +1,2 @@
+Promise.allSettled(1, 2, 3);
+Observable(1, 2, 3);
\ No newline at end of file
diff --git a/packages/babel-plugin-polyfill-corejs3/test/fixtures/regression/babel-runtime-missing/options.json b/packages/babel-plugin-polyfill-corejs3/test/fixtures/regression/babel-runtime-missing/options.json
new file mode 100644
index 00000000..39bcec39
--- /dev/null
+++ b/packages/babel-plugin-polyfill-corejs3/test/fixtures/regression/babel-runtime-missing/options.json
@@ -0,0 +1,13 @@
+{
+  "plugins": [
+    [
+      "@@/polyfill-corejs3",
+      {
+        "method": "usage-pure",
+        "#__secret_key__@babel/runtime__compatibility": {
+          "useBabelRuntime": true
+        }
+      }
+    ]
+  ]
+}
diff --git a/packages/babel-plugin-polyfill-corejs3/test/fixtures/regression/babel-runtime-missing/output.mjs b/packages/babel-plugin-polyfill-corejs3/test/fixtures/regression/babel-runtime-missing/output.mjs
new file mode 100644
index 00000000..92a8c457
--- /dev/null
+++ b/packages/babel-plugin-polyfill-corejs3/test/fixtures/regression/babel-runtime-missing/output.mjs
@@ -0,0 +1,3 @@
+import _Promise from "@babel/runtime-corejs3/core-js-stable/promise.js";
+_Promise.allSettled(1, 2, 3);
+Observable(1, 2, 3);


We've stumbled into quality issues with tangents that are calculated using the shader derivative approach while experimenting with anisotropy. From the spec, it is recommended that tangents are generated clientside using MikkTSpace:

> When tangents are not specified, client implementations SHOULD calculate tangents using default [MikkTSpace](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#mikktspace) algorithms with the specified vertex positions, normals, and texture coordinates associated with the normal texture.

Admittedly, most real-time engines do not incur this cost at load time, but demonstrating it's quality and utility here in the sample viewer could help inform other implementations of the benefits to this approach.

diff --git a/app_web/src/main.js b/app_web/src/main.js
index 13113ef7f..503d202a3 100644
--- a/app_web/src/main.js
+++ b/app_web/src/main.js
@@ -7,8 +7,14 @@ import { Observable, Subject, from, merge } from 'rxjs';
 import { mergeMap, filter, map, multicast } from 'rxjs/operators';
 import { gltfModelPathProvider, fillEnvironmentWithPaths } from './model_path_provider.js';

-async function main()
-{
+
+import mikktspaceWasm from '../../source/libs/mikktspace_module.js';
+
+let mikktspace = null;
+
+async function main() {
+    mikktspace = await mikktspaceWasm("libs/mikktspace_module_bg.wasm");
+
     const canvas = document.getElementById("canvas");
     const context = canvas.getContext("webgl2", { alpha: false, antialias: true });
     const ui = document.getElementById("app");
@@ -363,4 +369,4 @@ async function main()
     window.requestAnimationFrame(update);
 }

-export { main };
+export { main, mikktspace };
diff --git a/source/gltf/primitive.js b/source/gltf/primitive.js
index dd37cc713..6cf0ea5b6 100644
--- a/source/gltf/primitive.js
+++ b/source/gltf/primitive.js
@@ -1,6 +1,7 @@
 import { initGlForMembers } from './utils.js';
 import { GltfObject } from './gltf_object.js';
 import { gltfBuffer } from './buffer.js';
+import { gltfAccessor } from './accessor.js';
 import { gltfImage } from './image.js';
 import { ImageMimeType } from './image_mime_type.js';
 import { gltfTexture } from './texture.js';
@@ -9,13 +10,14 @@ import { gltfSampler } from './sampler.js';
 import { gltfBufferView } from './buffer_view.js';
 import { DracoDecoder } from '../ResourceLoader/draco.js';
 import { GL  } from '../Renderer/webgl.js';
+import { mikktspace } from '../../app_web/src/main.js';

 class gltfPrimitive extends GltfObject
 {
     constructor()
     {
         super();
-        this.attributes = [];
+        this.attributes = {};
         this.targets = [];
         this.indices = undefined;
         this.material = undefined;
@@ -53,6 +55,7 @@ class gltfPrimitive extends GltfObject

         if (this.extensions !== undefined)
         {
+            // Decode Draco compressed mesh:
             if (this.extensions.KHR_draco_mesh_compression !== undefined)
             {
                 const dracoDecoder = new DracoDecoder();
@@ -69,6 +72,15 @@ class gltfPrimitive extends GltfObject
             }
         }

+        if (this.attributes.TANGENT === undefined)
+        {
+            console.info("Generating tangents using the MikkTSpace algorithm.");
+            console.time("Tangent generation");
+            this.unweld(gltf);
+            this.generateTangents(gltf);
+            console.timeEnd("Tangent generation");
+        }
+
         // VERTEX ATTRIBUTES
         for (const attribute of Object.keys(this.attributes))
         {
@@ -722,6 +734,131 @@ class gltfPrimitive extends GltfObject
         };

     }
+
+    /**
+     * Unwelds this primitive, i.e. applies the index mapping.
+     * This is required for generating tangents using the MikkTSpace algorithm,
+     * because the same vertex might be mapped to different tangents.
+     * @param {*} gltf The glTF document.
+     */
+    unweld(gltf) {
+        // Unwelding is an idempotent operation.
+        if (this.indices === undefined) {
+            return;
+        }
+
+        const indices = gltf.accessors[this.indices].getTypedView(gltf);
+
+        // Unweld attributes:
+        for (const [attribute, accessorIndex] of Object.entries(this.attributes)) {
+            this.attributes[attribute] = this.unweldAccessor(gltf, gltf.accessors[accessorIndex], indices);
+        }
+
+        // Unweld morph targets:
+        for (const target of this.targets) {
+            for (const [attribute, accessorIndex] of Object.entries(target)) {
+                target[attribute] = this.unweldAccessor(gltf, gltf.accessors[accessorIndex], indices);
+            }
+        }
+
+        // Dipose the indices:
+        this.indices = undefined;
+    }
+
+    /**
+     * Unwelds a single accessor. Used by {@link unweld}.
+     * @param {*} gltf The glTF document.
+     * @param {*} accessor The accessor to unweld.
+     * @param {*} typedIndexView A typed view of the indices.
+     * @returns A new accessor index containing the unwelded attribute.
+     */
+    unweldAccessor(gltf, accessor, typedIndexView) {
+        const stride = accessor.getComponentCount(accessor.type);
+
+        const weldedAttribute = accessor.getTypedView(gltf);
+        const unweldedAttribute = new Float32Array(gltf.accessors[this.indices].count * stride);
+
+        // Apply the index mapping.
+        for (let i = 0; i < typedIndexView.length; i++) {
+            for (let j = 0; j < stride; j++) {
+                unweldedAttribute[i * stride + j] = weldedAttribute[typedIndexView[i] * stride + j];
+            }
+        }
+
+        // Create a new buffer and buffer view for the unwelded attribute:
+        const unweldedBuffer = new gltfBuffer();
+        unweldedBuffer.byteLength = unweldedAttribute.byteLength;
+        unweldedBuffer.buffer = unweldedAttribute.buffer;
+        gltf.buffers.push(unweldedBuffer);
+
+        const unweldedBufferView = new gltfBufferView();
+        unweldedBufferView.buffer = gltf.buffers.length - 1;
+        unweldedBufferView.byteLength = unweldedAttribute.byteLength;
+        unweldedBufferView.target = GL.ARRAY_BUFFER;
+        gltf.bufferViews.push(unweldedBufferView);
+
+        // Create a new accessor for the unwelded attribute:
+        const unweldedAccessor = new gltfAccessor();
+        unweldedAccessor.bufferView = gltf.bufferViews.length - 1;
+        unweldedAccessor.byteOffset = 0;
+        unweldedAccessor.count = typedIndexView.length;
+        unweldedAccessor.type = accessor.type;
+        unweldedAccessor.componentType = accessor.componentType;
+        unweldedAccessor.min = gltf.accessors[this.attributes.POSITION].min;
+        unweldedAccessor.max = gltf.accessors[this.attributes.POSITION].max;
+        gltf.accessors.push(unweldedAccessor);
+
+        // Update the primitive to use the unwelded attribute:
+        return gltf.accessors.length - 1;
+    }
+
+    generateTangents(gltf) {
+        const positions = gltf.accessors[this.attributes.POSITION].getTypedView(gltf);
+        const normals = gltf.accessors[this.attributes.NORMAL].getTypedView(gltf);
+        const texcoords = gltf.accessors[this.attributes.TEXCOORD_0].getTypedView(gltf);
+        for (let i = 0; i < positions.length; i += 3) {
+            mikktspace.writeFace(
+                positions[i], positions[i + 1], positions[i + 2],
+                normals[i], normals[i + 1], normals[i + 2],
+                texcoords[i], texcoords[i + 1]
+            );
+        }
+
+        mikktspace.generateTangents();
+
+        const tangents = new Float32Array(4 * positions.length / 3);
+        for (let i = 0; i < tangents.length; i++) {
+            let t = mikktspace.readTangent(i);
+            tangents[i] = t;
+        }
+
+        // Create a new buffer and buffer view for the tangents:
+        const tangentBuffer = new gltfBuffer();
+        tangentBuffer.byteLength = tangents.byteLength;
+        tangentBuffer.buffer = tangents.buffer;
+        gltf.buffers.push(tangentBuffer);
+
+        const tangentBufferView = new gltfBufferView();
+        tangentBufferView.buffer = gltf.buffers.length - 1;
+        tangentBufferView.byteLength = tangents.byteLength;
+        tangentBufferView.target = GL.ARRAY_BUFFER;
+        gltf.bufferViews.push(tangentBufferView);
+
+        // Create a new accessor for the tangents:
+        const tangentAccessor = new gltfAccessor();
+        tangentAccessor.bufferView = gltf.bufferViews.length - 1;
+        tangentAccessor.byteOffset = 0;
+        tangentAccessor.count = tangents.length / 4;
+        tangentAccessor.type = "VEC4";
+        tangentAccessor.componentType = GL.FLOAT;
+
+        // Update the primitive to use the tangents:
+        this.attributes.TANGENT = gltf.accessors.length;
+        gltf.accessors.push(tangentAccessor);
+
+        // Update the primitive to use the tangents:
+        this.attributes.TANGENT = gltf.accessors.length - 1;
+    }
 }

 export { gltfPrimitive };
diff --git a/source/libs/mikktspace_module.js b/source/libs/mikktspace_module.js
new file mode 100644
index 000000000..e18d20ddb
--- /dev/null
+++ b/source/libs/mikktspace_module.js
@@ -0,0 +1,360 @@
+
+let wasm;
+
+const heap = new Array(32).fill(undefined);
+
+heap.push(undefined, null, true, false);
+
+function getObject(idx) { return heap[idx]; }
+
+let heap_next = heap.length;
+
+function dropObject(idx) {
+    if (idx < 36) return;
+    heap[idx] = heap_next;
+    heap_next = idx;
+}
+
+function takeObject(idx) {
+    const ret = getObject(idx);
+    dropObject(idx);
+    return ret;
+}
+
+let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
+
+cachedTextDecoder.decode();
+
+let cachegetUint8Memory0 = null;
+function getUint8Memory0() {
+    if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.memory.buffer) {
+        cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer);
+    }
+    return cachegetUint8Memory0;
+}
+
+function getStringFromWasm0(ptr, len) {
+    return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
+}
+
+function addHeapObject(obj) {
+    if (heap_next === heap.length) heap.push(heap.length + 1);
+    const idx = heap_next;
+    heap_next = heap[idx];
+
+    heap[idx] = obj;
+    return idx;
+}
+
+function debugString(val) {
+    // primitive types
+    const type = typeof val;
+    if (type == 'number' || type == 'boolean' || val == null) {
+        return  `${val}`;
+    }
+    if (type == 'string') {
+        return `"${val}"`;
+    }
+    if (type == 'symbol') {
+        const description = val.description;
+        if (description == null) {
+            return 'Symbol';
+        } else {
+            return `Symbol(${description})`;
+        }
+    }
+    if (type == 'function') {
+        const name = val.name;
+        if (typeof name == 'string' && name.length > 0) {
+            return `Function(${name})`;
+        } else {
+            return 'Function';
+        }
+    }
+    // objects
+    if (Array.isArray(val)) {
+        const length = val.length;
+        let debug = '[';
+        if (length > 0) {
+            debug += debugString(val[0]);
+        }
+        for(let i = 1; i < length; i++) {
+            debug += ', ' + debugString(val[i]);
+        }
+        debug += ']';
+        return debug;
+    }
+    // Test for built-in
+    const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val));
+    let className;
+    if (builtInMatches.length > 1) {
+        className = builtInMatches[1];
+    } else {
+        // Failed to match the standard '[object ClassName]'
+        return toString.call(val);
+    }
+    if (className == 'Object') {
+        // we're a user defined class or Object
+        // JSON.stringify avoids problems with cycles, and is generally much
+        // easier than looping through ownProperties of `val`.
+        try {
+            return 'Object(' + JSON.stringify(val) + ')';
+        } catch (_) {
+            return 'Object';
+        }
+    }
+    // errors
+    if (val instanceof Error) {
+        return `${val.name}: ${val.message}\n${val.stack}`;
+    }
+    // TODO we could test for more things here, like `Set`s and `Map`s.
+    return className;
+}
+
+let WASM_VECTOR_LEN = 0;
+
+let cachedTextEncoder = new TextEncoder('utf-8');
+
+const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
+    ? function (arg, view) {
+    return cachedTextEncoder.encodeInto(arg, view);
+}
+    : function (arg, view) {
+    const buf = cachedTextEncoder.encode(arg);
+    view.set(buf);
+    return {
+        read: arg.length,
+        written: buf.length
+    };
+});
+
+function passStringToWasm0(arg, malloc, realloc) {
+
+    if (realloc === undefined) {
+        const buf = cachedTextEncoder.encode(arg);
+        const ptr = malloc(buf.length);
+        getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf);
+        WASM_VECTOR_LEN = buf.length;
+        return ptr;
+    }
+
+    let len = arg.length;
+    let ptr = malloc(len);
+
+    const mem = getUint8Memory0();
+
+    let offset = 0;
+
+    for (; offset < len; offset++) {
+        const code = arg.charCodeAt(offset);
+        if (code > 0x7F) break;
+        mem[ptr + offset] = code;
+    }
+
+    if (offset !== len) {
+        if (offset !== 0) {
+            arg = arg.slice(offset);
+        }
+        ptr = realloc(ptr, len, len = offset + arg.length * 3);
+        const view = getUint8Memory0().subarray(ptr + offset, ptr + len);
+        const ret = encodeString(arg, view);
+
+        offset += ret.written;
+    }
+
+    WASM_VECTOR_LEN = offset;
+    return ptr;
+}
+
+let cachegetInt32Memory0 = null;
+function getInt32Memory0() {
+    if (cachegetInt32Memory0 === null || cachegetInt32Memory0.buffer !== wasm.memory.buffer) {
+        cachegetInt32Memory0 = new Int32Array(wasm.memory.buffer);
+    }
+    return cachegetInt32Memory0;
+}
+/**
+*/
+export function init_panic_hook() {
+    wasm.init_panic_hook();
+}
+
+/**
+* @param {number} a
+* @param {number} b
+* @returns {number}
+*/
+export function add(a, b) {
+    var ret = wasm.add(a, b);
+    return ret >>> 0;
+}
+
+/**
+* @param {string} a
+* @returns {string}
+*/
+export function foo(a) {
+    try {
+        const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
+        var ptr0 = passStringToWasm0(a, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+        var len0 = WASM_VECTOR_LEN;
+        wasm.foo(retptr, ptr0, len0);
+        var r0 = getInt32Memory0()[retptr / 4 + 0];
+        var r1 = getInt32Memory0()[retptr / 4 + 1];
+        return getStringFromWasm0(r0, r1);
+    } finally {
+        wasm.__wbindgen_add_to_stack_pointer(16);
+        wasm.__wbindgen_free(r0, r1);
+    }
+}
+
+let cachegetUint32Memory0 = null;
+function getUint32Memory0() {
+    if (cachegetUint32Memory0 === null || cachegetUint32Memory0.buffer !== wasm.memory.buffer) {
+        cachegetUint32Memory0 = new Uint32Array(wasm.memory.buffer);
+    }
+    return cachegetUint32Memory0;
+}
+
+function passArrayJsValueToWasm0(array, malloc) {
+    const ptr = malloc(array.length * 4);
+    const mem = getUint32Memory0();
+    for (let i = 0; i < array.length; i++) {
+        mem[ptr / 4 + i] = addHeapObject(array[i]);
+    }
+    WASM_VECTOR_LEN = array.length;
+    return ptr;
+}
+/**
+* @param {any[]} a
+* @returns {number}
+*/
+export function length(a) {
+    var ptr0 = passArrayJsValueToWasm0(a, wasm.__wbindgen_malloc);
+    var len0 = WASM_VECTOR_LEN;
+    var ret = wasm.length(ptr0, len0);
+    return ret >>> 0;
+}
+
+/**
+* @param {number} p0
+* @param {number} p1
+* @param {number} p2
+* @param {number} n0
+* @param {number} n1
+* @param {number} n2
+* @param {number} t0
+* @param {number} t1
+*/
+export function writeFace(p0, p1, p2, n0, n1, n2, t0, t1) {
+    wasm.writeFace(p0, p1, p2, n0, n1, n2, t0, t1);
+}
+
+/**
+* @param {number} i
+* @returns {number}
+*/
+export function readTangent(i) {
+    var ret = wasm.readTangent(i);
+    return ret;
+}
+
+/**
+* Generates vertex tangents for the given position/normal/texcoord attributes.
+*/
+export function generateTangents() {
+    wasm.generateTangents();
+}
+
+async function load(module, imports) {
+    if (typeof Response === 'function' && module instanceof Response) {
+        if (typeof WebAssembly.instantiateStreaming === 'function') {
+            try {
+                return await WebAssembly.instantiateStreaming(module, imports);
+
+            } catch (e) {
+                if (module.headers.get('Content-Type') != 'application/wasm') {
+                    console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
+
+                } else {
+                    throw e;
+                }
+            }
+        }
+
+        const bytes = await module.arrayBuffer();
+        return await WebAssembly.instantiate(bytes, imports);
+
+    } else {
+        const instance = await WebAssembly.instantiate(module, imports);
+
+        if (instance instanceof WebAssembly.Instance) {
+            return { instance, module };
+
+        } else {
+            return instance;
+        }
+    }
+}
+
+async function init(input) {
+    if (typeof input === 'undefined') {
+        input = new URL('mikktspace_module_bg.wasm', import.meta.url);
+    }
+    const imports = {};
+    imports.wbg = {};
+    imports.wbg.__wbindgen_object_drop_ref = function(arg0) {
+        takeObject(arg0);
+    };
+    imports.wbg.__wbg_log_19b305dd6bb2d393 = function(arg0, arg1) {
+        console.log(getStringFromWasm0(arg0, arg1));
+    };
+    imports.wbg.__wbindgen_string_new = function(arg0, arg1) {
+        var ret = getStringFromWasm0(arg0, arg1);
+        return addHeapObject(ret);
+    };
+    imports.wbg.__wbg_new_59cb74e423758ede = function() {
+        var ret = new Error();
+        return addHeapObject(ret);
+    };
+    imports.wbg.__wbg_stack_558ba5917b466edd = function(arg0, arg1) {
+        var ret = getObject(arg1).stack;
+        var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+        var len0 = WASM_VECTOR_LEN;
+        getInt32Memory0()[arg0 / 4 + 1] = len0;
+        getInt32Memory0()[arg0 / 4 + 0] = ptr0;
+    };
+    imports.wbg.__wbg_error_4bb6c2a97407129a = function(arg0, arg1) {
+        try {
+            console.error(getStringFromWasm0(arg0, arg1));
+        } finally {
+            wasm.__wbindgen_free(arg0, arg1);
+        }
+    };
+    imports.wbg.__wbindgen_debug_string = function(arg0, arg1) {
+        var ret = debugString(getObject(arg1));
+        var ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
+        var len0 = WASM_VECTOR_LEN;
+        getInt32Memory0()[arg0 / 4 + 1] = len0;
+        getInt32Memory0()[arg0 / 4 + 0] = ptr0;
+    };
+    imports.wbg.__wbindgen_rethrow = function(arg0) {
+        throw takeObject(arg0);
+    };
+
+    if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) {
+        input = fetch(input);
+    }
+
+
+
+    const { instance, module } = await load(await input, imports);
+
+    wasm = instance.exports;
+    init.__wbindgen_wasm_module = module;
+
+    return wasm;
+}
+
+export default init;
+


**Describe the bug**
TypeError: get(...).cells.get is not a function at Object.getCellByIds

**Expected behavior**
Normal start-up

**Screenshots or gifs**

<image>

**Your environment details**
 - Device:  [e.g.desktop]
 - OS: [e.g. iOS]
 - Browser [e.g. chrome]

diff --git a/index.html b/index.html
index e4b78eae..d1fa9084 100644
--- a/index.html
+++ b/index.html
@@ -4,7 +4,7 @@
     <meta charset="UTF-8" />
     <link rel="icon" type="image/svg+xml" href="/vite.svg" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <title>Vite + React + TS</title>
+    <title>ReactGrid</title>

diff --git a/package.json b/package.json
index f8accda0..1d88c058 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
   "version": "0.0.0",
   "type": "module",
   "scripts": {
-    "dev": "vite",
+    "dev": "ladle serve",
     "build": "tsc && vite build",
     "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
     "preview": "vite preview"
diff --git a/src/App.css b/src/App.css
deleted file mode 100644
index eb2536e1..00000000
--- a/src/App.css
+++ /dev/null
@@ -1,42 +0,0 @@
-#root {
-  max-width: 1280px;
-  margin: 0 auto;
-  padding: 2rem;
-  /* text-align: center; */
-}
-
-.logo {
-  height: 6em;
-  padding: 1.5em;
-  will-change: filter;
-  transition: filter 300ms;
-}
-.logo:hover {
-  filter: drop-shadow(0 0 2em #646cffaa);
-}
-.logo.react:hover {
-  filter: drop-shadow(0 0 2em #61dafbaa);
-}
-
-@keyframes logo-spin {
-  from {
-    transform: rotate(0deg);
-  }
-  to {
-    transform: rotate(360deg);
-  }
-}
-
-@media (prefers-reduced-motion: no-preference) {
-  a:nth-of-type(2) .logo {
-    animation: logo-spin infinite 20s linear;
-  }
-}
-
-.card {
-  padding: 2em;
-}
-
-.read-the-docs {
-  color: #888;
-}
diff --git a/src/App.tsx b/src/App.tsx
deleted file mode 100644
index df256517..00000000
--- a/src/App.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-import { FC } from "react";
-import "./App.css";
-import ReactGrid from "./ReactGrid";
-import { ThemeProvider } from "@emotion/react";
-import { Cell } from "./types/PublicModel";
-import { ErrorBoundary } from "./components/ErrorBoundary";
-
-const TextCell: FC<{ value: string }> = ({ value }) =>
{value}
;
-
-function App() {
-  const cells: Cell[][] = Array(15).fill(null).map((_, rowIndex) => {
-    return Array(7).fill(null).map((_, colIndex) => {
-      return { Template: TextCell, value: `cell [${rowIndex}-${colIndex}]` };
-    });
-  });
-  // const cells: Cell[][] = Array.from({ length: 15 }, (_, rowIndex) =>
-  //   Array.from({ length: 7 }, (_, colIndex) => {
-  //     return {
-  //       Template: TextCell,
-  //       value: `cell [${rowIndex}-${colIndex}]`,
-  //     };
-  //   })
-  // );
-
-  // cells[4][4] = {
-  //   ...cells[4][4],
-  //   // rowSpan: 2,
-  //   colSpan: 2,
-  // };
-
-  cells[0][0] = {
-    ...cells[0][0],
-    colSpan: 2
-  }
-
-  // cells[0][1] = undefined;
-
-  // cells[5][2] = {
-  //   ...cells[5][2],
-  //   rowSpan: 4,
-  //   colSpan: 2,
-  // };
-
-  return (
-    <ErrorBoundary>
-      <ThemeProvider theme={{ grid: {} }}>
-        <ReactGrid
-          id={"rg-test"}
-          // columns={Array.from({ length: 7 }, () => {
-          //   return { width: (Math.random() * (150 - 100 + 1) + 100) << 0 };
-          // })}
-          // rows={Array.from({ length: 15 }, () => {
-          //   return { height: (Math.random() * (50 - 20 + 1) + 20) << 0 };
-          // })}
-          columns={Array.from({ length: 7 }, () => {
-            return { width: 100 };
-          })}
-          rows={Array.from({ length: 15 }, () => {
-            return { height: 25 };
-          })}
-          cells={cells}
-        />
-      </ThemeProvider>
-    </ErrorBoundary>
-  );
-}
-
-export default App;
diff --git a/src/index.css b/src/index.css
index 2c3fac68..8f56bda3 100644
--- a/src/index.css
+++ b/src/index.css
@@ -1,69 +1,28 @@
-:root {
-  font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
-  line-height: 1.5;
-  font-weight: 400;
-
-  color-scheme: light dark;
-  color: rgba(255, 255, 255, 0.87);
-  background-color: #242424;
-
-  font-synthesis: none;
-  text-rendering: optimizeLegibility;
-  -webkit-font-smoothing: antialiased;
-  -moz-osx-font-smoothing: grayscale;
-  -webkit-text-size-adjust: 100%;
-}
-
-a {
-  font-weight: 500;
-  color: #646cff;
-  text-decoration: inherit;
-}
-a:hover {
-  color: #535bf2;
-}
-
 body {
-  margin: 0;
-  display: flex;
-  place-items: center;
-  min-width: 320px;
-  min-height: 100vh;
+  max-width: 650px;
+  margin: 40px auto;
+  padding: 0 10px;
+  font: 18px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+  color: #444;
 }

-h1 {
-  font-size: 3.2em;
-  line-height: 1.1;
+h1,
+h2,
+h3 {
+  line-height: 1.2;
 }

-button {
-  border-radius: 8px;
-  border: 1px solid transparent;
-  padding: 0.6em 1.2em;
-  font-size: 1em;
-  font-weight: 500;
-  font-family: inherit;
-  background-color: #1a1a1a;
-  cursor: pointer;
-  transition: border-color 0.25s;
-}
-button:hover {
-  border-color: #646cff;
-}
-button:focus,
-button:focus-visible {
-  outline: 4px auto -webkit-focus-ring-color;
-}
-
-@media (prefers-color-scheme: light) {
-  :root {
-    color: #213547;
-    background-color: #ffffff;
+@media (prefers-color-scheme: dark) {
+  body {
+    color: #c9d1d9;
+    background: #0d1117;
   }
-  a:hover {
-    color: #747bff;
+
+  a:link {
+    color: #58a6ff;
   }
-  button {
-    background-color: #f9f9f9;
+
+  a:visited {
+    color: #8e96f0;
   }
-}
+}
\ No newline at end of file
diff --git a/src/main.tsx b/src/main.tsx
index 966f17a4..b7b97f98 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -1,10 +1,16 @@
 import React from "react";
 import ReactDOM from "react-dom/client";
-import App from "./App.tsx";
 import "./index.css";

 ReactDOM.createRoot(document.getElementById("root")!).render(
   <React.StrictMode>
-    <App />
+
You probably meant to run Ladle...

+

+      If you want to develop the library, or just play around with the code, run
+

+        (npm | pnpm | yarn) ladle serve
+

+      instead.
+

   </React.StrictMode>
 );