Manifest V3 extension, accessing local functions

What I would like to do

I’m trying to create a small extension for my own usage.
For this extension to work I need to execute a “local” js function call on a web page (that I do not own) and pass the result to my background service worker script.
So far I was either able to:

  • Have a content_scripts be able to execute the “local” function (using “world”: “MAIN”), but this script can’t access chrome.runtime.sendMessage().

Uncaught (in promise) Error: Extension context invalidated.
or

  • Have the content_scripts successfully send message to my background service worker but is not able to access the “local” function

main.js:4 Uncaught (in promise) ReferenceError: myFuncName is not defined

How do I do that?

What I tried

  • In the following files, if I let them as is, I get a ReferenceError on the 4th line of main.js.
  • If I comment this line an uncomment the 5th line it works (but is not what I want)
  • If I add “world”: “MAIN” in the “content_scripts” section of the manifest.json, I get the Extension context invalidated on line 7 of main.js

manifest.json

{
  "manifest_version": 3,
  "name": "ExtName",
  "version": "0.0.1",
  "content_scripts": [
    {
      "matches": ["*://url/*"],
      "js": ["main.js"]
    }
  ],
  "host_permissions": [
        "*://*/*"
    ],
  "background": {
    "service_worker": "background.js"
  }
}

main.js

var intervalID = window.setInterval(checkValue, 1000);

function checkValue() {
    let value = getValue();
    // let value = "value";
    if (value) {
        const response = await chrome.runtime.sendMessage({status: value});
        console.log(response.text);
    }
};

background.js

function handleMessage(request, sender, sendResponse) {
    console.log(request.status);
    sendResponse({ text: "Received!" });
}

chrome.runtime.onMessage.addListener(handleMessage);

in the page script (that I do not control)

function getValue() {
    return "value";
}

Style submenu to open on mobile tap, not just arrow [closed]

I am using a WordPress child theme, and would like to add some additional functionality to my mobile submenus. Right now on mobile devices, I can only open the submenus by tapping the arrow. I am looking to style my submenu items to open on a tap of the parent menu item, not just by tapping the arrow.

I do not want to use a plugin. I was reading some information in google searches, saying that entering # in my parent item’s link field would set it to open the submenu, but that doesn’t work for me… maybe that was for a different situation. I found several related threads that didn’t answer my question. Not sure how developer-heavy this will be, or if it is something solved in the WordPress editor. Thanks

The page I need help with: https://ultraviolet.com/

I have already posted to WordPress forums, and they told me to reach out to my parent theme’s support, which was not helpful. I can hire them to do it if necessary but I feel this should be an easy fix. I am not knowledgeable with javascript but can get by if I know what code to put where.

Rows width is unexpected

One of our sub-offices uses a website where a logo banner appears when someone scrolls down. They were able to do this on a WordPress site using a plugin they don’t pay for, hence I cannot update it. Bit of a snag. It being a basicly static page I decided to mimic the site using html, css and javascript (ow… and bootstrap, come to think of it… not really sure why)

Anyway… I got stuck on the apearing banner. It appears just fine, but I cannot get it to just as wide as the other divs/rows with content. It either appears to wide or to narraw.

This is the html I came up with:

<div class="container">
  <div id="stayontop" class="row hide" align="center"><p>Stay on top</p></div>
  <div class="row short"><p>Nice and short</p></div>
  <div class="row long">
    <p>[A very long piece of tekst to force scrollbar. Snipper away for posting here]</p>
  </div>  
</div>

As you can see: just a container with 3 rows. The last 2 are visible by default and fill the width of the container. The first div has an id “stayontop” and a class “hide”.

This is the css:

#stayontop{
  position: fixed;
  z-index: 2
  width: 100%;
  background-color: red;
  opacity: 50%;
}
#stayontop.hide{
  display: none;
}
#stayontop.show{
  display: block;
}
.short { background-color: yellow;}
.long { background-color: green;}

The position and z-index are supposed to keep the div at the top of page while the rest scrolls underneath it. Seems to work.
The classes “hide” and “show” toggle the vissability. That to seams to work.

The class is toggled using javascript:

window.onscroll = function() { scrollFunction()};

function scrollFunction(){
  console.log(document.documentElement.scrollTop);
  if (document.documentElement.scrollTop > 100){
    console.log('groter');
    document.getElementById("stayontop").classList.add('show'); 
    document.getElementById("stayontop").classList.remove('hide');
  }else{
    console.log('kleiner')
    document.getElementById("stayontop").classList.remove('show'); 
    document.getElementById("stayontop").classList.add('hide');
  }
};

This changes the hide or show based on the number of pixels scrolled. Works as expected.

But I cannot understand why the div “stayontop” does not have the same width as the other divs.

Got a codepen to fiddle around in: https://codepen.io/peter-kaagman/pen/zxYRydL

Inlining of generic types TS TypeScript

The CreateStore type takes a function that returns an object. I need to extract all the properties from this object, remove the functions, and then select the keys of the remaining types and inline those types into the Listeners type.

In the code, there’s an example type tempFunc, and when passing the function directly, everything works, and it results in a type like ["isLoading", [() => ReactElement]]. However, when using generics in CreateStore, the types are lost and generalized to a plain string type.

I don’t know how to preserve inline types when passing them through CreateStore.

import { ReactElement } from "react";

type SetStateInternal<T> = {
    _(
        partial: T | Partial<T> | ((state: T) => T | Partial<T>),
        replace?: false,
    ): void;
    _(partial: T | ((state: T) => T), replace: true): void;
}["_"];

type StoreApi<T> = {
    setState: SetStateInternal<T>;
    getState: (
        partial: T,
        newListener: () => ReactElement,
    ) => { state: T; removeListener: () => void };
    getInitialState: () => T;
    subscribe: (partial: T, newListener: () => ReactElement) => () => void;
};

interface StoreState<T> {}

