🐛 fix #289,#291,#290

This commit is contained in:
muwoo 2023-11-10 14:35:37 +08:00
parent dfb8446cfd
commit f671b83b6a
17 changed files with 277 additions and 88 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 845 B

View File

@ -33,6 +33,12 @@ import localConfig from './confOp';
const config: any = localConfig.getConfig(); const config: any = localConfig.getConfig();
// 暗夜模式
if (config.perf.common.darkMode) {
document.body.classList.add('dark');
window.rubick.theme = 'dark';
}
ConfigProvider.config({ ConfigProvider.config({
theme: config.perf.custom || {}, theme: config.perf.custom || {},
}); });

View File

@ -1,6 +1,6 @@
{ {
"name": "rubick", "name": "rubick",
"version": "4.1.4", "version": "4.1.5",
"author": "muwoo <2424880409@qq.com>", "author": "muwoo <2424880409@qq.com>",
"private": true, "private": true,
"scripts": { "scripts": {
@ -30,7 +30,7 @@
"fix-path": "^3.0.0", "fix-path": "^3.0.0",
"get-mac-apps": "^1.0.2", "get-mac-apps": "^1.0.2",
"got": "^11.8.3", "got": "^11.8.3",
"lodash.throttle": "^4.1.1", "lodash.debounce": "^4.0.8",
"memorystream": "^0.3.1", "memorystream": "^0.3.1",
"node-key-sender": "^1.0.11", "node-key-sender": "^1.0.11",
"npm": "6.14.7", "npm": "6.14.7",

BIN
public/icons/delete@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 845 B

BIN
public/icons/pin@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 830 B

BIN
public/icons/unpin@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 747 B

View File

@ -49,6 +49,10 @@ window.rubick = {
showOpenDialog(options) { showOpenDialog(options) {
return ipcSendSync('showOpenDialog', options); return ipcSendSync('showOpenDialog', options);
}, },
showSaveDialog(options) {
return ipcSendSync('showSaveDialog', options);
},
setExpendHeight(height) { setExpendHeight(height) {
ipcSendSync('setExpendHeight', height); ipcSendSync('setExpendHeight', height);
}, },

View File

@ -4,5 +4,6 @@ import path from 'path';
const appPath = app.getPath('userData'); const appPath = app.getPath('userData');
const PLUGIN_INSTALL_DIR = path.join(appPath, './rubick-plugins-new'); const PLUGIN_INSTALL_DIR = path.join(appPath, './rubick-plugins-new');
const PLUGIN_HISTORY = 'rubick-local-start-app';
export { PLUGIN_INSTALL_DIR }; export { PLUGIN_INSTALL_DIR, PLUGIN_HISTORY };

View File

@ -9,7 +9,8 @@ const useDrag = () => {
let draggable = true; let draggable = true;
const onMouseDown = (e) => { const onMouseDown = (e) => {
// if (commonConst.macOS()) return; // 右击不移动
if (e.button === 2) return;
draggable = true; draggable = true;
mouseX = e.clientX; mouseX = e.clientX;
mouseY = e.clientY; mouseY = e.clientY;

View File

@ -1,6 +1,6 @@
const WINDOW_MAX_HEIGHT = 600; const WINDOW_MAX_HEIGHT = 620;
const WINDOW_MIN_HEIGHT = 60; const WINDOW_MIN_HEIGHT = 60;
const PRE_ITEM_HEIGHT = 60; const PRE_ITEM_HEIGHT = 70;
const HISTORY_HEIGHT = 70; const HISTORY_HEIGHT = 70;
export default (searchList: Array<any>, historyList): number => { export default (searchList: Array<any>, historyList): number => {

View File

@ -139,7 +139,6 @@ class API extends DBInstance {
public hideMainWindow(arg, window) { public hideMainWindow(arg, window) {
window.hide(); window.hide();
} }
public showMainWindow(arg, window) { public showMainWindow(arg, window) {
window.show(); window.show();
} }
@ -148,6 +147,10 @@ class API extends DBInstance {
return dialog.showOpenDialogSync(window, data); return dialog.showOpenDialogSync(window, data);
} }
public showSaveDialog({ data }, window) {
return dialog.showSaveDialogSync(window, data);
}
public setExpendHeight({ data: height }, window: BrowserWindow, e) { public setExpendHeight({ data: height }, window: BrowserWindow, e) {
const originWindow = this.getCurrentWindow(window, e); const originWindow = this.getCurrentWindow(window, e);
if (!originWindow) return; if (!originWindow) return;

View File

@ -15,10 +15,12 @@ const registerHotKey = (mainWindow: BrowserWindow): void => {
// 设置开机启动 // 设置开机启动
const setAutoLogin = async () => { const setAutoLogin = async () => {
const config = await localConfig.getConfig(); const config = await localConfig.getConfig();
if (app.getLoginItemSettings().openAtLogin !== config.perf.common.start) {
app.setLoginItemSettings({ app.setLoginItemSettings({
openAtLogin: config.perf.common.start, openAtLogin: config.perf.common.start,
openAsHidden: true, openAsHidden: true,
}); });
}
}; };
const setTheme = async () => { const setTheme = async () => {
@ -86,10 +88,6 @@ const registerHotKey = (mainWindow: BrowserWindow): void => {
}); });
}); });
// globalShortcut.register(config.perf.shortCut.separate, () => {
//
// });
globalShortcut.register(config.perf.shortCut.quit, () => { globalShortcut.register(config.perf.shortCut.quit, () => {
// mainWindow.webContents.send('init-rubick'); // mainWindow.webContents.send('init-rubick');
// mainWindow.show(); // mainWindow.show();

View File

@ -27,18 +27,22 @@
:currentSelect="currentSelect" :currentSelect="currentSelect"
:options="options" :options="options"
:clipboardFile="clipboardFile || []" :clipboardFile="clipboardFile || []"
@setPluginHistory="setPluginHistory"
@choosePlugin="choosePlugin"
/> />
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { watch, ref, nextTick, toRaw } from 'vue'; import { watch, ref, toRaw } from 'vue';
import { ipcRenderer } from 'electron';
import Result from './components/result.vue'; import Result from './components/result.vue';
import Search from './components/search.vue'; import Search from './components/search.vue';
import getWindowHeight from '../common/utils/getWindowHeight'; import getWindowHeight from '../common/utils/getWindowHeight';
import createPluginManager from './plugins-manager'; import createPluginManager from './plugins-manager';
import useDrag from '../common/utils/dragWindow'; import useDrag from '../common/utils/dragWindow';
import { getGlobal } from '@electron/remote';
import { PLUGIN_HISTORY } from '@/common/constans/renderer';
import { message } from 'ant-design-vue';
const { onMouseDown } = useDrag(); const { onMouseDown } = useDrag();
const remote = window.require('@electron/remote'); const remote = window.require('@electron/remote');
@ -60,6 +64,8 @@ const {
clearClipboardFile, clearClipboardFile,
readClipboardContent, readClipboardContent,
pluginHistory, pluginHistory,
setPluginHistory,
changePluginHistory,
} = createPluginManager(); } = createPluginManager();
initPlugins(); initPlugins();
@ -76,19 +82,22 @@ getPluginInfo({
remote.getGlobal('LOCAL_PLUGINS').addPlugin(res); remote.getGlobal('LOCAL_PLUGINS').addPlugin(res);
}); });
watch([options, pluginHistory, currentPlugin], () => { watch(
[options, pluginHistory, currentPlugin],
() => {
currentSelect.value = 0; currentSelect.value = 0;
if (currentPlugin.value.name) return; if (currentPlugin.value.name) return;
nextTick(() => { window.rubick.setExpendHeight(
ipcRenderer.sendSync('msg-trigger', { getWindowHeight(
type: 'setExpendHeight',
data: getWindowHeight(
options.value, options.value,
(pluginLoading.value) ? [] : pluginHistory.value pluginLoading.value ? [] : pluginHistory.value
), )
}); );
}); },
}); {
immediate: true,
}
);
const changeIndex = (index) => { const changeIndex = (index) => {
if (!options.value.length) { if (!options.value.length) {
@ -125,13 +134,40 @@ const openMenu = (ext) => {
window.rubick.openMenu = openMenu; window.rubick.openMenu = openMenu;
const choosePlugin = () => { const choosePlugin = (plugin) => {
if (options.value.length) { if (options.value.length) {
const currentChoose = options.value[currentSelect.value]; const currentChoose = options.value[currentSelect.value];
currentChoose.click(); currentChoose.click();
} else { } else {
const currentChoose = pluginHistory.value[currentSelect.value]; const localPlugins = getGlobal('LOCAL_PLUGINS').getLocalPlugins();
currentChoose.click(); const currentChoose = plugin || pluginHistory.value[currentSelect.value];
let hasRemove = true;
localPlugins.find((plugin) => {
if (plugin.name === currentChoose.originName) {
hasRemove = false;
return true;
}
return false;
});
if (hasRemove) {
const result = window.rubick.db.get(PLUGIN_HISTORY) || {};
const history = result.data.filter(item => item.originName !== currentChoose.originName);
setPluginHistory(history);
return message.warning('插件已被卸载!');
}
changePluginHistory(currentChoose);
window.rubick.openPlugin(
JSON.parse(
JSON.stringify({
...currentChoose,
ext: {
code: currentChoose.feature.code,
type: currentChoose.cmd.type || 'text',
payload: null,
},
})
)
);
} }
}; };

View File

@ -1,20 +1,25 @@
<template> <template>
<div v-show="!currentPlugin.name" class="options">
<div <div
v-show="!currentPlugin.name" class="history-plugins"
class="options" v-if="!options.length || !(searchValue || !!clipboardFile.length)"
ref="scrollDom"
> >
<div class="history-plugins" v-if="!options.length || !(searchValue || !!clipboardFile.length)">
<a-row> <a-row>
<a-col <a-col
@click="() => item.click()" @click="() => openPlugin(item)"
:class="currentSelect === index ? 'active history-item' : 'history-item'" @contextmenu.prevent="openMenu($event,item)"
:class="
currentSelect === index ? 'active history-item' : 'history-item'
"
:span="3" :span="3"
v-for="(item, index) in pluginHistory" v-for="(item, index) in pluginHistory"
:key="index" :key="index"
> >
<a-avatar style="width: 28px; height: 28px;" :src="item.icon" /> <a-avatar style="width: 28px; height: 28px" :src="item.icon" />
<div class="name ellpise">{{item.cmd || item.pluginName || item._name || item.name}}</div> <div class="name ellpise">
{{ item.cmd || item.pluginName || item._name || item.name }}
</div>
<div class="badge" v-if="item.pin"></div>
</a-col> </a-col>
</a-row> </a-row>
</div> </div>
@ -39,16 +44,13 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import BScroll from '@better-scroll/core'; import {defineEmits, defineProps, reactive, toRaw, watch} from 'vue';
import { defineProps, onMounted, ref } from 'vue'; const path = window.require('path');
const remote = window.require('@electron/remote');
const scrollDom = ref(null); declare const __static: string;
onMounted(() => { const props: any = defineProps({
new BScroll(scrollDom.value);
});
const props = defineProps({
searchValue: { searchValue: {
type: [String, Number], type: [String, Number],
default: '', default: '',
@ -66,14 +68,22 @@ const props = defineProps({
clipboardFile: (() => [])(), clipboardFile: (() => [])(),
}); });
const emit = defineEmits(['choosePlugin', 'setPluginHistory']);
const renderTitle = (title, match) => { const renderTitle = (title, match) => {
if (typeof title !== 'string') return; if (typeof title !== 'string') return;
if (!props.searchValue || !match) return title; if (!props.searchValue || !match) return title;
const result = title.substring(match[0], match[1] + 1); const result = title.substring(match[0], match[1] + 1);
return `<div>${title.substring(0, match[0])}<span style='color: var(--ant-error-color)'>${result}</span>${title.substring(match[1]+1, title.length)}</div>`; return `<div>${title.substring(
0,
match[0]
)}<span style='color: var(--ant-error-color)'>${result}</span>${title.substring(
match[1] + 1,
title.length
)}</div>`;
}; };
const renderDesc = (desc) => { const renderDesc = (desc = '') => {
if (desc.length > 80) { if (desc.length > 80) {
return `${desc.substr(0, 63)}...${desc.substr( return `${desc.substr(0, 63)}...${desc.substr(
desc.length - 14, desc.length - 14,
@ -93,17 +103,97 @@ const sort = (options) => {
} }
} }
} }
return options; return options.slice(0, 20);
}; };
const openPlugin = (item) => {
emit('choosePlugin', item);
};
const menuState: any = reactive({
plugin: null,
});
let mainMenus;
const openMenu = (e, item) => {
const pinToMain = mainMenus.getMenuItemById('pinToMain');
const unpinFromMain = mainMenus.getMenuItemById('unpinFromMain');
pinToMain.visible = !item.pin;
unpinFromMain.visible = item.pin;
mainMenus.popup({
x: e.pageX,
y: e.pageY,
});
menuState.plugin = item;
};
const initMainCmdMenus = () => {
const menu = [
{
id: 'removeRecentCmd',
label: '从"使用记录"中删除',
icon: path.join(__static, 'icons', 'delete@2x.png'),
click: () => {
const history = props.pluginHistory.filter((item) => item.name !== menuState.plugin.name);
emit('setPluginHistory', toRaw(history));
},
},
{
id: 'pinToMain',
label: '固定到"搜索面板"',
icon: path.join(__static, 'icons', 'pin@2x.png'),
click: () => {
const history = props.pluginHistory.map((item) => {
if (item.name === menuState.plugin.name) {
item.pin = true;
}
return item;
});
emit('setPluginHistory', toRaw(history));
},
},
{
id: 'unpinFromMain',
label: '从"搜索面板"取消固定',
icon: path.join(__static, 'icons', 'unpin@2x.png'),
click: () => {
const history = props.pluginHistory.map((item) => {
if (item.name === menuState.plugin.name) {
item.pin = false;
}
return item;
});
emit('setPluginHistory', toRaw(history));
},
},
];
mainMenus = remote.Menu.buildFromTemplate(menu);
};
initMainCmdMenus();
</script> </script>
<style lang="less"> <style lang="less">
.ellpise { .ellpise {
overflow:hidden; overflow: hidden;
text-overflow:ellipsis; text-overflow: ellipsis;
display:-webkit-box; display: -webkit-box;
-webkit-line-clamp:1; -webkit-line-clamp: 1;
-webkit-box-orient:vertical; -webkit-box-orient: vertical;
}
.contextmenu {
margin: 0;
background: #fff;
z-index: 3000;
position: absolute;
list-style-type: none;
padding: 5px 0;
border-radius: 4px;
font-size: 12px;
font-weight: 400;
color: #333;
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
} }
.options { .options {
@ -120,6 +210,7 @@ const sort = (options) => {
border-top: 1px dashed var(--color-border-light); border-top: 1px dashed var(--color-border-light);
box-sizing: border-box; box-sizing: border-box;
.history-item { .history-item {
cursor: pointer;
box-sizing: border-box; box-sizing: border-box;
height: 69px; height: 69px;
display: flex; display: flex;
@ -128,6 +219,19 @@ const sort = (options) => {
flex-direction: column; flex-direction: column;
color: var(--color-text-content); color: var(--color-text-content);
border-right: 1px dashed var(--color-border-light); border-right: 1px dashed var(--color-border-light);
position: relative;
.badge {
position: absolute;
top: 2px;
right: 2px;
width: 0;
height: 0;
border-radius: 4px;
border-top: 6px solid var(--ant-primary-4);
border-right: 6px solid var(--ant-primary-4);
border-left: 6px solid transparent;
border-bottom: 6px solid transparent;
}
&.active { &.active {
background: var(--color-list-hover); background: var(--color-list-hover);
} }
@ -141,7 +245,7 @@ const sort = (options) => {
} }
.op-item { .op-item {
padding: 0 10px; padding: 0 10px;
height: 60px; height: 70px;
line-height: 50px; line-height: 50px;
max-height: 500px; max-height: 500px;
overflow: auto; overflow: auto;

View File

@ -8,7 +8,10 @@ import commonConst from '@/common/utils/commonConst';
import { exec } from 'child_process'; import { exec } from 'child_process';
import searchManager from './search'; import searchManager from './search';
import optionsManager from './options'; import optionsManager from './options';
import { PLUGIN_INSTALL_DIR as baseDir } from '@/common/constans/renderer'; import {
PLUGIN_INSTALL_DIR as baseDir,
PLUGIN_HISTORY,
} from '@/common/constans/renderer';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
const createPluginManager = (): any => { const createPluginManager = (): any => {
@ -28,16 +31,22 @@ const createPluginManager = (): any => {
const appList: any = ref([]); const appList: any = ref([]);
const initPlugins = async () => { const initPlugins = async () => {
initPluginHistory();
appList.value = await appSearch(nativeImage); appList.value = await appSearch(nativeImage);
initLocalStartPlugin(); initLocalStartPlugin();
}; };
const initPluginHistory = () => {
const result = window.rubick.db.get(PLUGIN_HISTORY) || {};
if (result && result.data) {
state.pluginHistory = result.data;
}
};
const initLocalStartPlugin = () => { const initLocalStartPlugin = () => {
const result = ipcRenderer.sendSync('msg-trigger', { const result = ipcRenderer.sendSync('msg-trigger', {
type: 'dbGet', type: 'dbGet',
data: { data: { id: PLUGIN_HISTORY },
id: 'rubick-local-start-app',
},
}); });
if (result && result.value) { if (result && result.value) {
appList.value.push(...result.value); appList.value.push(...result.value);
@ -70,15 +79,12 @@ const createPluginManager = (): any => {
const openPlugin = async (plugin, option) => { const openPlugin = async (plugin, option) => {
if (plugin.pluginType === 'ui' || plugin.pluginType === 'system') { if (plugin.pluginType === 'ui' || plugin.pluginType === 'system') {
if (state.currentPlugin && state.currentPlugin.name === plugin.name) { if (state.currentPlugin && state.currentPlugin.name === plugin.name) {
ipcRenderer.sendSync('msg-trigger', { window.rubick.showMainWindow();
type: 'showMainWindow',
});
return; return;
} }
await loadPlugin(plugin); await loadPlugin(plugin);
ipcRenderer.sendSync('msg-trigger', { window.rubick.openPlugin(
type: 'openPlugin', JSON.parse(
data: JSON.parse(
JSON.stringify({ JSON.stringify({
...plugin, ...plugin,
ext: plugin.ext || { ext: plugin.ext || {
@ -87,8 +93,8 @@ const createPluginManager = (): any => {
payload: null, payload: null,
}, },
}) })
), )
}); );
} }
if (plugin.pluginType === 'app') { if (plugin.pluginType === 'app') {
try { try {
@ -101,19 +107,52 @@ const createPluginManager = (): any => {
changePluginHistory({ changePluginHistory({
...plugin, ...plugin,
...option, ...option,
originName: plugin.name,
}); });
}; };
const changePluginHistory = (plugin) => { const changePluginHistory = (plugin) => {
state.pluginHistory.forEach((p, index) => { const unpin = state.pluginHistory.filter((plugin) => !plugin.pin);
const pin = state.pluginHistory.filter((plugin) => plugin.pin);
const isPin = state.pluginHistory.find((p) => p.name === plugin.name)?.pin;
if (isPin) {
pin.forEach((p, index) => {
if (p.name === plugin.name) { if (p.name === plugin.name) {
state.pluginHistory.splice(index, 1); pin.splice(index, 1);
} }
}); });
state.pluginHistory.unshift(plugin); pin.unshift(plugin);
if (state.pluginHistory.length > 8) { } else {
state.pluginHistory.pop(); unpin.forEach((p, index) => {
if (p.name === plugin.name) {
unpin.splice(index, 1);
} }
});
unpin.unshift(plugin);
}
if (state.pluginHistory.length > 8) {
unpin.pop();
}
state.pluginHistory = [...pin, ...unpin];
const result = window.rubick.db.get(PLUGIN_HISTORY) || {};
window.rubick.db.put({
_id: PLUGIN_HISTORY,
_rev: result._rev,
data: JSON.parse(JSON.stringify(state.pluginHistory)),
});
};
const setPluginHistory = (plugins) => {
state.pluginHistory = plugins;
const unpin = state.pluginHistory.filter((plugin) => !plugin.pin);
const pin = state.pluginHistory.filter((plugin) => plugin.pin);
state.pluginHistory = [...pin, ...unpin];
const result = window.rubick.db.get(PLUGIN_HISTORY) || {};
window.rubick.db.put({
_id: PLUGIN_HISTORY,
_rev: result._rev,
data: JSON.parse(JSON.stringify(state.pluginHistory)),
});
}; };
const { searchValue, onSearch, setSearchValue, placeholder } = const { searchValue, onSearch, setSearchValue, placeholder } =
@ -206,6 +245,8 @@ const createPluginManager = (): any => {
clipboardFile, clipboardFile,
clearClipboardFile, clearClipboardFile,
readClipboardContent, readClipboardContent,
setPluginHistory,
changePluginHistory,
}; };
}; };

View File

@ -1,5 +1,5 @@
import { ref, watch } from 'vue'; import { ref, watch } from 'vue';
import throttle from 'lodash.throttle'; import debounce from 'lodash.debounce';
import { ipcRenderer } from 'electron'; import { ipcRenderer } from 'electron';
import { getGlobal } from '@electron/remote'; import { getGlobal } from '@electron/remote';
import PinyinMatch from 'pinyin-match'; import PinyinMatch from 'pinyin-match';
@ -130,7 +130,7 @@ const optionsManager = ({
watch(searchValue, () => search(searchValue.value)); watch(searchValue, () => search(searchValue.value));
// search Input operation // search Input operation
const search = throttle((value) => { const search = debounce((value) => {
if (currentPlugin.value.name) return; if (currentPlugin.value.name) return;
if (clipboardFile.value.length) return; if (clipboardFile.value.length) return;
if (!value) { if (!value) {
@ -138,7 +138,7 @@ const optionsManager = ({
return; return;
} }
optionsRef.value = getOptionsFromSearchValue(value); optionsRef.value = getOptionsFromSearchValue(value);
}, 500); }, 100);
const setOptionsRef = (options) => { const setOptionsRef = (options) => {
optionsRef.value = options; optionsRef.value = options;

View File

@ -8867,7 +8867,7 @@ lodash.clonedeep@^4.5.0, lodash.clonedeep@~4.5.0:
lodash.debounce@^4.0.8: lodash.debounce@^4.0.8:
version "4.0.8" version "4.0.8"
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" resolved "https://registry.npmmirror.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
lodash.defaultsdeep@^4.6.1: lodash.defaultsdeep@^4.6.1:
@ -8910,11 +8910,6 @@ lodash.pick@^4.0.0:
resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3"
integrity sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q== integrity sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==
lodash.throttle@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4"
integrity sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==
lodash.transform@^4.6.0: lodash.transform@^4.6.0:
version "4.6.0" version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.transform/-/lodash.transform-4.6.0.tgz#12306422f63324aed8483d3f38332b5f670547a0" resolved "https://registry.yarnpkg.com/lodash.transform/-/lodash.transform-4.6.0.tgz#12306422f63324aed8483d3f38332b5f670547a0"