const func = (set) => ({
    isLoading: false,
    setIsLoading: (newIsLoading: boolean) => set({ isLoading: newIsLoading }),
});

type ExtractState<S> = S extends {
    getState: (...args: any) => { state: infer T };
}
    ? T
    : never;

type Get<T> = {
    [K in keyof T as T[K] extends (...args: any) => any ? never : K]: T[K];
};

type Listeners<T> = Map<T, Set<() => ReactElement>>;

type InitialListeners<T> = [keyof Get<T>, [() => ReactElement]] extends [
    infer K,
    [() => ReactElement],
]
    ? [K, [() => ReactElement]]
    : T;

type tempFunc = temp1<typeof func>;

type temp1<T> = T extends (...args: any) => any
    ? [keyof Get<ReturnType<T>>, [() => ReactElement]] extends [
          infer K,
          [() => ReactElement],
      ]
        ? [K, [() => ReactElement]]
        : never
    : never;

export type StateCreator<T> = (
    setState: StoreApi<T>["setState"],
    getState: StoreApi<T>["getState"],
    store: StoreApi<T>,
) => T;

export type CreateStore = {
    <T, U = T extends (...args: any) => any ? ReturnType<T> : T>(
        initializer: StateCreator<T>,
        initialListeners?: InitialListeners<U>,
    ): StoreApi<T>;

    <T, U = T extends (...args: any) => any ? ReturnType<T> : T>(): (
        initializer: StateCreator<T>,
        initialListeners?: InitialListeners<U>,
    ) => StoreApi<T>;
};

type CreateStoreInit = <
    T,
    U = T extends (...args: any) => any ? ReturnType<T> : T,
>(
    initializer: StateCreator<T>,
    initialListeners?: InitialListeners<U>,
) => StoreApi<T>;

const createStoreInit: CreateStoreInit = (createState, createListeners) => {
    type TState = ReturnType<typeof createState>;
    let state: TState;
    const listeners: Listeners<keyof Get<TState>> = new Map();
    
    ... 
    never mind
};

export const createStore: CreateStore = ((initialState, createListeners) =>
    initialState
        ? createStoreInit(initialState, createListeners)
        : createStoreInit) as CreateStore;

Invalid definition of location schema stated

The below listed function is not giving captains in radius data while creating a ride with user. It is accepting the latitude and longitude. I tried to log the data at every step, whole code is working fine except getCaptainsInRadius function is returning an empty array. I thought maybe there can be an issue with how I defined location in the schema. Let me know what you guys think?

module.exports.getCaptainsInRadius = async (ltd, lng, radius) => {
  try {
    const captains = await captainModel.find({
      location: {
        $geoWithin: {
          $centerSphere: [[ltd, lng], radius / 6371],
        },
      },
    });
    console.log("Captains found:", captains);
    return captains;
  } catch (err) {
    console.error("Error fetching captains:", err);
    throw err;
  }

I defined location schema like this:

location: {
    ltd: {
      type: Number,
    },
    lng: {
      type: Number,
    },
  }.

Now this is the alternative suggested by Chatgpt, Github copilot and other AI bots as per the MongoDB defined schema:

location: {
  type: {
    type: String,
    enum: ["Point"], // Must be "Point" for GeoJSON
    required: true,
  },
  coordinates: {
    type: [Number], // Array of numbers: [longitude, latitude]
    required: true,
  },
},

I tried using this method too. Still getting no response just plain empty array

How to change the type of the vertical grid line and extend the horizontal lines beyond the chart in ngx-echarts?

I am trying to create a beautiful chart to visualize the dynamics of school students’ performance.

I am using Angular version 17.3.0, ECharts version 5.5.1, and ngx-echarts version 17.2.0.

I have this config:

{
    grid: {
      left: 20,
      right: 25,
      top: 0,
      bottom: 40,
    },
    xAxis: {
      type: 'category',
      data: [
        '9 фев',
        '16 фев',
        '23 фев',
        '1 мар',
        '8 мар',
        '15 мар',
        '22 мар',
        '29 мар',
        '5 апр',
        '12 апр',
      ],
      boundaryGap: false,
      axisLabel: {
        interval: 0,
        overflow: 'truncate',
        color: '#86909C',
        fontSize: 14,
        fontWeight: 'normal',
        margin: 10,
        formatter: (value: string) => {
          const [day, month] = value.split(' ');
          return `{a|${day}}n{b|${month}}`;
        },
        rich: {
          a: {
            fontSize: 14,
            align: 'center',
          },
          b: {
            fontSize: 14,
            align: 'center',
          },
        },
      },
      axisLine: {
        show: false,
      },
      axisTick: {
        show: false,
      },
      splitLine: {
        show: true,
        lineStyle: {
          type: 'dashed',
        },
      },
    },
    yAxis: {
      type: 'value',
      min: 1,
      max: 6,
      interval: 1,
      position: 'right',
      splitLine: {
        show: true,
        lineStyle: {
          type: 'solid',
        },
      },
      axisLabel: {
        color: '#0B1F33',
        fontSize: 16,
        fontWeight: 'bold',
        margin: 12,
        formatter: (value: number) => {
          if (value < 2 || value > 5) {
            return '';
          } else {
            return value.toString();
          }
        },
      },
    },
    series: [
      {
        name: 'Test',
        type: 'line',
        smooth: true,
        data: [null, 4, null, null, 4.57, 5, 4],
        connectNulls: true,
        emphasis: {
          focus: 'series',
        },
        itemStyle: {
          color: '#0E69D5',
          borderColor: '#ffffff',
          borderWidth: 1,
        },
        lineStyle: {
          color: '#185AC5',
        },
        symbol: 'circle',
        symbolSize: 7,
        markLine: {
          symbol: 'none',
          tooltip: { show: false },
          label: { show: false },
          data: [
            {
              xAxis: 6,
              lineStyle: {
                color: '#0E69D5',
                width: 1,
                type: 'solid',
              },
              emphasis: {
                disabled: true,
              },
            },
          ],
        },
        areaStyle: {
          color: {
            type: 'linear',
            x: 0,
            y: 0,
            x2: 0,
            y2: 1,
            colorStops: [
              { offset: 0, color: 'rgba(192, 216, 253, 0.80)' },
              { offset: 1, color: 'rgba(237, 247, 255, 0.08)' },
            ],
          },
        },
      },
    ],
  }

This is how it looks:
my chart

Could you please tell me how I can change the type of the line marked in red to solid without affecting the other vertical lines?
And how can I make the horizontal lines extend beyond the boundaries of the chart as I’ve shown in green? Or is this impossible to do with ECharts? If it’s impossible, could you please recommend other libraries?

How to iterate over an object reactively?

I need to iterate over an object. The object is declared with:

const obj = $state({ 
    one: { checked: true }, 
    two: { checked: false }, 
    three: { checked: true } 
});

In my Svelte templating code, I do this:

{#each Object.entries(obj) as [key, value]}
        <div class="mb-8">
            {key}: {value.checked}
            <input type="checkbox" {value} onchange={e => obj[key].checked = e.target.checked}>
        </ion-toggle>
    </div>
{/each}

Checking the checkbox does update the value of one of the items of my object, but does not change the value of {value.checked} in the DOM. My suspicion is that obj itself is reactive, but value within obj is not. How do I fix this?

(Note: I’m actually using Ionic and <ion-toggle> instead of <input type="checkbox"> – I know there is a better way to bind the value (bind:value), but this method works throughout the rest of my app for <ion-toggle>.)

I have also attempted making the obj a SvelteMap<string, ClassVersion>, where ClassVersion is:

class MyObj {
   checked = $state(false);
}

Shopify Update Variants Options

Recently I need to update the graphql api version of my application,I am currently using ApiVersion.April24, and updating the options to variants is done using the respective graphql structure:

 static update_variant_data(variantId,data){
    const variantMutation = `
        mutation updateProductVariantMetafields($input: ProductVariantInput!) {
            productVariantUpdate(input: $input) {
                productVariant {
                    title
                    price
                    compareAtPrice
                    price
                    position
                    weight
                    weightUnit
                    sku
                    barcode
                    id
                    selectedOptions {
                        name
                        optionValue {
                            id
                            name
                        }
                        value
                    }
                    metafields(first: 3) {
                        edges {
                            node {
                                id
                                namespace
                                key
                                value
                            }
                        }
                    }
                }
                userErrors {
                     message
                    field
                }
            }
        }
    `;

    const variantVariable = {
        input: {
            id:"gid://shopify/ProductVariant/"+variantId,
            ...data
        }
    }

    return {query:variantMutation,variables:variantVariable};
}

and the value I send is the following

{
     "variant_id":aaaaaa,
     "options":["Default Title"],
     "price":"122.00",
     "compare_at_price":"100.00",
     "sku":"TD5200BLXXZZ",
     "barcode":"",
     "grams":0,
     "weight":0,
     "weight_unit":""
  }

https://shopify.dev/docs/api/admin-graphql/2024-07/mutations/productvariantupdate

The problem appeared in more recent versions of API where this method is deprecated.So I used the new productVariantBulkUpdate model, I manage to update everything without problems but I am unable to update the options for the variant. (https://shopify.dev/docs/api/admin-graphql/2024-07/mutations/productVariantsBulkUpdate)

static update_variant_data(variantId,prodId,data){
    const variantMutation = `
       mutation productVariantsBulkUpdate($productId: ID!, $variants: [ProductVariantsBulkInput!]!) {
            productVariantsBulkUpdate(productId: $productId, variants: $variants) {
              
                productVariants {
                    id
                    compareAtPrice
                    price
                    barcode
                    inventoryItem {
                        sku
                        measurement {
                            weight{
                                unit
                                value
                            }
                        }
                    }
                    optionValues {
                        id
                        name
                    }
                }
                userErrors {
                    field
                    message
                }
            }
        }
    `;

    const variantVariable = {
        productId:"gid://shopify/Product/"+prodId,
        variants:[
            { 
                id:"gid://shopify/ProductVariant/"+variantId,
                price:data.price,
                compareAtPrice:data.promo,
                inventoryItem:{
                    sku:data.sku,
                    measurement:{
                        weight:{
                            unit:data.weightUnit,
                            value:data.weight
                        }
                    }
                },
                optionValues:{
                    id:"gid://shopify/ProductOptionValue/zzzzzz",
                    name:"Default Title"
                }
            }
        ]
    }

    return {query:variantMutation,variables:variantVariable};
}

But it doesn’t work even though I followed what they give in the documentation:

"Error Update Variant DataError: Field 'optionValues' doesn't exist on type 'ProductVariant'"

I searched on the internet but generally the worst I found was the most confusing. If you have any ideas or a different approach, please help me.

I am an artist and I want my artwork tagged but am not able to get the JS together [closed]

Searching for tag mostly leads to html-tag. I have a pen of this on codepen: https://codepen.io/michielschukking/pen/ZEgwWvp

Turning tags on and off works because of this function:

tags = []
currentImages = []

function tagOnOff(obj) {
  obj.classList.toggle("carribean");
  tag = obj.innerHTML;
  console.log("Klik!"); 
  console.log("Aantal tags:"); 
  console.log(tags.length);
  displayedImagesNew = []

  // Als tag er nog niet is 'push' erbij, anders verwijder selectie uit array.
  if(!tags.includes(tag)){          
    tags.push(tag);              
  } else{
    tags.splice(tags.indexOf(tag), 1); 
  }
  // Met de juiste geselecteerde tags.
  displayTagsImages(tags, images);
}

On this html:

  <span onclick="tagOnOff(this)" class="tag random">karakter</span>
  <span onclick="tagOnOff(this)" class="tag random">kind</span>
  <span onclick="tagOnOff(this)" class="tag random">dier</span>
  <span onclick="tagOnOff(this)" class="tag random">plant</span>
  <span onclick="tagOnOff(this)" class="tag random">auto</span>
  <span onclick="tagOnOff(this)" class="tag random">vaartuig</span>
  <span onclick="tagOnOff(this)" class="tag random">vliegtuig</span>
  <span onclick="tagOnOff(this)" class="tag random">landschap</span>
  <span onclick="tagOnOff(this)" class="tag random">gebouw</span>
  <span onclick="tagOnOff(this)" class="tag random">muziekinstument</span>
  <span onclick="tagOnOff(this)" class="tag random">wapen</span>
  <span onclick="tagOnOff(this)" class="tag abstract">licht & donker (stijl)</span>
  <span onclick="tagOnOff(this)" class="tag abstract">klare lijn (stijl)</span>
  <span onclick="tagOnOff(this)" class="tag abstract">spontaan (stijl)</span>
</div>

From these test images

let images = [{ title: "Orchidee",
url: "./img/IMG_0001.JPG", 
tags: ["plant", "spontaan (stijl"]}, { title: "Bergwandelaar",
url: "./img/IMG_0025.JPG", 
tags: ["plant", "karakter"]}, { title: "Kantoor uit/Vrouw aan",
url: "./img/IMG_0007.JPG", 
tags: ["gebouw", "karakter"]}, { title: "Zeilboot in worsteling",
url: "./img/IMG_0007 (1).JPG", 
tags: ["spontaan", "vaartuig"]}, { title: "Een 'haven' in Afrika",
url: "./img/IMG_0342.jpeg", 
tags: ["gebouw", "vaartuig", "potlood"]}, { title: "Een 'haven' in Veere",
url: "./img/IMG_0343.jpeg", 
tags: ["gebouw", "vaartuig", "auto", "potlood"]}, { title: "Grot restaurant",
url: "./img/IMG_1621.jpeg", 
tags: ["gebouw", "karakter"]}, { title: "Omgegooid rood sap",
url: "./img/IMG_1624.jpeg", 
tags: ["karakter", "kind"]}, { title: "Liggend naakt",
url: "./img/IMG_2328.JPG", 
tags: ["karakter", "spontaan"]}, { title: "Mei",
url: "./img/IMG_4222.jpeg", 
tags: ["landschap", "gebouw"]}

]

I got this far.

function displayTagsImages(tags, allImages) {
  console.log("tags:")
  console.log(tags)

  console.log("allImages:")
  console.log(allImages)

  console.log("currentImages:")
  console.log(currentImages)
  console.log(tags.length)

  // current images is bij o of 1e tag all images

  // Eerste tag selectie
  if (tags.length === 0 || tags.length === 1) { 
    currentImages = allImages
  }

  console.log("currentImages!!!:")
  console.log(currentImages)


  // Voor elke tag uit vorige currentImages.
  currentImagesNew = []
  tags.forEach(t => {
    currentImages.forEach(i => {
      if (i.tags.includes(t)) {
        currentImagesNew.push(i);
      } else{
        currentImagesNew.splice(currentImagesNew.indexOf(i), 1);
      } 
    });
  })
  currentImages = currentImagesNew
  // Werkt. Maar nog 1 tag erbij niet. "karakter", maar "gebouw erbij", en 
  // "karakter" nog steeds volledig.

  console.log("currentImages after loop:")
  console.log(currentImages)

  console.log("EINDE")
  console.log("EINDE")
  console.log("EINDE")
  // let text = "";
  // document.getElementById("filtered-sketches").innerHTML = text;
}

It sometimes works but not neatly. It leaves gaps, saves doubles. This is way too hard for me, does someone know any code that does work? Or sees the mistakes I made immediately?

In javascript, whats return keyword of functions? [closed]

I started javascript, and understood the main key to learn coding is not to actually memorise it but to understand it. I learnt functions almost 1 week ago but I can’t understand what return; does? I mean many said it returns the value but I still didn’t understood it.

I saw many youtube courses..and even searched google.

Outlook WebAddin doesnt show up on Outlook Client

I have created an outlook web addin with visualstudio 2022 (I used the default template)
It works in the Outlook Web Client (OWA).
On an Outlook client with Office365 the addin is not displayed.
If I go to the addins under Installed web addins, I can see that the addin is active.

Could it be that there is an error in my manifest?

I would be grateful for any tips

<?xml version="1.0" encoding="UTF-8"?>
<!--Created:ce44715c-8c4e-446b-879c-ea9ebe0f09c8-->
<!-- Weitere Informationen zum XML-Manifest für Office-Add-Ins finden Sie unter https://go.microsoft.com/fwlink/?linkid=2252563. -->
<OfficeApp 
          xmlns="http://schemas.microsoft.com/office/appforoffice/1.1" 
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
          xmlns:bt="http://schemas.microsoft.com/office/officeappbasictypes/1.0" 
          xmlns:mailappor="http://schemas.microsoft.com/office/mailappversionoverrides/1.0" 
          xsi:type="MailApp">


  <!-- WICHTIG: Die ID muss eindeutig für Ihr Add-In sein. Wenn Sie dieses Manifest erneut verwenden, stellen Sie sicher, dass Sie diese ID in eine neue GUID ändern. -->
  <Id>cf933e3b-9987-4ef0-a079-a4b3defd66d0</Id>

  <Version>1.0.0.0</Version>
  <ProviderName>Spam Reporting</ProviderName>
  <DefaultLocale>de-CH</DefaultLocale>
  <!-- Der Anzeigename Ihres Add-Ins. Er wird im Store und an verschiedenen Stellen in der Office-Benutzeroberfläche verwendet, z. B. im Dialogfeld "Add-Ins". -->
  <DisplayName DefaultValue="Email Melden" />
  <Description DefaultValue="Email Melden"/>

  <IconUrl DefaultValue="https://reporting.test.ch/Images/icon32.png"/>
  <HighResolutionIconUrl DefaultValue="https://reporting.test.ch/Images/icon80.png"/>

  <SupportUrl DefaultValue="http://www.exchange.test.ch" />
  <!-- Domänen, die beim Navigieren zulässig sind. Wenn Sie z. B. "ShowTaskpane" verwenden und dann ein href-Link auftritt, ist die Navigation nur zulässig, wenn sich die Domäne in dieser Liste befindet. -->
  <AppDomains>
    <AppDomain>exchange.test.ch</AppDomain>
    <AppDomain>owa.test.ch</AppDomain>
  </AppDomains>
  
  <Hosts>
    <Host Name="Mailbox" />
  </Hosts>
  <Requirements>
    <Sets>
      <Set Name="Mailbox" MinVersion="1.1" />
    </Sets>
  </Requirements>
    
  <FormSettings>
    <Form xsi:type="ItemRead">
      <DesktopSettings>
        <SourceLocation DefaultValue="https://reporting.test.ch/MessageRead.html"/>
        <RequestedHeight>250</RequestedHeight>
      </DesktopSettings>
    </Form>
  </FormSettings>

  <Permissions>ReadWriteMailbox</Permissions>
  
  <Rule xsi:type="RuleCollection" Mode="Or">
    <Rule xsi:type="ItemIs" ItemType="Message" FormType="Read" />
  </Rule>

  <VersionOverrides xmlns="http://schemas.microsoft.com/office/mailappversionoverrides" xsi:type="VersionOverridesV1_0">
    <VersionOverrides xmlns="http://schemas.microsoft.com/office/mailappversionoverrides/1.1" xsi:type="VersionOverridesV1_1">
      <Requirements>
        <bt:Sets DefaultMinVersion="1.1">
          <bt:Set Name="Mailbox" />
        </bt:Sets>
      </Requirements>
      <Hosts>
        <Host xsi:type="MailHost">

          <DesktopFormFactor>
            <FunctionFile resid="functionFile" />

            <ExtensionPoint xsi:type="MessageReadCommandSurface">
              <OfficeTab id="TabDefault">
                <Group id="msgReadGroup">
                  <Label resid="groupLabel" />
                  <Control xsi:type="Button" id="msgReadOpenPaneButton">
                    <Label resid="taskPaneButtonLabel" />
                    <Supertip>
                      <Title resid="taskPaneButtonLabel" />
                      <Description resid="taskPaneButtonDescription" />
                    </Supertip>
                    <Icon>
                      <bt:Image size="16" resid="icon16" />
                      <bt:Image size="32" resid="icon32" />
                      <bt:Image size="80" resid="icon80" />
                    </Icon>
                    <Action xsi:type="ShowTaskpane">
                      <SourceLocation resid="messageReadTaskPaneUrl" />
                      
                    </Action>
                  </Control>
                  
                </Group>
              </OfficeTab>
            </ExtensionPoint>
          </DesktopFormFactor>
        </Host>
      </Hosts>

      <Resources>
        <bt:Images>
          <bt:Image id="icon16" DefaultValue="https://reporting.test.ch/Images/icon16.png"/>
          <bt:Image id="icon32" DefaultValue="https://reporting.talus.ch/Images/icon32.png"/>
          <bt:Image id="icon80" DefaultValue="https://reporting.test.ch/Images/icon80.png"/>
        </bt:Images>
        <bt:Urls>
          <bt:Url id="messageReadTaskPaneUrl" DefaultValue="https://reporting.test.ch/MessageRead.html"/>
        </bt:Urls>
        <bt:ShortStrings>
          <bt:String id="groupLabel" DefaultValue="My Add-in Group"/>        
          <bt:String id="taskPaneButtonLabel" DefaultValue="E-Mail melden"/>
        </bt:ShortStrings>
        <bt:LongStrings>
          <bt:String id="taskPaneButtonDescription" DefaultValue="Melden"/>
         
        </bt:LongStrings>
      </Resources>
    </VersionOverrides>
  </VersionOverrides>
</OfficeApp>

How to remove the display of the upper border of the chart grid in ngx-echarts?

I am trying to create a beautiful chart to visualize the dynamics of school students’ performance.

I am using Angular version 17.3.0, ECharts version 5.5.1, and ngx-echarts version 17.2.0.

I have this config:

{
    grid: {
      left: 20,
      right: 25,
      top: 0,
      bottom: 40,
    },
    xAxis: {
      type: 'category',
      data: [
        '9 фев',
        '16 фев',
        '23 фев',
        '1 мар',
        '8 мар',
        '15 мар',
        '22 мар',
        '29 мар',
        '5 апр',
        '12 апр',
      ],
      boundaryGap: false,
      axisLabel: {
        interval: 0,
        overflow: 'truncate',
        color: '#86909C',
        fontSize: 14,
        fontWeight: 'normal',
        margin: 10,
        formatter: (value: string) => {
          const [day, month] = value.split(' ');
          return `{a|${day}}n{b|${month}}`;
        },
        rich: {
          a: {
            fontSize: 14,
            align: 'center',
          },
          b: {
            fontSize: 14,
            align: 'center',
          },
        },
      },
      axisLine: {
        show: false,
      },
      axisTick: {
        show: false,
      },
      splitLine: {
        show: true,
        lineStyle: {
          type: 'dashed',
        },
      },
    },
    yAxis: {
      type: 'value',
      min: 1,
      max: 6,
      interval: 1,
      position: 'right',
      splitLine: {
        show: true,
        lineStyle: {
          type: 'solid',
        },
      },
      axisLabel: {
        color: '#0B1F33',
        fontSize: 16,
        fontWeight: 'bold',
        margin: 12,
        formatter: (value: number) => {
          if (value < 2 || value > 5) {
            return '';
          } else {
            return value.toString();
          }
        },
      },
    },
    series: [
      {
        name: 'Test',
        type: 'line',
        smooth: true,
        data: [null, 4, null, null, 4.57, 5, 4],
        connectNulls: true,
        emphasis: {
          focus: 'series',
        },
        itemStyle: {
          color: '#0E69D5',
          borderColor: '#ffffff',
          borderWidth: 1,
        },
        lineStyle: {
          color: '#185AC5',
        },
        symbol: 'circle',
        symbolSize: 7,
        markLine: {
          symbol: 'none',
          tooltip: { show: false },
          label: { show: false },
          data: [
            {
              xAxis: 6,
              lineStyle: {
                color: '#0E69D5',
                width: 1,
                type: 'solid',
              },
              emphasis: {
                disabled: true,
              },
            },
          ],
        },
        areaStyle: {
          color: {
            type: 'linear',
            x: 0,
            y: 0,
            x2: 0,
            y2: 1,
            colorStops: [
              { offset: 0, color: 'rgba(192, 216, 253, 0.80)' },
              { offset: 1, color: 'rgba(237, 247, 255, 0.08)' },
            ],
          },
        },
      },
    ],
  }

This is how it looks:
my chart
How can I hide the line I marked in red? Or is this impossible to do with ECharts? If it’s impossible, could you please recommend other libraries?

I can’t load the jspdf script in my content.js script in Google Chrome extension

Context

Hello, I’ll start saying that I’m sorry if my code sucks, I am a backend dev, not a frontend, I know a little of JS.
Btw I am trying to do this Chrome extension with manifest V3 that basically gets all the modules, all the units in the modules, of a Microsoft Learning Path and it exports them as a PDF using the jspdf library.

Structure & Code

My extension has those files:

  • content.js
  • jspdf.umd.min.js -> This is the jspdf script
  • manifest.js
  • popup.html
  • popup.js
  • styles.css

manifest.json

    {
    "manifest_version": 3,
    "name": "MS Learn Exporter",
    "version": "1.0",
    "description": "Esports Microsoft Learning Path in.pdf format.",
    "permissions": ["activeTab", "downloads", "scripting"],
    "host_permissions": [
        "https://learn.microsoft.com/*"
    ],
    "action": {
        "default_popup": "popup.html"
    },
    "web_accessible_resources": [
        {
            "resources": ["jspdf.umd.min.js"],
            "matches": ["<all_urls>"]
        }
    ]
}

popup.js

document.getElementById('startExport').addEventListener('click', () => {
    console.log('Popup button clicked');

    chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
        if (!tabs || tabs.length === 0) {
            console.error('No active tabs found');
            return;
        }

        chrome.scripting.executeScript({
            target: { tabId: tabs[0].id },
            files: ['content.js']
        }, () => {
            if (chrome.runtime.lastError) {
                console.error('Error injecting script:', chrome.runtime.lastError);
            } else {
                console.log('Content script injected successfully');
                chrome.tabs.sendMessage(tabs[0].id, { action: 'startExport' });
            }
        });
    });
});

content.js

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
    if (message.action === "startExport") {
        console.log("Received message to start export");
        loadJsPDF().then(() => {
            startExport();
            sendResponse({ status: "Export started" });
        }).catch(error => {
            console.error('Failed to load jsPDF:', error);
            sendResponse({ status: "Failed to load jsPDF" });
        });
    }
});

function loadJsPDF() {
    return new Promise((resolve, reject) => {
      if (typeof jsPDF !== 'undefined') {
        // jsPDF is already loaded, resolve immediately
        resolve();
      } else {
        // jsPDF is not loaded, try to load it.
        const script = document.createElement('script');
        script.src = chrome.runtime.getURL('./jspdf.umd.min.js'); // Use the UMD version of jsPDF
        script.type = 'text/javascript';
        script.async = true; // Load asynchronously
        script.onload = () => {
          if (typeof jsPDF !== 'undefined') {
            // jsPDF loaded successfully
            resolve();
          } else {
            // jsPDF was not loaded
            reject(new Error('jsPDF is undefined after loading'));
          }
        };
        script.onerror = (error) => {
          reject(new Error(`Failed to load jsPDF: ${error}`));
        };
        document.head.appendChild(script);
      }
    });
  }

// Funzione principale per l'export
function startExport() {
    console.log('Export function started');

    const learningPathLinks = getLearningPathLinks();
    console.log('Found learning paths:', learningPathLinks);

    let completeContent = '';

    (async () => {
        for (const pathLink of learningPathLinks) {
            completeContent += await processLearningPath(pathLink);
        }

        generatePDF(completeContent);
        console.log('Export completed!');
    })();
}

// Ottiene i link dei learning paths
function getLearningPathLinks() {
    return [...document.querySelectorAll('#study-guide-list a')]
        .map(a => a.href)
        .filter((value, index, self) => self.indexOf(value) === index); // Rimuove duplicati
}

// Processa ogni learning path per estrarre le unità tramite API
async function processLearningPath(pathUrl) {
    console.log(`Processing learning path: ${pathUrl}`);

    const pathIdMatch = pathUrl.match(/paths/(.*?)//);
    if (!pathIdMatch) {
        console.warn(`No valid path ID found in URL: ${pathUrl}`);
        return '';
    }

    const pathId = pathIdMatch[1];
    const apiUrl = `https://learn.microsoft.com/api/hierarchy/paths/learn.wwl.${pathId}?locale=en-us`;

    try {
        const response = await fetch(apiUrl, {
            headers: {
                'Accept': 'application/json'
            }
        });

        if (!response.ok) {
            console.error(`API request failed for ${apiUrl}:`, response.status);
            return '';
        }

        const data = await response.json();
        const moduleLinks = data.modules?.[0]?.units?.map(unit => unit.url) || [];

        console.log("Module Links:", moduleLinks);

        let pathContent = '';

        for (const link of moduleLinks) {
            pathContent += await processUnit(link);
        }

        return pathContent;

    } catch (error) {
        console.error(`Error fetching API for module ${pathUrl}:`, error);
        return '';
    }
}

// Scarica il contenuto di ogni unità
async function processUnit(unitUrl) {
    const fullUrl = `https://learn.microsoft.com/en-us${unitUrl}`;
    console.log(`Processing unit: ${fullUrl}`);
    const unitDocument = await fetchPage(fullUrl);

    const content = unitDocument.querySelector('#unit-inner-section');
    if (content) {
        return content.innerText + 'nn';
    } else {
        console.warn(`No content found in unit: ${fullUrl}`);
        return '';
    }
}

// Funzione per ottenere e parse-are una pagina HTML
async function fetchPage(url) {
    const response = await fetch(url);
    const text = await response.text();
    const parser = new DOMParser();
    return parser.parseFromString(text, 'text/html');
}

// Funzione per generare un PDF
function generatePDF(content) {
    if (!window.jspdf || !window.jspdf.jsPDF) {
        console.error('jsPDF is not available yet.');
        return;
    }

    const { jsPDF } = window.jspdf;
    const doc = new jsPDF();
    const pageWidth = doc.internal.pageSize.getWidth() - 20;

    const splitContent = doc.splitTextToSize(content, pageWidth);
    doc.text(splitContent, 10, 10);

    doc.save('exported_content.pdf');
}

Issue

When I check my browser source the jspdf.umd.min.js is present, but I get those errors:

content.js:8 Failed to load jsPDF: Error: jsPDF is undefined after loading
    at script.onload (content.js:52:20)
(anonymous) @ content.js:8
Promise.catch
(anonymous) @ content.js:7Understand this errorAI
VM4133 content.js:8 Failed to load jsPDF: Error: jsPDF is undefined after loading
    at script.onload (content.js:52:20)

could you help me understand where/what I am doing wrong?

Thank you

WordPress Plugin development – React integration is not working

I am developing a wordpress Plugin and during development, I decided to continue the code with react, instead of Html. In order to do that I needed to build the React environment on WordPress. I have built it and it was compiling correctly. Thereby my goal was to see if react was also rendering on the wordpress page, but on the wordpress settings page of my Plugin was nothing to see from the react test component.

The expected result is that the contents of the react file renders on the settings page. For this task I got help from the WordPress Documentation. I would be very happy if any of you can help me to solve this bug.

Here is how I did the enque on the php file(the whole contents of the php file):
Look at the functions justinLegalRegisterTestPage, justinLegalRenderTestPage, justinLegalEnqueueTestPageScripts which are relevant for the react component.

<?php

/**
 * Justin Legal Plugin – Admin Settings
 *
 * This file adds the Justin Legal settings page to the admin, registers common settings
 * (including AJAX handlers), and renders the settings page with tabs by including
 * tab-specific files.
 *
 * @package JustinLegalPlugin
 */


// Prevent direct access.
if (! defined('ABSPATH')) {
    exit;
}

// Always include the tab files so that their registration functions run.
require_once plugin_dir_path(__FILE__) . 'settings/main-settings-tab.php';
require_once plugin_dir_path(__FILE__) . 'settings/compliance-tab.php';

/**
 * Add the Justin Legal settings page to the admin menu.
 */
function justinLegalAddSettingsPage()
{
    add_menu_page(
        'Justin Legal Settings',           // Page title.
        'Justin Legal',                    // Menu title.
        'manage_options',                  // Capability.
        'justin-legal-settings',           // Menu slug.
        'justinLegalRenderSettingsPage',   // Callback function.
        'dashicons-admin-generic',         // Icon.
        100                                // Position.
    );
}
add_action('admin_menu', 'justinLegalAddSettingsPage');


/**
 * Render the settings page with tabs.
 * The URL remains unchanged (no query parameters are appended) because the tab switching
 * is handled via JavaScript.
 */
function justinLegalRenderSettingsPage()
{
?>
    <div class="wrap">
        <h1>Justin Legal Settings</h1>
        <h2 class="nav-tab-wrapper">
            <a href="#" class="nav-tab nav-tab-active" data-tab="main">Main Settings</a>
            <a href="#" class="nav-tab" data-tab="compliance">Compliance</a>
        </h2>

        <div id="tab-main">
            <?php justinLegalRenderMainSettingsTab(); ?>
        </div>
        <div id="tab-compliance" style="display: none;">
            <?php justinLegalRenderCompliance(); ?>
        </div>
    </div>
<?php
}

/**
 * AJAX handler to retrieve profile data from the external API.
 */
function justinLegalGetProfileData()
{
    if (! current_user_can('manage_options')) {
        wp_send_json_error('Permission denied');
    }

    $options = get_option('justin_legal_settings');
    $encodedId = '';
    if (isset($_POST['lawfirm']) && ! empty($_POST['lawfirm'])) {
        $encodedId = sanitize_text_field($_POST['lawfirm']);
    } else {
        $encodedId = isset($options['lawfirm']) && ! empty($options['lawfirm']) ? $options['lawfirm'] : 'sFuCvloJ';
    }

    $apiUrl = 'https://justin-api-dev.azurewebsites.net/api/v1/LawFirm/encoded/' . urlencode($encodedId);
    $response = wp_remote_get($apiUrl);
    if (is_wp_error($response)) {
        wp_send_json_error($response->get_error_message());
    }
    $code = wp_remote_retrieve_response_code($response);
    $body = wp_remote_retrieve_body($response);
    if ($code != 200) {
        wp_send_json_error('API returned HTTP code ' . $code . ': ' . $body);
    }
    $data = json_decode($body, true);
    if (empty($data) || ! is_array($data)) {
        wp_send_json_error('Empty data returned from API');
    }
    $kanzleiName = isset($data['name']) ? sanitize_text_field($data['name']) : '';
    $options['kanzlei_name_customer'] = $kanzleiName;

    update_option('justin_legal_settings', $options);
    wp_send_json_success(array('kanzlei_name' => $kanzleiName));
}
add_action('wp_ajax_justin_legal_get_profile_data', 'justinLegalGetProfileData');

/**
 * Enqueue admin scripts on the Justin Legal settings page.
 */
function justinLegalAdminScripts($hook)
{
    if ($hook !== 'toplevel_page_justin-legal-settings') {
        return;
    }
    wp_enqueue_script(
        'justin-legal-admin-js',
        plugin_dir_url(__FILE__) . 'js/admin.js',
        array('jquery'),
        filemtime(plugin_dir_path(__FILE__) . 'js/admin.js'),
        true
    );
}
add_action('admin_enqueue_scripts', 'justinLegalAdminScripts');


/**
 * Enqueue the Justin Legal widget CSS in WP Admin
 */
function justinLegalEnqueueWidgetCSS($hook)
{
    if ($hook === 'toplevel_page_justin-legal-settings') {
        wp_enqueue_style(
            'justin-legal-widget-css',
            'https://widgets.justin-legal.com/index.css',
            array(), // without array() it will not work
            null
        );
    }

    wp_enqueue_style(
        'justin-legal-admin-overrides',
        plugin_dir_url(__FILE__) . 'css/admin.css',
        array('justin-legal-widget-css'),
        filemtime(plugin_dir_path(__FILE__) . 'css/admin.css')
    );
}
add_action('admin_enqueue_scripts', 'justinLegalEnqueueWidgetCSS');


// Register the Test Page as a Subpage under the Justin Legal settings page.
function justinLegalRegisterTestPage()
{
    add_submenu_page(
        'justin-legal-settings',             // Correct parent slug (matches your add_menu_page slug)
        __('Test Page', 'justin-legal'),     // Page title
        __('Test Page', 'justin-legal'),     // Menu title
        'manage_options',                    // Capability required
        'test-page',                         // Menu slug
        'justinLegalRenderTestPage'          // Callback to render the page
    );
}
add_action('admin_menu', 'justinLegalRegisterTestPage');

function justinLegalRenderTestPage()
{
    printf(
        '<div class="wrap" id="justin-legal-test-page">%s</div>',
        esc_html__('Loading…', 'justin-legal')
    );
}


function justinLegalEnqueueTestPageScripts($hook)
{
    if ($hook !== 'justin-legal-settings_page_test-page') {
        return;
    }

    $asset_file = plugin_dir_path(__FILE__) . 'build/index.asset.php';
    if (! file_exists($asset_file)) {
        wp_die('The asset file for the Test Page does not exist. Run `npm run build` to create it.');
    }
    $asset = include $asset_file;

    wp_enqueue_script(
        'justin-legal-admin-script',
        plugin_dir_url(__FILE__) . 'build/index.js',
        $asset['dependencies'],
        $asset['version'],
        array(
            'in_footer' => true,
        )
    );

    wp_localize_script(
        'justin-legal-admin-script',
        'justinLegalData',
        array(
            'nonce' => wp_create_nonce('justin_legal_nonce'),
            'ajaxUrl' => admin_url('admin-ajax.php')
        )
    );
}
add_action('admin_enqueue_scripts', 'justinLegalEnqueueTestPageScripts');

And here is the src/index.jsx:

import domReady from "@wordpress/dom-ready";
import { createRoot } from "@wordpress/element";

const SettingsPage = () => {
  return <div>Placeholder for settings page</div>;
};

domReady(() => {
  const container = document.getElementById("justin-legal-test-page");

  if (container) {
    const root = createRoot(container);
    root.render(<SettingsPage />);
  } else {
    console.error("Container #justin-legal-test-page not found");
  }
});

On the browser page, I don’t see any error. When I run npm run build:

asset index.js 587 bytes [emitted] [minimized] (name: index)
asset index.asset.php 133 bytes [emitted] (name: index)
Entrypoint index 720 bytes = index.js 587 bytes index.asset.php 133 bytes
orphan modules 126 bytes [orphan] 3 modules
runtime modules 663 bytes 3 modules
./src/index.js + 3 modules 674 bytes [not cacheable] [built] [code generated]
webpack 5.98.0 compiled successfully in 1398 ms

And when I run npm run start:

> [email protected] start
> wp-scripts start

asset index.js 5.26 KiB [emitted] (name: index) 1 related asset
asset index.asset.php 133 bytes [emitted] (name: index)
Entrypoint index 5.39 KiB (3.75 KiB) = index.js 5.26 KiB index.asset.php 133 bytes 1 auxiliary asset
runtime modules 937 bytes 4 modules
built modules 674 bytes [built]
  ./src/index.js 548 bytes [built] [code generated]
  external ["wp","domReady"] 42 bytes [built] [code generated]
  external ["wp","element"] 42 bytes [built] [code generated]
  external "ReactJSXRuntime" 42 bytes [built] [code generated]
webpack 5.98.0 compiled successfully in 343 ms
asset index.js 5.26 KiB [emitted] (name: index) 1 related asset
asset index.asset.php 133 bytes [emitted] (name: index)
Entrypoint index 5.39 KiB (3.75 KiB) = index.js 5.26 KiB index.asset.php 133 bytes 1 auxiliary asset
runtime modules 937 bytes 4 modules
cached modules 126 bytes [cached] 3 modules
./src/index.js 548 bytes [built] [code generated]

Insert data using php to html textarea in mustache template [closed]

I read multi-line data using php from a database which has been previously validated on insertion. To create an editable form I’m using a Mustache template containing html form data with data inserted from php. SFSG with standard form elements (text, number).

However I’m not having success with trying to insert the data into a textarea with:

<div>
  <label col-sm-2" for="description">Article description:</label>
  <br>
  <textarea  name="description" rows="8" cols="45" <?php echo {{adescription}}; ?></textarea>
</div> 

Where adescription is the data item holding the text read from the database.

What I would like to achieve is that text is editable in the textarea so I can have the user edit it and then I’d check it and put it back in the database again.